(study) 파이썬 입문 (14주차) 6월6일
클래스 공부 7,8,9,10 단계
-
(1/8) 클래스 공부 7단계 (1)
-
(2/8) 클래스 공부 7단계 (2)
-
(3/8) 클래스 공부 7단계 (3)
-
(4/8) 클래스 공부 8단계
-
(5/8) 클래스 공부 9단계 (1)
-
(6/8) 클래스 공부 9단계 (2)
-
(7/8) 클래스 공부 10단계 (1)
-
(8/8) 클래스 공부 10단계 (2)
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
-
다시 함수를 공부해봅시다.
def f(x):
return x+1
f(3)
-
함수의 사용방법?
- 입력으로 변수 x를 받음 = 입력으로 인스턴스 x를 받음.
- 출력으로 변수 x+1을 리턴 = 출력으로 인스턴스 x+1을 리턴.
-
사실1: 파이썬에서 함수는 인스턴스를 입력으로 받고 인스턴스를 출력한다.
-
함수의 자료형?
?f
- type이 functiond이다.
- f는 function class의 instance이다.
- 결국 f 역시 하나의 오브젝트에 불과하다.
-
사실2: 함수도 결국 인스턴스이다. -> 함수의 입력으로 함수를 쓸 수도 있고 함수의 출력으로 함수가 나올 수도 있다.
(예제1) 숫자입력, 함수출력
def f(a):
def _f(x):
return (x-a)**2
return _f
g=f(10) # g(x)=(x-10)**2
g(2) # (2-10)**2 = 64
- 해석: f(a)는 a를 입력으로 받고 g(x)=(x-a)^2 를 함수를 리턴해주는 함수
(예제1)의 다른표현: 익명함수 lambda
표현1
def f(a):
_f = lambda x: (x-a)**2 ### lambda x: (x-a)**2 가 실행되는 순간 함수오브젝트가 만들어지고 그것이 _f 로 저장됨
return _f
g=f(10) # g(x)=(x-10)**2
g(3) # (3-10)**2 = 49
표현2
def f(a):
return lambda x: (x-a)**2
g=f(10) # g(x)=(x-10)**2
g(3) # (3-10)**2 = 49
-
lambda x: (x-a)**2
는 $\text{lambda}(x) = (x-a)^2$의 느낌으로 기억하면 외우기 쉽다. -
lambda x: (x-a)**2
는 "아직 이름이 없는 함수오브젝트를 (가칭 lambda 라고 하자) 만들고 기능은 x를 입력으로 하고 (x-a)**2를 출력하도록 하자" 라는 뜻으로 해석하면 된다.
(예제2) 함수입력, 숫자출력
def f(x):
return x**2
def d(f,x): # 함수를 입력을 받는 함수를 정의
h=0.000000000001
return (f(x+h)-f(x))/h
d(f,4) # f'(4) = 2*4 = 8
(예제3) 함수입력, 함수출력
def f(x):
return x**2
def derivate(f):
def df(x):
h=0.000000000001
return (f(x+h)-f(x))/h
return df
ff = derivate(f)
ff(7) # f의 도함수
원래함수 시각화
x = np.linspace(-1,1,100)
plt.plot(x,f(x))
도함수 시각화
x = np.linspace(-1,1,100)
plt.plot(x,ff(x))
(예제3)의 다른표현
def f(x):
return x**2
def derivate(f):
h=0.000000000001
return lambda x: (f(x+h)-f(x))/h
ff = derivate(f)
ff(10)
(예제4) 함수들의 리스트
flst = [lambda x: x, lambda x: x**2, lambda x: x**3]
flst
for f in flst:
print(f(2))
for f in flst:
plt.plot(x,f(x),'--')
위의코드는 아래와 같음
plt.plot(x,(lambda x: x)(x),'--')
plt.plot(x,(lambda x: x**2)(x),'--')
plt.plot(x,(lambda x: x**3)(x),'--')
-
지금까지 개념
- 함수: 변수를 입력으로 받아서 변수를 출력하는 개념
- 변수: 어떠한 값을 저장하는 용도로 쓰거나 함수의 입력 혹은 출력으로 사용함
-
파이썬의 함수형프로그래밍을 잘하려면?
- 변수든 함수이든 둘다 인스턴스임.
- 변수를 함수처럼: 메소드
- 함수를 변수처럼($\star$): 함수자체를 함수의 입력으로 혹은 출력으로 쓸 수도 있음. 함수를 특정 값처럼 생각해서 함수들의 list를 만들 수도 있다.
-
함수 오브젝트의 비밀?
f = lambda x: x+1
lst=[1,2,3]
set(dir(f)) & {'__call__'}
- 함수 오브젝트에는 숨겨진 기능
__call__
이 있다.
f.__call__(3) # f(3)
f.__call__(4) # f(4)
- 여기에 우리가 정의한 내용이 있다.
-
함수처럼 쓸 수 없는 인스턴스는 단지 call이 없는 것일 뿐이다.
class Klass:
def __init__(self):
self.name='guebin'
a=Klass()
a()
- a는 callable이 아니라고 한다.
class Klass2(Klass): # 상속
def __call__(self):
print(self.name)
b=Klass2()
b()
- b는 callable object! 즉 숨겨진 메서드로
__call__
를 가진 오브젝트! - Klass는 callable object를 만들지 못하지만 Klass2는 callable object를 만든다.
-
클래스로 함수를 만들기
class AddConstant:
def __init__(self,c):
self.c = c
def __call__(self,a):
return a + self.c
f = AddConstant(3) # callable object생성, f.c에는 3이 저장되어있음.
f(5) # f.c 와 5를 더하는 기능을 수행, # f(x) = x+3 을 수행함
f(10)
-
클래스도 그러고보니까 오브젝트
아니었나? 클래스도 함수처럼 Klass()
와 같이 사용하면 인스턴스를 만들었음. -> Klass.__call__()
은 Klass()
와 같은 역할을 할 것이다.
class Klass:
def __init__(self):
self.name='hynn'
a=Klass.__call__() # 이것이 a=Klass() 와 같은 효과
a.name
- 파이썬의 비밀1: 자료형은 클래스의 이름이다.
- 파이썬의 비밀2: 클래스에는
__str__
처럼 숨겨진 메서드가 존재한다. 이를 이용하여 파이썬 내부의 기능을 가로챌 수 있다. - 파이썬의 비밀3: 주피터노트북에서는 "오브젝트이름+엔터"를 쳐서 나오는 출력은
__repr__
로 가로챌 수 있따. (주피터의 비밀) - 파이썬의 비밀4: 함수와 클래스는 숨겨진 메소드에
__call__
을 가진 오브젝트일 뿐이다.
-
아래와 같은 예제들을 관찰하여 for문을 복습하자.
(예제1)
for i in [1,2,3,4]:
print(i)
(예제2)
for i in (1,2,3,4):
print(i)
(예제3)
for i in '1234':
print(i)
(예제4)
a=5
for i in a:
print(i)
- 5라고 출력되어야 하지 않나?
-
의문1:
for i in ???:
print(i)
에서 ??? 자리에 올수 있는 것이 무엇일까?
(예제5)
상황1
lst = [[1,2,3,4],[3,4,5,6]]
for l in lst:
print(l)
상황2
df=pd.DataFrame(lst)
df
for i in df:
print(i)
칼럼이름들이 나오는것 같음 -> 확인해보자.
df.columns = pd.Index(['X'+str(i) for i in range(1,5)])
df
for i in df:
print(i)
-
의문2: for의 출력결과는 어떻게 예측할 수 있을까?
-
의문1의 해결: 아래의 ??? 자리에 올 수 있는 것은 dir()하여 __iter__
가 있는 object이다.
for i in ???:
print(i)
이러한 오브젝트를 iterable object라고 한다.
-
확인
a=[1,2,3]
set(dir(a)) & {'__iter__'}
a=1,2,3
set(dir(a)) & {'__iter__'}
a='123'
set(dir(a)) & {'__iter__'}
a=5
set(dir(a)) & {'__iter__'}
- 예상대로 예제1~4에서는 int의 클래스의 instance만
__iter__
가 없다.
-
__iter__
의 역할: iterable object를 iterator로 만들 수 있다!
lst = [1,2,3]
lst
lst[1] # 충실한 리스트
ltor = iter(lst)
#ltor = lst.__iter__()
ltor
ltor[1] # 더이상 리스트가 아니다
ltor?
-
iterator가 되면 무엇이 좋은가? -> 숨겨진 기능 __next__
가 열린다.
set(dir(lst)) & {'__next__'}, set(dir(ltor)) & {'__next__'}
- lst에는
__next__
가 없지만 ltor에는 있다!
-
그래서 __next__
의 기능은? -> 원소를 차례대로 꺼내준다 + 더 이상 꺼낼 원소가 없으면 StopIteration Error를 발생시킨다.
lst
ltor.__next__()
ltor.__next__()
ltor.__next__()
ltor.__next__()
-
for문의 동작원리
for i in lst:
print(i)
(1) lst.__iter__()
혹은 iter(lst)
를 이용하여 lst를 iterator로 만든다. (iterable object를 iterator object로 만든다)
(2) iterator에서 .__next__()
함수를 호출하고 결과를 i에 저장한뒤에 for문 블락안에 있는 내용 (들여쓰기 된 내용)을 실행한다. -> 반복
(3) StopIteration 에러가 발생하면 for문을 멈춘다.
-
아래의 ??? 자리에 올 수 있는 것이 iterable object가 아니라 iterator 자체이여도 for문이 돌아갈까? (당연히 돌아가야 할 것 같음)
for i in ???:
print(i)
for i in iter([1,2,3]):
print(i)
- 당연히가능!
-
a가 iterator일때 iter(a) 의 출력결과가 a와 같도록 조정한다면 for문의 동작원리 (1)-(3)을 수정하지 않아도 좋다. -> 실제로 이렇게 동작한다.
-
요약
- iterable object는 숨겨진 기능으로
__iter__
를 가진다. - iterator object는 숨겨진 기능으로
__iter__
와__next__
를 가진다. (즉 iterator는 그 자체로 iterable object가 된다!)
lst = [1,2,3]
ltor = iter(lst)
set(dir(lst)) & {'__iter__', '__next__'}
set(dir(ltor)) & {'__iter__', '__next__'}
-
의문2의 해결: for의 출력결과는 어떻게 예측할 수 있을까? iterator를 만들어서 .__next__()
의 출력값을 확인하면 알 수 있다.
for i in df:
print(i)
dftor=iter(df)
dftor.__next__()
dftor.__next__()
dftor.__next__()
dftor.__next__()
dftor.__next__()
-
파이썬에서 for문을 처음 배울 때: range(5)를 써라!
for i in range(5):
print(i)
- range(5)가 도데체 무엇이길래?
-
range(5)
의 정체는 그냥 iterable object이다.
set(dir(range(5))) & {'__iter__','__next__'}
-
그래서 언제든지 iterator로 바꿀 수 있다.
rtor= iter(range(5))
rtor
set(dir(rtor)) & {'__iter__','__next__'}
-
for문에서 range(5)가 행동하는 방법?
rtor = iter(range(5))
rtor.__next__()
rtor.__next__()
rtor.__next__()
rtor.__next__()
rtor.__next__()
rtor.__next__()
-
이터레이터의 개념을 알면 for문에 대한 이해도가 대폭 상승한다.
for i in zip([1,2,3],'abc'):
print(i)
- zip은 뭐지?
zip([1,2,3],'abc')
-
어차피 for i in ????
: 의 ???? 자리는 iterable object의 자리이다.
set(dir(zip([1,2,3],'abc'))) & {'__iter__','__next__'}
-
__next__()
함수가 있음 $\to$zip([1,2,3],'abc')
은 그자체로 iterator 였다!
z= zip([1,2,3],'abc')
z.__next__()
z.__next__()
z.__next__()
z.__next__()
-
내가 이터레이터를 만들어보자.
class Klass: # 찌를 내는순간 for문이 멈추도록 하는 이터레이터를 만들자
def __init__(self):
self.candidate = ["묵","찌","빠"]
def __iter__(self):
return self
def __next__(self):
action = np.random.choice(self.candidate)
if action == "찌":
print("찌가 나와서 for문을 멈춥니다")
raise StopIteration
else:
return action
a= Klass()
set(dir(a)) & {'__iter__', '__next__'} # a는 이터레이터!
a.__next__()
a.__next__()
a.__next__()
a.__next__()
for i in a:
print(i)
- 파이썬의 비밀1: 자료형은 클래스의 이름이다.
- 파이썬의 비밀2: 클래스에는
__str__
처럼 숨겨진 메서드가 존재한다. 이를 이용하여 파이썬 내부의 기능을 가로챌 수 있다. - 파이썬의 비밀3: 주피터노트북에서는 "오브젝트이름+엔터"를 쳐서 나오는 출력은
__repr__
로 가로챌 수 있따. (주피터의 비밀) - 파이썬의 비밀4: 함수와 클래스는 숨겨진 메소드에
__call__
을 가진 오브젝트일 뿐이다. - 파이썬의 비밀5: for문의 비밀 (iterable object, iterator, StopIteration Error)
커널을 재시작하고 아래를 관찰하자
예제1
-
관찰1: 함수내의변수 출력
def f():
x=10
print(x)
f()
-
관찰2: 함수내의 변수가 없을 경우 출력이 되지 않음
def g():
print(x)
g()
-
관찰3: 동일한 이름의 변수가 globald에 있다면 함수내에 (local에) 그 이름의 변수가 선언되지 않아도 global의 변수를 빌려서 사용함
x=20
def g():
print(x)
g()
-
관찰4: f()가 실행되면서 x=10
이 함수내에 (=local에) 실행되지만 이 결과가 외부의 x=20에 (=global에) 영향을 미치지는 못함
f()
x
예제2
(코드1)
x = 38
def nextyear():
y= x+1
print(x,y)
nextyear()
(코드2)
x = 38
def nextyear():
y= x+1
print(x,y)
x= 0
nextyear()
-
해석:
- 잘못된 해석: 코드1은 실행되었고, 코드2에서 에러가 났다. 코드1과 2의 차이점은
x=0
이라는 코드가 코드2에 추가로 포함되어있다는 것이다. 따라서x=0
이 잘못된 코드이고 이걸 실행하는 과정에서 에러가 발생했다. - 올바른 해석: 코드1에서는 x가 global variable 이고 코드2에서는 x가 local variable이어서 생기는 문제
-
코드2의 올바른 수정
x = 38
def nextyear():
x= 0
y= x+1
print(x,y)
nextyear()
-
예비학습이 주는 교훈
(원칙1) global 에서 정의된 이름은 local 에서 정의된 이름이 없을 경우 그를 대신할 수 있다 (local은 경우에 따라서 global에 있는 변수를 빌려 쓸 수 있다)
(원칙2) local과 global에서 같은 이름 'x'가 각각 정의되어 있는 경우? global의 변수와 local의 변수는 각각 따로 행동하며 서로 영향을 주지 않는다. (독립적이다)
- 만약에 local이 global의 변수를 같이 쓰고 있었다고 할지라도, 추후 새롭게 local에 새롭게 같은 이름의 변수가 정의된다면 그 순간 local과 global의 변수를 각자 따로 행동하며 서로 영향을 주지 않는다. $\to$ 아래예제확인
x= 10
def f():
print(x)
f() # x를 빌려쓰는 신세
def f():
x=20 # 이제 새롭게 x를 정의했으니까
print(x)
f() # 다른길을 간다.
-
이전에 공부하였던 인스턴스변수와 클래스변수 역시 비슷한 행동을 보인다.
class Moo:
x=0 # 클래스변수
moo=Moo()
(관찰1)
Moo.x, moo.x
- moo.x는 사실 정의한적 없지만 Moo.x를 빌려쓰고 있다. (원칙1)
(관찰2)
Moo.x = 100
Moo.x, moo.x
- Moo.x 를 변화시키면 moo.x 도 변화한다 (빌려쓰고 있는 것이니까, 원칙1의 재확인)
(관찰3)
moo.x = 200
Moo.x, moo.x
- moo.x=200을 하는 순간 새롭게 인스턴스변수를 선언한 셈이된다. 따라서 원칙2가 적용되어 이제부터 Moo.x 와 moo.x 는 서로 독립적으로 행동한다.
(관찰4)
Moo.x = - 99
Moo.x, moo.x
moo.x = 99
Moo.x, moo.x
- Moo.x 를 바꾼다고 해서 moo.x 가 영향받지 않고 moo.x 를 바꿔도 Moo.x 가 영향받지 않음 (완전히 독립, 원칙2의 재확인)
-
포인트: (1) 클래스변수와 인스턴스 변수의 구분 (2) 인스턴스 변수가 정의되지 않으면 클래스변수를 빌려쓸 수 있음 (3) 인스턴스변수와 클래스변수가 같은 이름으로 저장되어 있으면 각각 독립적으로 행동
-
self 비밀: 사실 클래스에서 정의된 함수의 첫번째 인자의 이름이 꼭 self일 필요는 없다. (무엇으로 전달하든 클래스안에서 정의된 메소드의 첫번째 인자는 기본적으로 instance의 태명역할을 한다)
class Moo:
def __init__(abab):
abab.name = 'guebin'
def f(cdcd):
print(cdcd.name)
moo=Moo()
moo.name
moo.f()
-
인스턴스 메서드: 위의 __init__
와 f
와 같이 첫번째 인자를 인스턴스의 태명으로 받는 함수를 인스턴스 메서드 (간단히 메서드) 라고 한다.
- 인스턴스 메소드는
self.f()
와 같이 사용한다. 의미는f(self)
이다.
moo.name = 'hynn'
moo.__init__() # 인스턴스메서드의 사용예시: self.__init__()의 꼴로 사용
moo.name
moo.f() # 인스턴스메서드의 사용예시: self.__init__()의 꼴로 사용
-
아래와 같이 사용할 수 없다.
클래스.메서드 할 수 없음
Moo.__init__()
Moo.f()
-
클래스 메서드: 함수의 첫 인자로 클래스오브젝트를 받는 메서드를 클래스메서드라고 한다.
-
목표: Moo.f()
와 같은 형태로 사용할 수 있는 함수를 만들어 보자 -> 클래스메서드를 만들어보자!
class Moo:
def f(self):
print("인스턴스 메서드")
moo=Moo()
moo.f()
Moo.f()
class Moo:
@classmethod
def f(cls): # 함수의 첫 인자로 클래스오브젝트를 받는다. cls는 클래스 Moo의 별명?이라고 생각하면 된다.
print("클래스 메서드")
moo=Moo()
Moo.f()
moo.f() # 인스턴스 메서드를 따로 정의한적은 없지만 같은 이름의 클래스 메서드가 있으므로 빌려와서 씀!
-
예제
class Moo:
@classmethod
def set_class_x(cls,value): # 클래스 메서드
cls.x = value # 클래스변수선언, note: Moo.x = value 와 같은 코드!
def set_instance_x(self,value): # 인스턴스 메서드
self.x = value # 인스턴스 변수선언
moo=Moo()
Moo.set_class_x(10) # 클래스메서드로 클래스변수에 10을 설정
Moo.set_instance_x(10) # 클래스에서 인스턴스 메서드를 사용 -> 사용불가
Moo.x, moo.x # 인스턴스변수는 따로 설정하지 않았지만 클래스 변수값을 빌려쓰고 있음
moo.set_class_x(20) # 인스턴스에서는 원래 set_class_x 라는 메서드는 없지만 클래스에서 빌려씀
Moo.x, moo.x # 현재 moo.x는 클래스 변수를 빌려쓰고 있는 상황이므로 같이 바뀜
moo.set_instance_x(-20) # 인스턴스에서 인스턴스 메서드를 사용하여 인스턴스 변수값을 -20으로 설정
#-> 이때부터 인스턴스변수와 클래스 변수는 서로 독립적인 노선을 간다.
Moo.x, moo.x
Moo.set_class_x(30) # 독립적인 노선을 가기로 했으므로 클래스변수만 30으로 바뀜
Moo.x, moo.x
moo.set_class_x(-40) # 여전히 인스턴스에서 set_class_x라는 함수는 없으므로 클래스메소드를 빌려쓰고 있음
Moo.x, moo.x
-
스태틱 메서드: 첫 인자로 인스턴스와 클래스 모두 받지 않음. (클래스안에 정의되어 있지만 그냥 함수와 같음)
class Cals:
@staticmethod
def add(a,b):
return a+b
@staticmethod
def sub(a,b):
return a-b
fs = Cals()
fs.add(1,2)
fs.sub(1,2)
- fs는 그냥 함수들을 묶어놓은 느낌? 정리하게 편하게?
-
예제
lst = list('abcd')
lst
''.join(lst)
-
해설: ''는 string object 이고 .join
는 string object에 소속된 메서드이다.
a=''
a.join(lst) # join(a,lst) 와 같은 효과
-
join의 간단한 사용방법
'-'.join(lst)
-
파이썬의 모든것은 객체이다: matplotlib의 다른 사용 (객체지향적 언어로 그림그리기!)
-
그림오브젝트 생성
fig = plt.figure() # 그림오브젝트 생성되고 fig라는 이름이 붙음
-
그림오브젝트의 액시즈를 확인 -> 아무것도 없음
fig.axes
-
(0,0)자리에 (가로=1,세로=1) 크기의 액시즈를 넣어보자.
fig.add_axes([0,0,1,1])
fig.axes
fig
-
(0,1.2) 위치에 (가로=1,세로=1) 크기의 액시즈 추가
fig.add_axes([0,1.2, 1,1])
fig
-
(0.5,0.5) 위치에 (가로=1, 세로=1) 크기의 그림 추가
fig.add_axes([0.5,0.5, 1,1])
fig
-
fig의 세번째 액시즈에 접근
a3 = fig.axes[2] # 이것역시 오브젝트임
a3
-
액시즈의 메소드중에 plot이 있음 -> 이것으로 그림을 그려봄
a3.plot([1,2,3],[4,5,3],'--r')
fig
-
다시 세번째 축에 접근하여 다른그림을 그려보자.
fig.axes[-1].plot([1,2,3],[5,4,3],':o')
fig
-
이제 첫번째 축에 접근하여 새로운 그림을 그려보자.
fig.axes[0].plot([1,2,3],[4,1,4],'--b')
fig
-
클래스에 대한 이해가 없다면 위와 같은 그림을 그리기도 힘들고 코드를 해석하기도 힘듦
-
아래의 코드를 관찰하자.
a=[1,2,3]
b=a
a=a+[4]
현재 a,b의 출력결과는?
a, b
-
이제 다시 아래의 코드를 관찰하자.
a=[1,2,3]
b=a
a.append(4)
현재 a,b의 출력결과는?
a,b
-
아래의 코드를 다시 살펴보자.
a=[1,2,3]
b=a
a.append(4)
a,b라는 변수들은 메모리에 어떻게 저장이 되어있을까?
상상력을 조금 발휘하면 아래와 같이 여길 수 있다.
(1) 메모리는 변수를 담을 방이 여러개 있는 호텔이라고 생각하자.
(2) 아래를 실행하였을 경우
a=[1,2,3]
- 메모리주소1에 존재하는 방을 a라고 하고, 그 방에 [1,2,3]을 넣는다.
(3) 아래를 실행하였을 경우
b=a
- 메모리주소38에 존재하는 방을 b라고 하고, 그 방에 a를 넣어야하는데, a는 [1,2,3]이니까 [1,2,3]을 넣는다.
(4) 아래를 실행하면
a.append(4)
- 방 a로가서 [1,2,3]을 [1,2,3,4]로 바꾼다.
- 그리고 방 b에는 아무것도 하지 않는다.
-
R에서는 맞는 비유인데, 파이썬은 적절하지 않은 비유이다.
id(a)
id(b)
실제로는 a,b가 저장된 메모리 주소가 동일함
-
파이썬에서는 아래가 더 적절한 비유이다.
(1) 메모리는 변수를 담을 방이 여러개 있는 호텔이라고 생각하자.
(2) 아래를 실행하였을 경우
a=[1,2,3]
- 메모리주소139851743661952에서 [1,2,3]을 생성해요
- 방 139851743661952의 방문에 a라는 포스트잇을 붙인다.
- 앞으로 [1,2,3]에 접근하기 위해서는 여러 메모리방중에서 a라는 포스트잇이 붙은 방을 찾아가면 된다.
(3) 아래를 실행하였을 경우
b=a
- a라는 포스트잇이 있는데, a라는 포스트잇이랑 b라는 포스트잇과 같은 효과를 주도록 한다.
- 쉽게말하면 b라는 포스트잇을 방 139851743661952의 방문에 붙인다는 이야기.
- 앞으로 [1,2,3]에 접근하기 위해서는 여러 메모리방중에서 a라는 포스트잇이 붙어 있거나 b라는 포스트잇이 붙어있는 방을 찾아가면 된다.
(4) 아래를 실행하면
a.append(4)
- a라는 포스트잇이 붙어있는 방으로 가서, 그 내용물 append함수를 써서 4를 추가하라. 즉 내용물 [1,2,3]을 [1,2,3,4]로 바꾸라.
- 같은방에 a,b라는 포스트잇이 모두 붙어있음. 따라서 b라는 포스트잇이 붙은 방을 찾아가서 내용물을 열어보면 [1,2,3,4]가 나온다.
-
결론: 파이썬의 모든것은 오브젝트이다. 그리고 모든 오브젝트는 메모리주소위에 올라간다. 하지만 그 메모리주소에 붙어있는 포스트잇이 하나라는 보장은 없다.