08 Aug 2016
|
python
static variable
stackoverflow에서 발췌
다음과 같이 static int를 정의하고 쓰던 사람이 파이썬에 놀러와서 질문을 했다.
void foo()
{
static int counter = 0;
counter++;
printf("counter is %d\n", counter);
}
“나 C 쓰던 사람인데 이런거 Python으로 어떻게 하니?”
그러자 어떤 현인이 다음과 같은 방식을 제안했다.
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
foo.counter = 0
요런 식으로 foo.counter를 초기화 시켜주고 쓰면 돼!
근데 저러면 초기화 안시키면 망하니까 decorator
를 쓰렴!
def static_vars(**kwargs):
def decorate(func):
for k in kwargs:
setattr(func, k, kwargs[k])
return func
return decorate
요렇게 정의하구
@static_vars(counter=0)
def foo():
foo.counter += 1
print "Counter is %d" % foo.counter
요렇게 쓰면 대!
진짜 되나 실험해보았다.
>>> def static_vars(**kwargs):
... def decorate(func):
... for k in kwargs:
... print k
... print kwargs[k]
... setattr(func, k, kwargs[k])
... return func
... return decorate
...
>>> @static_vars(counter=0)
... def foo():
... foo.counter += 1
... print "Counter is %d" % foo.counter
...
counter
0
>>> foo()
Counter is 1
>>> foo()
Counter is 2
>>> foo()
Counter is 3
참고로 setattr()
는 다음과 같은 형식이다.
setattr(object, name, value)
getattr()과 반대되는 녀석. 인자로는 object, string, value가 된다. 이 string은 존재하는 attribute 또는 새로운 attribute의 이름이 된다.
예시 > setattr(x, ‘foobar’, 123) == x.foobar = 123.
근데 같다고했는데 func.k = kwargs[k]하면 에러나네..
08 Aug 2016
|
python
iterator
generator
Iterator
예제를 보자.
>>> a = [0,1,2,3,4]
>>> b = iter(a)
>>> b
<listiterator object at 0x106984a50>
>>> b.next()
0
>>> b.next()
1
# ...
>>> b.next()
4
>>> b.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
a라는 공간에 list를 생성하였고, a에는 0,1,2,3,4가 순차적으로 저장되어있다. b는 iter() 함수의 인자로 a를 주었다. 이제 b는 메모리 주소 0x106984a50
안에 위치한다.
- iterator : b
- iteration : iterator에서 순차적으로 요소를 가져오는 행위
- iterable : iteration이 가능한 경우
list는 iterable한 객체이다. 이에 대해서 iter(b)
로 해당 객체의 iterator를 얻어올 수 있다. 그 후 이 iterator의 next()
를 호출해서 iteration이 가능하다. next()
를 호출 시에 더이상 줄 값이 없다면 StopIteration
Exception이 나게된다.
>>> a = [0,1,2,3,4]
>>> for i in a:
... print i,
...
0 1 2 3 4
위의 코드는 이렇게도 쓸 수 있다.
>>> a = [0,1,2,3,4]
>>> b = iter(a)
>>>
>>> while True:
... try:
... i = b.next()
... except StopIteration:
... break
... print i,
...
0 1 2 3 4
Generator
이것도 먼저 예제를 보자!
>>> def num_generator(n):
... print "Function Start"
... while n < 6:
... yield n
... n += 1
... print "Function End"
...
>>> for i in num_generator(0):
... print i
...
Function Start
0
1
2
3
4
5
Function End
Generator는 함수의 형태를 가진다. 일반적으로 for문과 같이 사용된다. yield를 사용하는 함수 == generator라고 봐도 무방하다.
이거 언제쓰냐?
List Comprehension 구문은 python에서 자주 쓰이는 방법이다.
a = [0,1,2,3,4]
b = [i**2 for i in a]
for j in b:
...
위의 코드를 보면 a를 이용해 list b를 만들고 이를 이용하여 어떤 조작을 하려고 한다. a, b가 모두 메모리를 차지한다. 작은 리스트는 괜찮지만 a가 엄청 많은 데이터를 가지고있을 때 비슷한 사이즈의 메모리를 차지하는 것은 별로 효율적이지 못하다. 이 때 generator를 쓰면 list a만 유지하며 각 iteration에 맞는 값을 제공할 수 있다.
- 장점
- 단점?
- b가 앞으로도 계속 쓰일 것이라면 어떤 것이 비용이 더 클까? (나중에 조사해서 쓰자)
>>> a = [0,1,2,3,4]
>>>
>>> def genTest(l):
... for i in a:
... yield i**2
...
>>> for j in genTest(a):
... print j,
...
0 1 4 9 16
yield?
python에서 yield란 return과 비슷하나 좀 다르다. return처럼 yield를 만나는 순간 값을 리턴하나, 함수가 종료되지 않고 다음 호출 시 거기서부터 시작된다!
이 예제 좋구만
>>> def generator(n):
... print "Generator start!"
... while n < 6:
... print "Generator : I give control to the Main"
... yield n
... print "Generator : I received a control."
... n += 1
... print "Generator : n += 1"
... print "Generator End!"
...
>>> for i in generator(0):
... print "Main: I received a control."
... print i
... print "Main: I give control to Generator"
...
Generator start!
Generator : I give control to the Main
Main: I received a control.
0
Main: I give control to Generator
Generator : I received a control.
Generator : n += 1
Generator : I give control to the Main
Main: I received a control.
1
.........
5
Main: I give control to Generator
Generator : I received a control.
Generator : n += 1
Generator End!
for문
을 맨 위에서 봤던 동치 while문
으로 바꿔 생각하면 쉽게 이해할 수 있다.
while문에서 b.next()
를 호출할 때 generator가 호출된다
08 Aug 2016
|
python
coroutine
Coroutine 정의
프로그램에서 대등한 관계로 서로 호출하는 것. 예를 들면, 게임 프로그램에서 각 플레이어의 루틴은 서로 코루틴된다.
종속적이지 않고 서로 대화하는 형식의 함수
다음 그래프는 동작 방식을 sequence로 설명한 그림이다.
Coroutine을 직접 만들어보자!
# coding: utf-8
import random
import sys
reload(sys)
sys.setdefaultencoding('utf8')
def game(name):
print "안녕 " + name
print "조건 : 0 < num < 101"
count = 0
num = random.randrange(1,101)
while True:
OoC = (yield)
count += 1
if OoC < 0 or OoC > 100:
print "잘못된 숫자!"
elif OoC > num:
print "더 작은 숫자야."
elif OoC < num:
print "더 큰 숫자야."
else:
print "%d번 만에 맞췄네!" %count
OoC = (yield)
: 요걸로 입력을 받을 때 까지 대기한다.
>>> from coroutine import game
>>> c = game("Hulk")
>>> c.next()
안녕 Hulk
조건 : 0 < num < 101
>>> c.send(50)
더 작은 숫자야.
>>> c.send(1)
더 큰 숫자야.
>>> c.send(20)
더 작은 숫자야.
>>> c.send(10)
더 큰 숫자야.
>>> c.send(15)
더 큰 숫자야.
>>> c.send(17)
더 작은 숫자야.
>>> c.send(16)
7번 만에 맞췄네!
>>> c.close()
c.next()
: 코루틴을 호출한 직후 next()
를 사용하여 첫 yield까지 동작시켜야 한다.
c.send()
: send로 데이터를 보내서 yield 뒤를 실행시켜야 함.
c.close()
: 코루틴을 종료시키려면 close()함수를 사용한다.
01 Aug 2016
|
python
repr
dict
Call By Reference!
** Python 함수에서 모든 argument는 call by reference로 불린다. **
다만 mutable object이냐, immutable object이냐에 따라서 call by value처럼 보이는 것 뿐이다.
Immutable type
def foo( a ):
a = 2
b = 3
foo( b )
print b # 3
위의 코드처럼 immutable한 경우는 call by value처럼 동작한다.
|0x0001|
주소에 3이라는 값이 있었으면 처음에
b |0x0001|
, a |0x0001|
가 된다.
이제 a = 2를 하면 |0x0002|
에 2라는 값이 생기고 a는 |0x0002|
의 주소를 가리키게된다.
따라서 함수 밖에 나왔을 때 b에게는 영향이 없는 것이다.
다음 코드르 보면 확실히 알 수 있을 것이다.
def foo( a ):
print "Before operation, id(a) is ", id( a )
a = 3
print "After operation, id(a) is ", id( a )
b = 2
print "Before calling foo, id(b) is ", id( b )
foo( b )
print "After calling foo, id(b) is ", id( b )
Mutable type
def foo( a ):
a.append( 2 )
b = [ 3,2,1]
foo(b)
print b # [3, 2, 1, 2]
List의 경우 mutable한 녀석이며 위의 foo는 list에 append를 하기 때문에 a와 b의 주소가 바뀌지 않으며 따라서 call by reference로 볼 수 있다.
Conclusion!
결국, object의 reference를 바꾸지 않는 operation의 경우 call by reference이며, reference를 바꾸면 call by value!