# Neseted function (Support both function and methods)
# This is just a common pattern, not the only way
def decorator(fun):
def wrapper(*args):
print("[Args]: ", args)
return fun(*args)
return wrapper
# Function decorator
@decorator
def func(x, y):
pass
func(6, 7)
class C:
@decorator
def method(self, x, y):
pass
c = C()
c.method(1, 2)
[Args]: (6, 7) [Args]: (<__main__.C object at 0x1043b1a58>, 1, 2)
__getattr__
based tracing wrapper will not trace and propagate operator overloading calls for built-ins in Python3@decorator
class C:
pass
c = C(1)
class C:
pass
C = decorator(C)
c = C(1)
Just like function decorators, though some may involve two levels of augmentation
def decorator(cls):
class Wrapper:
def __init__(self, *args):
self.wrapped = cls(*args)
self.calls = 0
def __getattr__(self, name):
self.calls += 1
print("[name]: ", name)
print("[calls]: ", self.calls)
return getattr(self.wrapped, name)
return Wrapper
@decorator
class C:
def __init__(self, x, y):
self.attr = "spam"
c1 = C(6, 7)
print(c1.attr)
c2 = C(7, 8)
print(c2.attr)
[name]: attr [calls]: 1 spam [name]: attr [calls]: 1 spam
class Decorator:
def __init__(self, C): # On @decoration
self.C = C
self.calls = 0
def __call__(self, *args): # On instance creation
self.calls += 1
print("[Args]: ", args)
print("[Calls]: ", self.calls)
self.wrapped = self.C(*args)
return self
def __getattr__(self, attrname):
return getattr(self.wrapped, attrname)
@Decorator
class C:
def __init__(*args):
pass
c1 = C(1)
c2 = C(2)
[Args]: (1,) [Calls]: 1 [Args]: (2,) [Calls]: 2
def singleton(cls):
instance = None
def on_call(*args, **kwargs):
nonlocal instance
if not instance:
instance = cls(*args, **kwargs)
return instance
return on_call
@singleton
class C:
def __init__(self, data):
self.data = data
def display(self):
print(self.data)
c1 = C(1)
c1.display()
# Cannot create a new one
c2 = C(2)
c2.display()
1 1
@decorator(x, y)
def fun(arg):
pass
fun(1)
def fun(arg):
pass
fun = decorator(x, y)(fun)
fun(1)
def decorator(x, y):
# save or use x, y
def actualDecorator(func):
# save or use function func
# return a callable: nested def, class with __call__ etc.
return callable
return actualDecorator
(May be used to implement a decorator)
def func(a, b, c=1, d=2):
x = 3
y = 4
code = func.__code__
code.co_nlocals
6
code.co_varnames
('a', 'b', 'c', 'd', 'x', 'y')
code.co_varnames[: code.co_argcount]
('a', 'b', 'c', 'd')