원문 : http://pypix.com/python/advanced-data-structures/
List 에 대한 이해
다양한 숫자가 저장된 list가 있고 이 list 에서 0보다 큰 수의 제곱값을 가지는 새로운 list를 만들려고 할때 아래와 같이 코딩하곤 한다.
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = []
for number in num:
if number > 0:
filtered_and_squared.append(number ** 2)
print filtered_and_squared
# [1, 16, 100, 4, 9]
4, 5 행을 보면 for, if 문이 중첩되어 사용된다. 이 부부을 filter, lamda, map을 사용하여 아래와 같이 확 줄일 수 있다.
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = map(lambda x: x ** 2, filter(lambda x: x > 0, num))
print filtered_and_squared
# [1, 16, 100, 4, 9]
이 코드를 조금 더 줄일 수 있다.
num = [1, 4, -5, 10, -7, 2, 3, -1]
filtered_and_squared = [ x**2 for x in num if x > 0]
print filtered_and_squared
# [1, 16, 100, 4, 9]
출처 : http://pypix.com/python/advanced-data-structures/
위 방법은 좋지만 전체 리스트를 메모리에 한번 올려야 한다는 단점이 있다.(라고 한다. 왜 그런지는 잘 모르겠음) 리스트가 작으면 크게 상관 없는데 리스트다 크다면 문제가 될 수 있다고 함. 그래서 나온게 Generator!!!
Generators 는 리스트 전체를 메모리에 올리지 않고 "generator 객체(object)"를 생성함. 그래서 필요할때 원본 리스트를 메모리에 올려서 원하는 결과를 얻을 수 있다고 함
예)
num = [1, 4, -5, 10, -7, 2, 3, -1]
def square_generator(optional_parameter):
return (x ** 2 for x in num if x > optional_parameter)
print square_generator(0)
# Option I
for k in square_generator(0):
print k
# 1, 16, 100, 4, 9
# Option II
g = list(square_generator(0))
print g
# [1, 16, 100, 4, 9]
Decorators는 함수(function)나 클래스에 유용한 기능을 추가 할 수 있게 한다. 함수가 실행되기 전이나 실행된 이후 동작하는 기능을 정의 할 수 있다. (함수를 감싸고 있는 함수라고 이해하면 된다. 보안, 추적, locking에 유용하겠다.)
예) 함수 실행 시간을 측정하는 데코레이터 함수를 만들어 보자
import time from functools
import wraps
def timethis(func):
@wraps(func)
def wrapper(*args, **kwargs):
print "wrapper start!!"
start = time.time()
#result = func(*args, **kwargs)
func(*args, **kwargs)
end = time.time()
print (func.__name__, end - start)
#return result
return wrapper
@timethis
def countdown(n):
print "function start!!"
while n > 0:
n -= 1
print "function end!!"
#wrapper start!!
#function start!!
#function end!! #('countdown', 0.0559999942779541)
위 예제에서 보는바와 같이
timethis 함수는 func 라는 이름으로 함수를 받아서 실행시키고 실행된 시간을 측정해서 출력하는 함수로 구성했고
이를 decorator를 이용해 countdown이라는 함수를 감쌌다
이번엔 decorator 클래스를 만들어보자
class decorator(object): def __init__(self, f): print("inside decorator.__init__()") f() # Prove that function definition has completed def __call__(self): print("inside decorator.__call__()") print "start!!!" @decorator def function(): print("inside function()") print("Finished decorating function()") function() # start!!! # inside decorator.__init__() # inside function() # Finished decorating function() # inside decorator.__call__()
예제에서 알 수 있듯이
1. @decorator 를 만나면 decorator 클래스의 "__init__" 을 실행한다.
2. 함수를 실행시키면 정의된 decorator 클래스의 "__call__" 함수를 실행한다.
마지막 예제 하나를 더 보자
def decorator(func):
def modify(*args, **kwargs):
variable = kwargs.pop('variable', None)
print variable
func(*args, **kwargs)
return modify
@decorator
def func(a,b):
print a**2,b**2
func(a=4, b=5, variable="hi") func(a=4, b=5)
# hi
# 16 25
# None
# 16 25
함수에는 정의되지 않은 변수라도 이런식으로 다룰 수 있다.
ContextLib는 context manager와 with문과 관련된 유용한 기능들을 제공한다. context manager를 작성하기 위해서는 __enter__()함수와 __exit__()함수가 포함된 클래스를 정의 해야 한다.
일단 예를 살펴 보자
import time class demo:
def __init__(self, label):
self.label = label
def __enter__(self):
self.start = time.time()
def __exit__(self, exc_ty, exc_val, exc_tb):
end = time.time()
print('{}: {}'.format(self.label, end - self.start))
with demo('counting'):
n = 10000000
while n > 0:
n -= 1
# counting: 1.36000013351
context manager는 with 문으로 사용할 수 있다. __enter__() 함수는 with 문 안의 코드가 실행될 때 실행되는 함수이며, __exit__()함수는 with 문 안의 모든 코드가 실행되고 관련된 모든 자원과 메모리가 해제 되는 시점에 실행 된다.
위 예제를 contextlib 모듈에 정의된 "@contextmanager" decorator 를 이용해서 다시 작성해 보자.
from contextlib
import contextmanager
import time
@contextmanager
def demo(label):
start = time.time()
try:
yield
finally:
end = time.time()
print('{}: {}'.format(label, end - start))
with demo('counting'):
n = 10000000
while n > 0:
n -= 1
# counting: 1.32399988174
이전 코드와 비교해보면 쉽게 알 수 있겠지만
"yield" 이전에는 __enter__() 함수에 들어갈 내용
"yield" 이후에는 __exit__() 함수에 들어갈 내용 을 작성하면 된다.
Descriptors는 어떤 변수나 함수에 접근할때 동작한다. __get__(), __set__(), __delete__() 함수로 각 이벤트별 동작을 정의 할 수 있다.
__get__(self, instance, owner) - 변수나 함수에 접근할때
__set__(self, instance, value) - 변수나 함수를 변경 할 때
__delete__(self, instance) - 변수나 함수를 삭제 할때
instance 는 descriptor 가 걸려있는 변수나 함수가 포함된 인스턴스를 말한다.
owner는 변수나 함수가 포함된 클래스를 말한다.
value는 변경되는 값을 말한다.
예)
import types
class descriptor(object):
def __init__(self, attr):
self.attr = attr
def __get__(self, obj, cls):
print "==== __get__() ===="
if isinstance(self.attr, types.FunctionType):
return self.attr(obj)
else:
return self.attr
def __set__(self, obj, value):
print "==== __set__() ===="
if not isinstance(self.attr, types.FunctionType):
print "new Value is %s but not changed!!" % value
class Foo(object):
bar = descriptor(100)
@descriptor
def bar2(self):
return 42
f = Foo()
print f.bar
#==== __get__() ====
#100 f.bar = 300
#==== __set__() ====
#new Value is 300 but not changed!!
print f.bar
#==== __get__() ====
#100
print f.bar2
#==== __get__() ====
#42
어떤 변수나 함수에 접근하고, 변경하는것을 제어하거나 이벤트를 걸고 싶을때 사용하면 좋을것 같다.
'Python > Python' 카테고리의 다른 글
Python - lambda() (0) | 2013.12.20 |
---|---|
Python - zip() (0) | 2013.12.20 |
Python 어렵게 배우기 (0) | 2013.08.23 |
Python - @staticmethod, @classmethod의 사용 (0) | 2013.08.12 |
Python - UTF-8 인코딩 (0) | 2013.05.02 |