1. 리스트(List)란? [ ]
리스트는 각 오브젝트를 "그룹화" 한다고 생각하시면 됩니다. 아래를 보시면 리스트는 내부 오브젝트에 인덱스를 부여하기 때문에 인덱스를 통해 출력할 수 있습니다.
company = ['kakao', 'naver', 'aws', 'google', 'apple']
company[0]
company[-1]
리스트 내부 오브젝트는 오버라이딩이 가능합니다. 즉 다른 값으로 대체할 수 있다는 뜻입니다.
company[0] = 'facebook'
print(company[0])
리스트는 어떤 타입의 데이터든 그룹화할 수 있습니다. 심지어 그룹 내 데이터 형태가 각기 달라도 됩니다. 이는 편해보일 수도 있지만 코드를 작성한 사람이 잘 기억하고 있어야겠죠. 몇 번째가 문자열인지, 몇 번째가 숫자형인지 말이죠.
company_detail = ['apple', 12, 'iphone'] # apple, iphone -> string / 12 -> integer
company_detail
아래는 리스트 내부에 소수점(float) 타입의 데이터가 들어간다는 것을 명시적으로 표기한 것입니다.
from typing import List
def average(L: List[float]) -> float:
...
아래는 리스트에 사용할 수 있는 기본적인 함수 및 연산 방법입니다.
A = [1, 2, 3, 4, 5, 6]
print(len(A))
print(max(A))
print(min(A))
print(sum(A))
print(sorted(A))
print(A + [2, 3, 5])
print(A * 2)
del A[1] # 인덱스 1번에 해당하는 값 삭제
print(A)
"apple" in company
슬라이싱(slicing)을 하면 리스트를 출력합니다. 그리고 마지막 숫자에 해당하는 인덱스는 포함되지 않는다는 것에 주의해주세요!
print(company)
print(company[1: 3])
print(company[1:-1]) # company[-1] = 'apple'
print(company[:3])
print(company[:])
복사(Copy)와 알리아스(Alias)의 차이점은?
복사와 알리아스의 개념은 조금 다릅니다. 복사는 말 그대로 나의 복제본이 생긴 느낌이라 복제본이 바뀌어도, 아예 사라진다 해도 "나"는 그대로 남아 있습니다. 하지만 알리아스는 나의 분신으로 분신에 변화가 생긴다면 나도 영향을 받습니다. 분신의 첫 번째 값이 'kakao'에서 'MS'로 바뀌면 나의 첫 번째 값도 'MS'로 바뀌게 됩니다.
리스트를 조작할 때 주의할 필요가 있겠죠? 특히 함수를 정의할 때 잘 생각하셔서 복제본을 넘길지, 분신을 넘길지 고민해야 합니다.
# Copy
company_version1 = company[:]
print(company_version1)
company_version1[0] = 'MS'
print(company) # 원래 company 리스트에는 영향이 없음
print(company_version1)
# Alias
company_version2 = company
company_version2[0] = 'MS'
print(company_version2)
print(company) # 원래 company 리스트도 바뀌게 됨
company_copy = company[:] # copy를 쓰자!
company_copy.append('kakao')
print(company_copy)
company_copy.clear() # 요소(element) 전부 삭제
print(company_copy)
company_copy = company[:] # 다시 살리기
print(company_copy.index('aws'))
company_copy.insert(3, 'openai') # index=3 위치에 삽입
print(company_copy)
company_copy.pop() # 마지막 요소 제거
print(company_copy)
company_copy.remove('aws')
print(company_copy)
company_copy.reverse() # 거꾸로 정렬
print(company_copy)
company_copy.sort() # 알파벳 순서대로 정렬
print(company_copy)
company_copy.sort(reverse=True) # 알파벳 순서대로 역정렬
print(company_copy)
리스트의 리스트
company_list = [['IT', 'facebook', 'naver', 'kakao'], ['Media', 'bloomberg', 'newyorktimes'],
['Finance', 'jpmorganchase', 'boa']]
print(company_list[0])
print(company_list[1][0])
print(company_list[2][1])
2. Loop이란?
특정 프로세스를 반복적으로 진행하는 과정으로 for loop가 대표적입니다. "Do you like <company_name> ?"를 반복적으로 출력하고 싶은데, 아래처럼 단 2줄의 코드로 완성할 수 있습니다. 리스트에 요소가 많아질수록 훨씬 작업이 편해짐을 느낄 수 있을 것입니다.
for company_name in company:
print("Do you like", company_name, "?")
for loop은 리스트, 문자열, 숫자형, 딕셔너리 모두 가능합니다. 또 흥미로운 것은 range() 인데요, ()안에는 숫자가 들어가 설정된 범위에 따라 반복적으로 살펴보겠다는 것입니다.
for num in range(10): # range(10)은 인덱스=0부터 시작
print("The number is:", num)
# for num in range(1:10) <- 인덱스=0부터 시작하기 싫을 때
리스트에서 복사와 알리아스를 살펴봤는데, 마찬가지로 루프에서는 인덱싱(indexing)를 주의해야 합니다.
values = [0, 1, 2, 3, 4]
for num in values:
num = num * 3
print(num)
print(values)
# 인덱스를 사용하면 원본이 바뀜
for i in values:
values[i] = values[i] * 2
print(values)
이번엔 while loop을 알아보겠습니다. while 문은 조건이 만족하는 한 계속 반복을 진행하기 때문에, 끝날 수 있는 조건을 반드시 선언해야 합니다. 조건이 없다면 무한 루프(infitie loop)에 빠지게 되는 문제가 발생합니다. 물론, Ctrl + C를 눌러 강제 종료할 수 있습니다.
count = 0
while count < 5:
print('okay')
count += 1
break와 continue에 대해서도 알아봅시다. break는 내가 원하는 값을 얻었을 때 반복 프로그램을 멈출 수 있도록 해주는 기능을 합니다.
아래에서 우리는 문자열에서 처음으로 등장하는 대문자의 위치를 알고 싶습니다. 즉 H의 위치를 알고 싶지, 뒤에 등장하는 B의 위치를 알고 싶지 않습니다. 따라서 첫 대문자 위치를 알게 되면 break 선언을 통해 반복 기능을 제거하는 것이죠.
first_upper_index = -5
random_string = 'How are you doing, Babe?'
for char in range(len(random_string)):
if random_string[char].isupper():
first_upper_index = char
break
first_upper_index # B -> index=19
아래는 continue를 사용한 것입니다. continue는 break와 반대로 어떤 조건에 해당하면 계속 뒤에 남아있는 코드를 진행하라는 것입니다. 여기선 소문자라면 넘어가고 first_uppder_index를 업데이트하는 코드를 진행하는 것이죠. 만약 소문자가 아니라면 first_update_index를 업데이트하는 동시에 break가 되어 코드가 종료됩니다.
그런데 우리는 B의 index=19를 기대했는데, 아래는 3을 출력했네요. 그 이유는 무엇일까요? 답은 댓글에 달아두겠습니다!
first_upper_index = -5
random_string = 'how are you doing, Babe?'
for char in range(len(random_string)):
if random_string[char].islower():
continue
first_upper_index = char
break
first_upper_index
3. Set이란? { }
리스트와 달리 순서가 없고 별개의 원소로 구성되어 있습니다. 하지만 리스트와 마찬가지로 수정 가능하고(mutable) 함수의 인자로 사용될 수 있습니다. 또 중요한 점은 int, str, bool, float처럼 클래스입니다!
순서가 없다는 의미는 중복된 값을 허용하지 않는다는 것입니다. 아래 예시를 살펴보면 이해가 가능합니다.
alphabet = {"a", "b", "c", "a", "b", "d"}
set(alphabet)
아래와 같이 a를 set()으로 선언하는 경우 클래스 자체가 반환됩니다.
# a = set()
# a
>>> set()
# 리스트를 set으로 변환 가능
company = set(['apple', 'apple', 'samsung', 'google', 'huawei'])
for i in company:
print(i)
Set의 메서드를 살펴봅시다.
digits = set([0, 1, 2, 3, 4, 5])
odds = set([1, 3, 5, 7, 9])
digits.add(6) # 어디에 추가한지는 중요하지 않음, set은 순서가 상관없음
print(digits)
digits.remove(2)
print(digits)
digits.clear()
print(digits)
digits = set([0, 1, 2, 3, 4, 5]) # 없어진 set 다시 살리기
print(digits.issubset(odds)) # 부분집합인지 표시
print(digits.issuperset(odds)) # 부모집합인지 표시
print(digits.difference(odds)) # digits를 기준으로 한 차집합
print(digits.intersection(odds)) # 교집합
print(digits.symmetric_difference(odds)) # 합집합 - 교집합
print(digits.union(odds)) # 합집합
set 자체는 추가, 삭제 등 바꿀 수 있으나 요소는 값을 바꿀 수 없습니다. 반면, 리스트는 요소 역시 수정 가능한 mutable object이기 때문에 아래처럼 코드를 작성하면 오류가 발생합니다. 요소 수정이 가능한 리스트를 set 내부에 추가하지 못하겠다는 의미죠. 이는 set이 요소를 찾을 때 hashing 기법을 사용하는 것과 관련되어 있고, 따라서 리스트보다 요소를 찾는 속도가 훨씬 빠릅니다. hashing 기법에 대해선 다음에 설명하도록 하겠습니다.
S = set()
L = [1, 2, 3] # mutable object
S.add(L)
4. 튜플(Tuple)이란? ( )
튜플은 리스트와 마찬가지로 순서를 가지고 있습니다. 하지만 set과 똑같이 요소를 수정할 수는 없습니다.
nums = ()
print(type(nums))
nums = (8, ) # 원소가 1개일 때, 튜플을 위해선 쉼표 필수
nums = (5+3, )
print(nums)
nums = (1, 2, 3, 4)
for num in nums:
print(num)
튜플은 원소를 수정할 수 없지만, 순서가 존재하기 때문에 인덱스로 값을 불러올 수 있습니다. 원소 추가는 튜플 간 연산으로 가능합니다.
company = ('LG', 'HD', 'Samsung', 'SK')
company[0] = 'KT'
company = ('LG', 'HD', 'Samsung', 'SK')
company += ('KT', ) # 쉼표 주의
company[-1]
재밌는 점은 튜플 원소 내의 원소가 mutable 가능할 경우, 해당 원소를 바꿀 수 있습니다. 아래 예시를 들어 설명하면, 튜플 company 내 리스트 ['LG', 'korea'] 내 원소 'korea'는 값을 변경할 수 있다는 것입니다. k를 대문자로 다시 선언했고, 그 결과 대문자로 바뀌어 있습니다.
company = (['LG', 'korea'], ['HD', 'Korea'], ['Samsung', 'Korea'], ['apple', 'USA'])
company[0][1] = 'Korea'
company
5. 딕셔너리(Dictionary)란? { }
딕셔너리는 set과 비슷하게 순서가 없는, list와 비슷하게 원소 수정이 가능합니다. 특이한 점은 원소가 키(key)와 밸류(value)로 구성되어 있다는 것인데요. 키는 리스트의 인덱스와 비슷한 역할을 해서 키로 밸류값을 찾기도 합니다.
dict_demo = {} # 선언하기
dict_company = {'SK': 'KOREA', 'GOOGLE': 'USA'}
dict_company['SK']
딕셔너리 키는 수정 불가하며 리스트의 인덱스처럼 별도로(distinct) 존재하기 때문에 set과 마찬가지로 hashing을 사용하기 때문에 찾는 속도가 빠릅니다.
dict_company = {'SK': 'koreaaa', 'GOOGLE': 'USA'}
dict_company['SK'] = 'KOREA'
print(dict_company)
print('GOOGLE' in dict_company)
del dict_company['GOOGLE'] # key-value 쌍이 동시에 지워짐
dict_company
dict_company = {'SK': 'KOREA', 'GOOGLE': 'USA'}
for company in dict_company: # company는 key를 가리킴
print(company, "was founded in ", dict_company[company])
다음은 딕셔너리의 여러 메서드들입니다.
dict_company.clear()
dict_company.keys() # 모든 키 반환
dict_company.items() # key-value 쌍 반환
dict_company.values() # 모든 밸류 반환
dict_company.get('SK') # 키에 따른 밸류 반환
dict_company.get('SK', default) # SK가 딕셔너리에 있다면 해당 밸류 반환 or 없다면 우리가 임의로 정한 default 값 반환
dict_company.pop('SK') # 키를 입력하면 해당 key-value 쌍 삭제, 키에 해당하는 밸류값 출력
dict_company.pop('SK', default)
# 결과는 아래 예시 코드 참고
dict_company.setdefault('SK') # 키에 따른 밸류 반환, 키가 없다면 밸류로 None이 추가
dict_company.setdefault('SK', default)
dict_company.update(dict2)
dict_company.setdefault('LG')
print(dict_company)
dict_company = {'SK': 'KOREA', 'GOOGLE': 'USA'}
dict_company.setdefault('LG', 'KOREA') # 디폴트 밸류 값 = KOREA
print(dict_company)
dict_company2 = {'Huawei': 'CHINA', 'SONY': 'JAPAN'}
dict_company.update(dict_company2) # 새로운 딕셔너리 추가
dict_company2
딕셔너리의 in 연산은 키에만 해당합니다. 즉 키가 딕셔너리에 속해 있는지 알려주는 것이지, 밸류가 딕셔너리에 속한지는 알지 못합니다.
dict_company = {'SK': 'KOREA', 'GOOGLE': 'USA'}
print('KOREA' in dict_company)
print('SK' in dict_company)
수정 가능함(Mutability)이란?
1. List(mutable container with mutable elements)
리스트는 컨테이너도 mutable, 원소도 mutable합니다. 아래 바깥 리스트를 부모 리스트(혹은 컨테이너=container)로, 내부 리스트를 자식 리스트(혹은 원소=element)라 칭하겠습니다.
list_parent = [['SK', 'KOREA'], ['SAMSUNG', 'KOREA'], ['GOOGLE', 'USA']]
list_parent
리스트는 부모 리스트와 자식 리스트 모두 수정 가능한 경우입니다. 보통 부모 리스트가 수정 가능하다는 것은 1) 자식 리스트가 추가되거나 제거될 수 있고, 2) 자식 리스트가 가리키는 오브젝트(메모리 공간)가 달라질 수 있다는 것을 의미합니다. 예시를 살펴보겠습니다.
list_parent.append(['NAVER', 'KOREA'])
print(list_parent) # 추가
list_parent[0] = ['KAKAO', 'KOREA']
print(list_parent) # list_append[0]이 가리키는 오브젝트가 ['SK', 'KOREA']에서 ['KAKAO', 'KOREA']로 바뀜
2. Tuple(immutable container with mutable elements)
튜플은 리스트와 다릅니다. 자식 리스트를 추가는 할 수 있지만, 일반적으로 자식 리스트 수정 및 삭제가 불가합니다. 튜플 내 자식 리스트가 가리키는 오브젝트는 변하지 않습니다.
list_parent = (['SK', 'KOREA'], ['SAMSUNG', 'KOREA'], ['GOOGLE', 'USA'])
list_parent += (['NAVER', 'KOREA'],)
print(list_parent)
list_parent[0] = ['KAKAO', 'KOREA'] # 자식 리스트 수정 불가(메모리 공간 상 오브젝트 불변)
print(list_parent)
하지만 위에서도 언급했듯이, 1) 자식 리스트를 다시 부모 리스트로 보고, 2) 각 문자열을 자식 원소로 본다면 리스트는 원소 수정이 가능하기 때문에 값을 변경할 수 있습니다. 예시를 통해 쉽게 이해합시다.
# list_parent[0] -> ['SK', 'KOREA']
list_parent[0][0] = 'KT' # 원래 값은 'SK'
list_parent
3. Set(mutable container with immutable elements)
set은 추가, 삭제는 자유롭게 가능하기 때문에 자식 원소가 가리키는 오브젝트가 바뀔 수 있습니다. 하지만 자식 원소를 1) 부모로 생각했을 때, 2) 그 자식 원소는 수정 불가합니다. 아래 예시를 봅시다.
list_parent = set(['SK', 'KT', 'KAKAO', 'NAVER'])
list_parent.remove('SK') # 'SK'가 가리키는 오브젝트는 메모리 공간 상에서 사라짐
list_parent
아래 ['MS', 'APPLE']은 set 입장에서 자식 원소에 해당합니다. 하지만 ['MS', 'APPLE'] 자체를 부모 원소로 생각한다면, 자식 원소인 'MS', 'APPLE'은 수정 가능합니다. 하지만 set은 이 경우를 지원하지 않는다는 것이죠. 쉽게 말해, 자식은 바뀌어도 자식의 자식이 바뀌는 경우는 있을 수 없다는 것입니다.
list_parent.add(['MS', 'APPLE'])
이어서 다음에 설명하겠습니다. 비전공자 문돌이가 설명하는 파이썬(Python) 기본 문법
'문돌이 존버 > 프로그래밍 스터디' 카테고리의 다른 글
Sorting 알고리즘 구현 예제 코드 모음집 (0) | 2021.02.01 |
---|---|
Merge sort, Quick sort비전공자 문돌이가 설명하는 파이썬(Python) 기본 문법 (5) (0) | 2021.01.31 |
Search, Sort 비전공자 문돌이가 설명하는 파이썬(Python) 기본 문법 (4) (0) | 2021.01.27 |
File I/O, 객체 지향 프로그래밍 비전공자 문돌이가 설명하는 파이썬(Python) 기본 문법 (3) (0) | 2021.01.26 |
비전공자 문돌이가 설명하는 파이썬(Python) 기본 문법 (1) (0) | 2021.01.22 |