Python_3 - jjin-choi/study_note GitHub Wiki
Β§ Meta Programming
1. ν¨μ κ°μΈκΈ° (λ°μ½λ μ΄ν°)
-
ν¨μμ μΆκ°μ μΈ μ²λ¦¬ (λ‘κΉ , νμ΄λ° λ±)μ νλ λ νΌ λ μ΄μ΄λ₯Ό λ£κ³ μΆμ κ²½μ°
-
meta data : ν¨μμ λΆκ°μ μΈ μ 보λ€
- ν¨μμ μ΄λ¦, argument, doc λ¬Έμμ΄, annotation (μΈμμ μ£Όμ) λ±
- μ¬λ¬ μ€ μ£Όμ μ²λ¦¬λ ''' ''' μ μ΄μ©νλ©΄ λκ³ μ΄λ₯Ό doc λ¬Έμμ΄ μ΄λΌκ³ νλ€.
-
λ¨μν ν¨μμ decorator λ₯Ό μμ°λ©΄ ν¨μμ meta μ λ³΄κ° μ¬λΌμ§κ² λλ€.
- μ΄λ₯Ό λ§κΈ° μν΄ @wraps(func) μ μΆκ°ν΄μ£Όμλ€.
# ------------- #
# version 1 #
# ------------- #
import time
from functools import wraps
def timethis(func):
# wrapper = wraps(func, wrapper) λ‘ λμ΄κ° κ²κ³Ό κ°λ€
# μ΄ μ½λμμλ wraps(countdown)(wrapper) λ₯Ό λ겨주λ κ²κ³Ό κ°λ€.
# wraps(countdown) μμ meta data λ₯Ό λ½μμ λ€μνλ² λ°μ½λ μ΄ν°λ₯Ό μμ°λ κ²
@wraps(func) # μλ‘ μΆκ°λ λΆλΆ (ν¨μν λ°μ½λ μ΄ν°)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
if __name__ == '__main__':
# μλ μ½λλ μ¬μ€ μ΄λ° μλ―Έκ° λλ€.
# countdown = timethis(countdown)
# λ°λΌμ ν¨μμ μΈμλ‘ λ€μ΄κ° countdown κ³Ό ν¨μ return μΌλ‘ κ°μ κ°κ² λ countdown μ μ ν λ€λ₯Έ μ‘΄μ¬
@timethis
def countdown(n:int):
while n > 0:
n -= 1
countdown(100000)
countdown(10000000)
# ------------- #
# version 2 #
# ------------- #
import time
from functools import wraps
def timethis(func):
'''
Decorator that reports the execution time.
'''
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(func.__name__, end-start)
return result
return wrapper
if __name__ == '__main__':
@timethis
def countdown(n:int):
'''
Counts down
'''
while n > 0:
n -= 1
countdown(100000)
print('Name:', countdown.__name__)
print('Docstring:', repr(countdown.__doc__))
print('Annotations:', countdown.__annotations__)
2. λ°μ½λ μ΄ν° νκΈ°
- ν¨μμ λ°μ½λ μ΄ν°λ₯Ό μ μ©νλλ°, μ΄λ₯Ό μ·¨μνκ³ μλ³Έ ν¨μμ μ κ·Όνκ³ μΆλ€.
- @wraps λ₯Ό μ¬μ©ν΄μ λ°μ½λ μ΄ν°λ₯Ό μ¬λ°λ₯΄κ² ꡬννλ€κ³ κ°μ νλ©΄, μλ³Έν¨μμλ wrapped μμ±μΌλ‘ μ κ·Όν μ μλ€.
from functools import wraps
def decorator1(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Decorator 1')
return func(*args, **kwargs)
return wrapper
def decorator2(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Decorator 2')
return func(*args, **kwargs)
return wrapper
@decorator1
@decorator2
def add(x, y):
return x + y
# add = decorator2(add)
# add = decorator1(add)
# νμ§λ§ decorator1 λΆν° νΈμΆλ¨
print(add(2,3))
print(add.__wrapped__(2,3))
# print(add.__wrapped__.__wrapped__(2,3))
# λ λ² decorator λ₯Ό λ²κ²¨λ΄μ μλ³Έ ν¨μλ₯Ό μ κ·Όν μ μμκΉ? YES !
-
wrapped λ decorator λ₯Ό μμ°λ©΄ μκΈ°λ©° νλ² νΈλ κ²κ³Ό κ°λ€.
- λ°λΌμ decorator1 μ΄ νλ €μ κ·Έ μμ decorator2 λΆν° μνλκ² λλ€.
- λ¨ wrapped λ μλ³Έ ν¨μμ ν¨μ κ°μ²΄λ₯Ό μ μ₯νκΈ° μν΄μ wraps(func) μ΄ νμνλ€.
-
log level μ λ°λΌμ λͺ¨λμ λ°λΌ logλ₯Ό λ¨κΈΈ μ μλλ‘
- @logged(logging.DEBUG) μλ―Έλ, add = logged(logging.DEBUG)(add) μ λμΌνλ€
- log level, module name, ν¨μλͺ μΌλ‘ λ¨κΈΈ μ μλ μ½λ
- ν¨μ λ³λ‘ λ¨κ³λ₯Ό λ€λ₯΄κ² ν μ μμ
from functools import wraps
import logging
def logged(level, name=None, message=None):
def decorate(func):
logname = name if name else func.__module__
log = logging.getLogger(logname)
logmsg = message if message else func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
log.log(level, logmsg)
return func(*args, **kwargs)
return wrapper
return decorate
@logged(logging.DEBUG)
def add(x, y):
return x + y
@logged(logging.CRITICAL, 'example')
def spam():
print('Spam!')
if __name__ == '__main__':
import logging
logging.basicConfig(level=logging.DEBUG)
print(add(2,3))
spam()
3. μ¬μ©μκ° μ‘°μ κ°λ₯ν μμ±μ κ°μ§ λ°μ½λ μ΄ν°
-
ν¨μλ₯Ό κ°μΈλ λ°μ½λ μ΄ν° ν¨μλ₯Ό μμ±νλλ°, μ¬μ©μκ° μ€ν μκ°μ λμμ±μ λ³κ²½ν μ μλλ‘ μμ±μ μ‘°μ νκ³ μΆμ κ²½μ°
- μ¦, log level μ΄ DEBUG level λ‘ μ ν΄μ§ κ²μ λμ μΌλ‘ λ°κΏ μ μλκ°.
- ν¨μ μ΄λ¦ μ΄λ λͺ¨λ μ΄λ¦ λ±μ λμ μΌλ‘ λ°κΎΈκΈ°
-
nonlocal level μ΄λΌκ³ μ€μ ν΄μ£Όλ©΄ νλ² μ§μ λ level μ λ³κ²½ ν μ μμ.
- @attach_wrapper μμ set_level μ wrapper ν΄μ£Όλλ°, set_level = attach_Wrapper(wrapper)(set_level)
-
partial ? μ΄ λκΉ
- ν¨μ μΈμ μ€μ νΉμ νλλ₯Ό μ΄λ€ κ°μΌλ‘ κ³ μ νκ³ μΆμλ μ¬μ©νλ€.
- μΈμκ° μ¬λ¬κ°μ΄μ¬λ μ μ κ° μ¬μ©ν λμλ νΉμ μΈμλ§ μ¬μ©ν μ μλ κΈ°λ₯
- attach_wrapper μμμ 첫λ²μ§Έ μΈμλ wrapper μ΄λΌκ³ κ³ μ μν¨ κ².
5. λ°μ½λ μ΄ν°λ₯Ό μ¬μ©ν΄μ ν¨μμμ νμ νμΈ
-
ν¨μμ 맀κ°λ³μ νμ μ κ°μ μ μΌλ‘ νμΈνλ κΈ°λ₯
-
λͺ¨λ 맀κ°λ³μμ λν΄ νμ μ λͺ μνκ±°λ νΉμ λΆλΆλ§ μ§μ ν μ μμ΄ λ°μ½λ μ΄ν°κ° μ΄λ μ λ μ μ°ν¨
-
signature : ν¨μμμ λκΈ΄ μ€μ μΈμλ₯Ό λ½μμ€λ κ². κ·Έ νμ μ λν΄μλ λ½μμ€μ§ μμ.
- sig = signature(func) ν ν sig.bind_partial(...) μ νκ² λλ©΄ typeκ³Ό valueλ₯Ό listλ‘ μλ‘ λ¬Άμ΄μ€λ€.
- closure μ μν΄μ μλ λμ΄μμΌ ν type μ΄ μ μ₯λμ΄ μμΌλ©΄ type μ 체ν¬ν μ μμ
6. λ°μ½λ μ΄ν°λ₯Ό ν΄λμ€μ μΌλΆλ‘ μ μ
-
ν΄λμ€ λ΄λΆ methodλ 2μ’ λ₯.
- instance method : κ°μ²΄μ μ£Όμκ° λμ΄κ°
- class method : class method λΌλ λ°μ½λ μ΄ν°
- wraps (μλ³Έ ν¨μμ λ°μ΄ν° μ μ₯μ μν΄μ κ°μ₯ λ¨Όμ μμμ€λ€.)
-
class Person : first_name = property() λ‘ μ¬μ© κ°λ₯
- λ΄μ₯ λ°μ½λ μ΄ν° @property λ μ€μ λ‘ getter() setter() delete() λ©μλλ₯Ό κ°μ§ ν΄λμ€μ΄κ³ λ°μ½λ μ΄ν°μ²λΌ λμλλ€.
-
ν΄λμ€λ₯Ό μ 체 λ°μ½λ μ΄ν°λ‘ μ¬μ© κ°λ₯. ν¨μλ₯Ό λ°μ½λ μ΄ν°λ‘ κ°μΈκ³ μΆμ§λ§ κ·Έ κ²°κ³Όλ νΈμΆ κ°λ₯ μΈμ€ν΄μ€κ° λλλ‘ νκ³ μΆμ κ²½μ°. κ·Έλ¦¬κ³ λ°μ½λ μ΄ν°λ₯Ό ν΄λμ€ μ μ λ΄λΆ/μΈλΆμμ λͺ¨λ μ¬μ©νκ³ μΆλ€.
- call κ³Ό get μ μ¬μ μ ν΄μ£Όμ΄μΌ ν¨.
-
μλμ κ°μ μμμμ κ°μ²΄ add μ κ΄νΈλ₯Ό λ£μ΄ κ°μ λ£μ΄μ£Όλ©΄, κ°μ²΄μ κ°μ λ£κΈ° λλ¬Έμ μλ¬κ° λ°μνλ€. μ¬μ€ μ΄ κ²μ add.call() μ λμΌνκΈ° λλ¬Έμ __call__μ μ μν΄μ£Όλ©΄ λ¬Έμ κ° μμ
@Profiled
def add(x, y):
return x + y
# add = Profiled(Add)
# add(2, 3) -> add.__call__(2, 3)
- λν ν΄λμ€μ λ©€λ²μ λ°μ½λ μ΄ν°λ₯Ό μμμ£Όλ μν©μμλ get μ΄ νμνλ€.
- ν΄λμ€ κ°μ²΄ μμ κ°μ κ°μ ΈμμΌ νκΈ° λλ¬Έμ
def __get__(self, instance, cls):
if instance is None:
return self
else:
return types.MethodType(self, instance)
# instance κ° λμ΄μ€λ κ²½μ°μ λμ΄μ€μ§ μλ κ²½μ°κ° μμ.
# λμ΄μ€μ§ μλ κ²½μ°λ λ°μ½λ μ΄ν°μ μ
νλ₯Ό λκΈ°κΈ° λλ¬Έμ, instanceκ° λμ΄κ°μ§ μμ.
...
class Spam:
@Profiled
def bar(self, x):
print (self, x)
if __name__ = '__main__':
s = Spam()
s.bar(1)
s.bar(2)
s.bar(3)
# s ν΄λμ€μ μΈμ€ν΄μ€ λ©€λ² λ΄λΆ ν¨μλ₯Ό κ°μ Έμ€κΈ° μν΄ get λ νμνκ³ call λ νμνλ€.
print ('ncalls:', Spam.bar.ncalls)
# μ΄ κ²½μ°κ° instance κ° None μΈ κ²½μ°μ ν΄λΉ. λ°μ½λ μ΄ν° @Profiled μ ncallss μ μ κ·Ό.
7. ν΄λμ€μ static method μ λ°μ½λ μ΄ν° μ μ©
- ν΄λμ€λ static method μ λ°μ½λ μ΄ν°λ₯Ό μ μ©νκ³ μΆμ κ²½μ°
- λ°μ½λ μ΄ν°λ₯Ό @classmethod λλ @staticmethod μ (μ¦ μλ)μ μ μ©νλλ‘ μ£Όμνμ
8. μΈμ€ν΄μ€ μμ± μ‘°μ μ λ©νν΄λμ€ μ¬μ©
- meta-class : μ΄λ€ ν΄λμ€κ° typeμ μμλ°μΌλ©΄, κ·Έκ²μ meta-class λΌκ³ νλ€.
- class λ₯Ό λ§λλ class
- μΌλ°μ μΈ class μμ call μ μ¬μ μ ν λμ meta-class μμ μ¬μ μ ν λλ μ ν λ€λ₯΄λ€.
- class λ₯Ό λ§λ€ λ λͺ¨λ κ·μΉμ΄ meta-class μμ λ€μ΄κ°λ€.
- meta-class μ call μ class κ° μλ‘μ΄ κ°μ²΄λ₯Ό λ§λ€ λ λμ (s = Class())
- λ°λΌμ ννΉμ΄ κ°λ₯νκΈ° λλ¬Έμ, μ±κΈν€, μΊμ± λ± κΈ°λ₯ ꡬνμ μν΄ μΈμ€ν΄μ€κ° μμ±λλ λ°©λ²μ λ³κ²½νκ³ μΆμ λ
- μ±κΈν€ : κ°μ²΄λ₯Ό νλλ§ μ‘΄μ¬νλλ‘ !
class NoInstances(type):
# μλλ ν΄λμ€λ₯Ό λͺ»λ§λ€κ² νκ³
# κ°μ²΄ μμ΄ class method μ static method λ§ μ¬μ©νκ² νκΈ° μν μ½λ
# class κ° κ°μ²΄λ₯Ό μμ±νλ €κ³ νλ μμ μ νΈμΆ
def __call__(self, *args, **kwargs):
raise TypeError("Can't instantiate directly")
class Spam(metaclass=NoInstances):
@staticmethod
def grok(x):
print('Spam.grok')
if __name__ == '__main__':
try:
s = Spam()
except TypeError as e:
print(e)
Spam.grok(42)
- meta-class μ init μ class μμ²΄κ° μμ±λ λ νΈμΆλκ³ class λ΄λΆμ init μ κ°μ²΄κ° μμ±λκ³ μ΄κΈ°ν ν λ νΈμΆλλ€.
- μμμμΌλ‘ 보면, class μμ²΄κ° μμ±λ λ meta-class μ init
- β ν΄λμ€κ° () λ₯Ό λ§λ¬μ λ meta-class μ call
- β μΈμ€ν΄μ€κ° λ€ λ§λ€μ΄μ§κ³ μ΄κΈ°ν λ λ class λ΄λΆμ init
- meta-class λ class μμ±κ³Ό κ΄λ ¨λμ΄ μκ³ , class λ΄λΆλ μΈμ€ν΄μ€ μμ±κ³Ό κ΄λ ¨μ΄ μμ
class Singleton(type):
# class κ° μμ±λ λ νΈμΆ
def __init__(self, *args, **kwargs):
self.__instance = None
super().__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
# class κ° μΈμ€ν΄μ€λ₯Ό μμ±νλ €κ³ ν () κ° μμ λ νΈμΆ
if self.__instance is None:
self.__instance = super().__call__(*args, **kwargs)
return self.__instance
else:
return self.__instance
class Spam(metaclass=Singleton):
# class μ κ°μ²΄κ° λ€ λ§λ€μ΄μ§κ³ κ·Έ μΈμ€ν΄μ€κ° μ΄κΈ°ν λλ μμ μ νΈμΆ
def __init__(self):
print('Creating Spam')
if __name__ == '__main__':
a = Spam()
b = Spam()
print(a is b)
10. ν΄λμ€ μμ± μ μ μμ μμ§
- meta class λ₯Ό μμλ°μ μλ meta class κ° λλ€.
- meta class μ init μ class κ° μμ±λ λ μμ±. *__call__μ calling μ ν λ μμ±
- init μ μ΄κΈ°ν, __new__λ κ°μ²΄ (ν΄λμ€) κ° λ©λͺ¨λ¦¬μ μμ±λ λ.
- μ¦, new β init
- prepare : class μ λν μ 보, μμ μ 보μ λν κ²λ€. μ΄κ² __new__λ₯Ό νΈμΆ
- μ¦, prepare β new β init (10. 03:41 λΆν° λ£κΈ°)
μ°Έκ³
- μ λλ μ΄ν° (generator) : iterator λ₯Ό μμ±ν΄μ£Όλ ν¨μ
- iterator λ class μ iter, next λλ getitem λ©μλλ₯Ό ꡬνν΄μΌ νμ§λ§, generator λ ν¨μ μμ yield ν€μλλ§ μ¬μ©νλ©΄ λ¨.
- iterator λ next method μμ μ§μ return λ°νν΄μΌ νμ§λ§ generator μ yield μ§μ ν κ°μ΄ next λ°νκ°μΌλ‘ λμ΄
- iterator λ StopIteration μμΈλ₯Ό μ§μ λ°μμν€μ§λ§ generator λ ν¨μ λκΉμ§ λλ¬νλ©΄ StopIteration μμΈκ° μλμΌλ‘ λ°μ.
- λμμ iterator μ λμΌ
- μ°Έκ³ λ‘ μ λλ μ΄ν° κ°μ²΄μμ __iter__λ₯Ό νΈμΆνλ©΄ selfλ₯Ό λ°ννλ―λ‘ κ°μ κ°μ²΄κ° λμ΄
- (μ λλ μ΄ν° ν¨μ νΈμΆ > μ λλ μ΄ν° κ°μ²΄ > __iter__λ self λ°ν > μ λλ μ΄ν° κ°μ²΄).
- κ·Έλ°λ° generateλΌλ ν€μλλ₯Ό μ¬μ©νλ©΄ λμ§ μ yieldλΌκ³ μ΄λ¦μ μ§μμκΉ?
- yieldλ μμ°νλ€λΌλ λ»κ³Ό ν¨κ» μ보νλ€λΌλ λ»λ κ°μ§κ³ μμ΄, yieldλ₯Ό μ¬μ©νλ©΄ κ°μ ν¨μ λ°κΉ₯μΌλ‘ μ λ¬νλ©΄μ μ½λ μ€νμ ν¨μ λ°κΉ₯μ μ보.
- λ°λΌμ yieldλ νμ¬ ν¨μλ₯Ό μ μ μ€λ¨νκ³ ν¨μ λ°κΉ₯μ μ½λκ° μ€νλλλ‘ λ§λλλ€.
- λ©μλ λͺ©λ‘μ dir(ν¨μμ΄λ¦) μΌλ‘ νμΈν μ μμ.
- generator μμμ return ν¨μλ₯Ό μ¬μ©νλ©΄ StopIteration μμΈ λ©μμ§λ‘ return λ°νκ°μ΄ μ§μ λ¨