print('ex1-1')
print(dir())
print()
print()
print(__name__) # <--- 이 스코프 안의 속성을 볼 수 있음
# id vs __eq__(==) 증명
x = {'name':'kim', 'age':33, 'city':'Seoul'}
y = x
print('ex2-1', id(x), id(y)) # 동일한 id값, 즉 얕은 복사임
print('ex2-2', x == y) # 값이 같은지 물어봄
print('ex2-3', x is y) # id 가 같은지 물어봄
print('ex2-4', x, y)
x['class'] = 10
print('ex2-5', x,y)
z = {'name':'kim', 'age':33, 'city':'Seoul', 'class':10}
print('ex2-6', x,z)
print('ex2-7', x is z) # 값이 같지만 id값이 다름 -> False
print('ex2-8', x is not z)
print('ex2-8', x == z)
객체 생성 후 완전 불변 -> 즉, id는 객체 주소 비교, ==(eq) 값 비교
참고로 is keyword를 == 비교 연산자보다 먼저 사용하는 습관이 필요함. 이유는 속도와 퍼포먼스에서 엄청난 차이를 보임
예를 들어 key - value가 백만건 이상 되어 있다면 동등비교 연산자(==)로 사용 할 경우 key값 100만개를 하나씩 비교하기에 엄청나게 소요되지만
is, is not 키워드의 경우 해당 객체의 id값 하나만 각각 비교하기에 수백, 수천배 그 이상의 효율성을 가질수 있음
print()
print()
tuple1 = (10, 15, [100, 1000])
tuple2 = (10, 15, [100, 1000])
print('ex3-1', id(tuple1), id(tuple2))
print('ex3-2', id(tuple1) is id(tuple2))
print('ex3-3', tuple1 == tuple2)
print('ex3-4', tuple1.__eq__(tuple2))
output
ex3-1 1672527758080 1672528021248
ex3-2 False
ex3-3 True
ex3-4 True
ex3-1에서 중요한점은 순수 튜플자료형이라면(내부 요소값도 동일한 구조라면) 주소값이 같지만 만약 mutable한 자료형이 있는 경우 객체참조를 하지 않고 다른(새로운) 주소값을 갖게 되는 거조.
고급으로 가는 길은 이렇게 자료형까지 염두해두어야 된다는점.
tl1 = [10, [100, 105], (5,10,15)]
tl2 = tl1
tl3 = list(tl1) # list메서드를 이용하면 새로운 객체를 생성함.
print('ex4-1', tl1 == tl2)
print('ex4-2', tl1 is tl2)
print('ex4-3', tl1 == tl3)
print('ex4-4', tl1 is tl3) # list()메서드로 인해 새로운 객체를 반환하게됨.
# 증명
tl1.append(1000)
tl1[1].remove(105)
print('ex4-5', tl1)
print('ex4-6', tl2)
print('ex4-7', tl3)
print()
print('tl1: ',id(tl1[2]))
tl1[1] += [110, 120]
tl1[2] += (110, 120) # 튜플 값 변동은 == 객체 생성을 의미함.
print('ex4-8', tl1)
print('ex4-9', tl2) # 튜플 재할당(객체 생성). 튜플의 특징인 불변(값 변동X )때문
print('ex4-10', tl3)
# print('tl1: ',id(tl1[2]))
# 결론적으로 말하자면, 리스트안에 튜플을 넣어서 사용하게되면 id값 변동으로 인해 안전하지 않음.
# 1) 데이터 소실문제
# 2) 성능문제(새로운 객체생성은 cost가 많이듬)
쇼핑몰 장바구니 결제에서 실수(Copy, Deep Copy)하는 예시를 보여 드릴게요.
class Basket():
def __init__(self, products=None):
if products is None: # 장바구니에 상품을 넣지 않았을 경우
self._products = []
else: # 장바구니 상품을 넣었을 경우
self._products = list(products)
# 장바구니에서 하는 action 1)넣고 2)빼는것
def put_prod(self, prod_name):
self._products.append(prod_name)
def del_prod(self, prod_name):
self._products.remove(prod_name)
#remove는 인자로 값을 받아 지우고, pop과 del의 경우 index를 받아 삭제함.
import copy
# 경우에 따라 맞는 얕은, 깊은 복사를 하게되요.
basket1 = Basket(['Apple', 'Bag', 'TV', 'Snack', 'Water'])
basket2 = copy.copy(basket1) # 일반 카피로 복사.
basket3 = copy.deepcopy(basket1) # deepcopy로 원본의 속성값까지 쫓아가서 복사한다는 말임
print('ex5-1', id(basket1), id(basket2), id(basket3))
# basket1과 basket2는 서로 다른 주소를 가짐. 그렇다고 다른 객체라고 볼수 없는 것이 basket1에서 값변동이 발생하면 basket2에서도 동일하게 변동을 수직으로 받게됨.
#대신 basket2변동이 basket1의 값 변동에 영향을 주진 않음. basket1과 basket3은 주소가 다르고 값은 다른 별도의 객체임. 값변동이 서로에게 영향을 주지 않음.
print('ex5-2', id(basket1._products), id(basket2._products), id(basket3._products))
# basket1과 basket2의 인스턴스 속성값 동일, but basket1과 basket3의 속성값은 다름
print()
basket1.put_prod('Orange') # 1번 바구니에 오렌지를 넣음
basket2.del_prod('Snack') # 2번 바구니에 snack을 뺌
print('ex5-3', basket1._products)
# 1번 바구니에 오렌지가 들어 간거 확인
print('ex5-4', basket2._products)
# 2번 바구니에 오렌지를 넣지 않았는데 들어가 있음 이는 2번바구니가 1번바구니를 copy.copy()로 참조하기 때문
print('ex5-5', basket3._products) # 3번 바구니는 정상적으로 오랜지가 들어가지도 스낵이 빠지지 않는 별도의 바구니로서 구실을 하고 있다는게 확인되고 있음, 즉 처음 클래스의 인자값으로 받은 것을 제외하고는 어떠한 변동이 없음
# 만약 뭔가가 불안할 경우 객체의 경우 깊은 복사를 한다면 더 안전하게 취급 할 수 있음.
def mul(x,y):
x += y
return x
x = 10
y = 5
print('ex6-1', mul(x,y), x,y)
print()
a = [10, 100]
b = [5, 10]
print('ex6-2 - ', mul(a,b), a,b)
# 변수 a의 원본이 변경된 문제 발생. 지금 한것이라고는 리스트를 함수의 인자로 넘겼는데 원본이 변경되었네요?
# 가변형 a -> 데이터 변경
# 만약 튜플인 경우는 어떻게 될까요?
c = 10, 100
d = 5, 10
print('ex6-2', mul(c,d), c,d)
# 리스트를 인자로 넘겼을때와 사뭇다르게. 값이 변경되지 않습니다. 이 말은 튜플이 특징이 불변형 이기 때문이에요.
# 이때 우리가 생각해야 할 것은. 인자값으로 리스트로 넘길지 튜플로 넘길지 입니다.
# 원본의 값이 변경되도 상관없다면 리스트를 넣어도 되지만 변경되서 안된다면 tuple로 하면 안전합니다.
# 사용하고자 하는 데이터의 구조와 원칙에 맞게 사용하면되요. 무조건 리스트를 쓰는게 아니에요.
str, bytes, frozenset, Tuple : 사본 생성 X -> 참조 반환
tt1 = 1, 2, 3, 4, 5
tt2 = tuple(tt1)
tt3 = tt1[:]
print('ex7-1', tt1 is tt2, id(tt1), id(tt2)) # 불변형이지만 성능을 위해 기존에 고수하던 원칙을 깨고 참조를 반환함!
# 결과: ex3-1 True 2407472866384 2407472866384
print('ex7-2', tt3 is tt1, id(tt3), id(tt1))
# 여기서는 str이나 tuple은 동일한 값을 갖고 있는 경우 동일한 주소 값!!을 가지게 된다.
tt4 = 10, 20, 30, 40, 50
tt5 = 10, 20, 30, 40, 50
ss1 = 'hello'
ss2 = 'hello'
print('ex7-3', tt4 is tt5, tt4 == tt5, id(tt4), id(tt5))
print('ex7-4', ss1 is ss2, ss1 == ss2, id(ss1), id(ss2))
print(ss1 is ss2, id(ss1) is id(ss2))
# 그냥 아~ 이런게 있구나 하고 넘어가면 좋을듯 해요. 결과는 True, False로 뜨네요.