Object Reference
1. reference
파이썬 참조 심화 // 실수할 수 있는 부분이나 중급 이상 문법
1)파이썬 객체 참조 다양한 특징
2)Copy
3)Deep copy - 깊은 복사
4)매개변수 전달 주의할 점
# Chapter05-1
# 파이썬 심화
# 객체 참조 중요한 특징들
# Python Object Referrence
print('EX1-1 -')
print(dir()) #dir 함수를 바로 쓰면 이 05_01 파일에 대해 나올 수 있다
# __name__으로 실행
# id vs __eq__ (==) 증명
x = {'name': 'kim', 'age': 33, 'city': 'Seoul'}
y = x
print('EX2-1 -', id(x), id(y)) #두 변수의 id가 같다/ 다른 변수로 하나의 주소 (얕은 복사) - 같은 객체,x의 주소를 복사한다
print('EX2-2 -', x == y) #값이 같은지 - true / / 객체가 복사되었고 여러가지 변수가 하나를 보고 있는 것(즉 별개의 객체가 아니다!)
print('EX2-3 -', x is y) #id값이 같은 객체를 보고 있는지- true
print('EX2-4 -', x, y) #같은 값이 출력이 된다
x['class'] = 10 #class:10을 딕셔너리에 넣으면
print('EX2-5 -', x, y) #둘다 위에 추가된 값이 들어간다
print()
print()
z = {'name': 'kim', 'age': 33, 'city': 'Seoul', 'class': 10}
print('EX2-6 -', x, z) # 값만 같은 다른 객체
print('EX2-7 -', x is z) # 같은 객체 아님 - false
print('EX2-8 -', x is not z) #true
print('EX2-9 -', x == z) # 값이 같다 - true (값은 같지만 같은 객체는 아니다)
# 객체 생성 후 완전 불변 -> 즉, id는 객체 주소(정체성)비교, ==(__eq__) 는 값 비교
#즉 z는 x와 id가 다르지만 ==는 맞다(magic method로 __eq__)
#그러므로 is로 id값이 같은지를 확인하는 것이 훨씬 정확
print()
print()
# 튜플 불변형의 비교
tuple1 = (10, 15, [100, 1000])
tuple2 = (10, 15, [100, 1000])
print('EX3-1 -', id(tuple1), id(tuple2)) #다른 객체. 값은 같지만 다른 주소
print('EX3-2 -', tuple1 is tuple2) # false
print('EX3-3 -', tuple1 == tuple2) # true
print('EX3-4 -', tuple1.__eq__(tuple2)) #위와 같은 말(매직 메서드)
print()
print()
# Copy, Deepcopy( 얕은 복사,깊은 복사)
# Copy(얕은 복사: x= y 같은 형식)
tl1 = [10, [100, 105], (5, 10, 15)]
tl2 = tl1
tl3 = list(tl1)
print('EX4-1 -', tl1 == tl2) #값은 같다
print('EX4-2 -', tl1 is tl2) #id값도 같다
print('EX4-3 -', tl1 == tl3) #값은 같다
print('EX4-4 -', tl1 is tl3) #false - 타입형의 생성자를 이용해서 새롭게 만들면 다른 주소를 가지게 된다
# 증명
tl1.append(1000)
tl1[1].remove(105)
print('EX4-5 -', tl1) #변경
print('EX4-6 -', tl2) #변경
print('EX4-7 -', tl3) #변경되지 않는다(다른 객체니까)
print()
# print(id(tl1[2])) #튜플에 원소를 추가하기 전 - id주소가 아래 추가후와 다르다
tl1[1] += [110, 120]
tl1[2] += (110, 120)
print('EX4-8 -', tl1)
print('EX4-9 -', tl2) # 튜플 재 할당(객체 새로 생성)// 튜플은 불변 그러므로 원래의 튜플과 다른 id값을 가진 새 튜플이 생긴 것
print('EX4-10 -', tl3)
# print(id(tl1[2])) #튜플에 원소 추가 후 - 다른 객체가 되어서 id주소가 달라진다
#즉 리스트 안에 튜플은 위험성이 있다(수정되면 다른 객체가 되니까)
print()
print()
#얕은 복사: 값이 같고 x=y했으면 id 주소도 같다. 즉, 수정시 함께 수정된다. x, y각각 같은 값을 지정해주었으면 당연히 id가 다르다(다른 객체)
# Deep Copy
# 장바구니 // 인터넷 쇼핑 장바구니- copy, deep copy를 혼동하면 큰 문제가 생긴다
class Basket:
def __init__(self, products=None): #아무것도 넣지 않았으면 기본값 None
if products is None:
self._products = [] #아무것도 안 들어있으면 리스트 선언
else:
self._products = list(products) #안에 뭐가 있으면 product라는 변수를 가지고 또 다른 list를 생성(값은 같은 다른 객체)/초기화
#원본이 수정되지 않도록 새로운 리스트 생성
def put_prod(self, prod_name): #장바구니에 담기
self._products.append(prod_name)
def del_prod(self, prod_name): #장바구니에서 빼기
self._products.remove(prod_name)
import copy #원본이 수정되지 않도록 (객체 복사 시)해주는 패키지
basket1 = Basket(['Apple', 'Bag', 'TV', 'Snack', 'Water'])
basket2 = copy.copy(basket1) # copy패키지의 copy 메서드를 호출해서 basket1을 복사
basket3 = copy.deepcopy(basket1) #copy패키지의 deepcopy 메서드로 복사
print('EX5-1 -', id(basket1), id(basket2), id(basket3)) #각각의 id값 확인 : 다 다르다 (객체를 copy매서드, deepcopy메서드를 쓰든 모두 id가 다르다)
print('EX5-2 -', id(basket1._products), id(basket2._products), id(basket3._products)) #바구니로 접근하기 위해서는 인스턴스 변수로 접근 (클래스 Basket을 이용해서 만든 인스턴스들 안에 있는 인스턴스 변수를 copy했으니까)
# copy로 하면 _products는 모두 같음 [얕은 복사] : 인스턴스 (basket1, 2)와 안에 있는 product도 같은 주소// 표면적인 객체, 안에 있는 리스트 모두 복사 - 그러므로 원본을 수정하면 2도 수정이 된다 (문제가 있다)
# deepcopy는 _products(인스턴스 변수에 할당된 id reference주소까지 깊게 쫓아가서 다른 주소로 복사를 한다 : 안에까지 다른)
print()
basket1.put_prod('Orange')
basket2.del_prod('Snack')
print('EX5-3 -', basket1._products) #Orange추가, Snack 삭제 (그러므로 원본이 아니어도 같은 객체를 참조하고 있기 때문에 어느쪽에서 변경하든 원본, 복제본 모두 변화 수용)
print('EX5-4 -', basket2._products) #Orange추가, Snack삭제
print('EX5-5 -', basket3._products) #아무런 변경 없음
#즉 deepcopy가 가장 안전(포함 변수까지 복사해서 새로운 객체에 할당)
#copy는 바로 접근 가능한 데이터 타입만 복사
print()
print()
# 함수 매개변수 전달 사용법
def mul(x, y): #함수: x와 y를 더해서 다시 x에 할당// 매개변수 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) #[10,100,5,10] [10,100,5,10] [5,10]
# a가 가변형 원본 데이터 변경// 왜냐? a의 주소를 매개변수로 같이 보내게 되어서 수정되는 값이 반영된다
c = (10, 100)
d = (5, 10)
print('EX6-2 -', mul(c, d), c, d) #(10,100,5,10) (10,100) (5,10)
# 불변형 c -> 원본 데이터 변경 안됨// 튜플은 불변이지만 합해서 아예 새로운 튜플이 됨, 불변형이라 매개변수로 주소가 넘어가도 변하지 않음
# 파이썬 불변형 예외
# 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)) #tuple로 직접 생성해서 할당을 했는데도 다른 객체처럼 주소가 달리 나오지 않음. 어차피 불변이라 파이썬이 효율적으로 사용하기 위해 같은 주소로 참조
print('EX7-2 -', tt3 is tt1, id(tt3), id(tt1)) #마찬가지로 같은 id. 같은 객체. 어떻게 복사를 하든 하나의 id주소로 통일.
tt4 = (10, 20, 30, 40, 50)
tt5 = (10, 20, 30, 40, 50)
ss1 = 'Apple'
ss2 = 'Apple'
print('EX7-3 -', tt4 is tt5, tt4 == tt5, id(tt4), id(tt5)) #위의 tuple()처럼 같은 값을 가진 새로운 객체를 만드는 것 같지만 튜플은 불변형, 다 같은 id, 객체로 취급
print('EX7-4 -', ss1 is ss2, ss1 == ss2, id(ss1), id(ss2)) #string도 마찬가지
#효율성을 위해 같은 값을 가진 불변형들은 하나로 본다 (컴퓨터가 보기에는 수정 가능성도 없으니까 하나로 본다
# Deep Copy는 안에 있는 변수까지 다 새롭게 복사해서 만드니까 시간이 더 오래 걸릴 수 있다