class decorator:
class tracer0:
def __init__(self, func):
self.calls = 0
self.func = func
def __call__(self, *args):
self.calls += 1
print("call %s to %s" % (self.calls, self.func.__name__))
return self.func(*args)
function decorator:
1. use function attribute
def tracer(func):
def oncall(*args):
oncall.calls += 1
print('call %s to %s' % (oncall.calls, func.__name__))
return func(*args)
oncall.calls = 0
return oncall
- use
nonlocal
def tracer1(func):
count = 0
def __decorator(*args, **kargs):
nonlocal count
count += 1
print('call %s to %s' % (count, func.__name__))
return func(*args, **kargs)
return __decorator
class decorator could act like a proxy that could intercept attribute access to wrapped class.
def class_wrapper(cls):
class Proxy:
def __init__(self, *args):
self.__wrapped = cls(*args)
def __getattr__(self, name):
return getattr(self.__wrapped, name)
return Proxy
@class_wrapper
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
p1 = Person('alice')
print(p1)
print(p1.__class__)
python2 result:
alice
__main__.Proxy
python3 result:
<__main__.class_wrapper.<locals>.Proxy object at 0x7f6e501a03c8>
<class '__main__.class_wrapper.<locals>.Proxy'>
Why, because implicit attribute fetch starts at class level in python3.x while at instance level in python2.x.
One thing to be clear is __getattr__
, from python doc:
object.__getattr__(self, name)
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance
attribute nor is it found in the class tree for self). name is the attribute name. This method should return
the (computed) attribute value or raise an AttributeError exception.
__getattr__
only be called under two conditions:
1. when fetch attribute in an instance
2. the attribute cannot be found(in instance and in MRO)