
y = x = 3
print(x is y)
# True
print([3] is [3])
# False
2 개의 list를 생성하면 (설령 두 list에 같은 값을 저장하더라도) 이 두 list는 메모리상에서 서로 다른 객체다. 그래서 한 list 객체를 변경해도 다른 list 객체는 영향을 받지 않는다.
list는 가변(mutable) list 생성 이후에 변경 가능하기 때문이다.
하지만 정수값은 불변(immutable)이므로 한 변수를 변경한다고 해서 다른 모든 변수가 같이 변경되는 위험은 발생하지 않는다.
# 리스트: 가변
lst1 = [1, 2, 3]
lst2 = lst1
lst1[0] = 100 # lst1 값을 변경
print(lst1) # [100, 2, 3]
print(lst2) # [100, 2, 3] -> lst1과 lst2는 같은 객체
# 정수: 불변
a = 5
b = a
a = 10 # a에 새로운 값을 대입 (기존 5는 유지)
print(a) # 10
print(b) # 5 -> a가 바뀌어도 b는 그대로
- 메모리 관점:
리스트는 같은 메모리 공간을 공유하기 때문에 둘 중 하나가 바뀌면 나머지도 바뀝니다.
정수는 새로운 값을 할당할 때마다 새로운 메모리 공간을 사용하게 됩니다. 그래서 다른 변수에 영향을 주지 않아요.- 정리:
가변(mutable): 객체의 값을 바꿀 수 있고, 변수가 그 객체를 공유하면 함께 변경됩니다. (리스트, 딕셔너리 등)
불변(immutable): 값을 바꾸면 새로운 객체가 생성되고, 다른 변수에는 영향을 주지 않습니다. (정수, 문자열, 튜플 등)
in 키워드 이용print(42 in [2, 39, 42])
# True
print("21" in {"2", "39", "42"})
# True
print("list" in {"list": [1, 2, 3], "set": {1, 2, 3}})
# True
값 x가 컬렉션 y에 저장되어 있다면 x를 y의 멤버라고 함.
Set의 멤버십을 확인하는 것은 List의 멤버십을 확인하는 것보다 빠름. 값 x를 찾을 때까지 List에 저장된 모든 값을 훑어봐야 하기 때문임. 하지만 Set은 Dict와 비슷하게 구현되어 있어서, 파이썬은 값 x가 Set y에 저장되어 있는지 확인하기 위해 내부적으로 y[hash(x)]를 실행하고 리턴값이 None이 아닌지 확인함.
[표현식 + 컨텍스트] 로 표현가능# 원래 코드
squares = []
for i in range(10):
squares.append(i**2)
print(squares)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# 한 줄 코드
print([i**2 for i in range(10)])
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
| 타입 | 순서 있음 | 중복 허용 | Mutable | 인덱싱 가능 | 특징 |
|---|---|---|---|---|---|
| 리스트 | O | O | O | O | 순서가 있고, 값 변경 가능 (mutable) |
| 튜플 | O | O | X | O | 순서가 있지만, 값 변경 불가 (immutable) |
| 셋 (Set) | X | X | O | X | 순서 없음, 중복 불가, 값 변경 가능 |
| 딕셔너리 | O | 키: X, 값: O | O | 키로 접근 가능 | 키는 immutable, 값은 mutable, 키-값 쌍, 순서 보장 (Python 3.7 이후) |
| 문자열 | O | O | X | O | 문자 시퀀스, 값 변경 불가 (immutable) |
| 제너레이터 | X | - | X | X | 값을 하나씩 생성, 메모리 효율적 |
루프를 종료하는 기본적인 2가지 방법
1. 마지막에 False 값으로 평가되는 루프 조건 정의
2. 루프 본문 안에서 어떤 조건이 만족하면 break 키워드 사용
while True:
break # 무한 반복하지 않는다
print("hello world")
# hello world
이 예제의 경우는 루프를 미리 종료한 후에 print 구문을 실행함.
파이썬 인터프리터가 루프를 미리 중단하지 않고도 루프의 특정 부분을 건너뛰게 할 수도 있음.
루프의 현재 실행을 중단하고 다음 루프 조건을 실행하는 continue 키워드를 사용하면 됨.
while True:
continue
print("43") # 이 코드는 절대 실행되지 않는다
이 코드는 print 구문을 한 번도 실행하지 않고 무한히 반복됨.
이렇게 절대 실행되지 않는 코드를 dead code라고 함.
그런 이유로 continue 구문(break 구문 포함)은 if-else를 이용한 특정 조건 내에서만 실행하는 것이 일반적임.
lambda <매개변수> : <리턴 표현식>print((lambda x: x + 3)(3))
# 6
이 예제는 먼저 값 x를 받아 x + 3의 결과를 리턴하는 람다 함수를 정의함.
이 표현식의 결과는 다른 함수처럼 호출 가능한 함수 객체임.
함수의 동작으로 미루어 이 함수는 증분 함수(incrementor function)임.
이 증분 함수에 3이라는 매개변수를 전달해 호출하려면, print 함수 ㄴ에서 (3)과 같이 덧붙이면 됨. 그러면 결과로 정수값 6이 리턴됨.
[표현식 + 컨텍스트][ ]은 리스트를 의미함.컨텍스트는 리스트에서 어떤 값을 선택할지 정의함. 하나에서 여러개의 중첩 for 루프를 이용해 정의된 여러 변수로 구성됨. 게다가 if 문 이용해 컨텍스트에 제한 둘수도 있음. (조건에 맞는 값만 새로운 리스트에 추가 됨.)표현식 context에서 정의된 변수를 전달받은 함수이거나 어떤 계산이든 수행 가능함. 외부의 함수까지 호출 가능.# 1
print([x * 2 for x in range(3)])
# [0, 2, 4]
# 2
print([(x, y) for x in range(3)
for y in range(3)])
# [(0, 0), (0, 1), (0, 2),
# (1, 0), (1, 1), (1, 2),
# (2, 0), (2, 1), (2, 2)]
# 3
print([x ** 2 for x in range(10)
if x % 2 > 0])
# [1, 9, 25, 49, 81]
# 4
print(x.lower() for x in ['I', 'AM', 'NOT', 'SHOUTING'])
# ['i', 'am', 'not', 'shouting']
# 5
## 데이터
employees = {'Alice': 100000,
'Bob': 99000,
'Carol': 122000,
'Frank': 88000,
'Eve': 93000}
## 한 줄 코드
top_earners = [(k,v) for k, v in employees.items()
if v >= 100000]
## 출력
print(top_earners)
# [('Alice', 100000), ('Carol', 122000)]
items()는 딕셔너리 객체의 메서드임. (key, value)의 쌍을 tuple로 묶어서 반환함.
# 6
w = [[x for x in line.split() if len(x) > 3]
for line in text.split('\n')]
print(w)
split()은 문자열(str) 객체의 메서드로, text.split('\n') 코드에서는 개행문자를 기준으로 텍스트를 나눔. 각 줄을 요소로 하는 리스트를 반환함.line.split()에서 입력이 없으면, 개별 단어 단위로 쪼갬(공백 기준). 즉, 각 줄을 개별 단어로 분리해 냄.# 7
print([line.strip() for line in open("@@@.py")])
strip()은 문자열(str)객체의 메서드로, 문자열의 양쪽 끝에서 공백이나 특정 문자를 제거하는 역할을 함.