Metaclass 使用两种不同的装饰器实现来装饰所有 class 方法
Metaclass which decorate all the class methods using two different decorator implementation
我在这个 metaclass 装饰器上应用装饰器的实现有问题,我写道:
def decorateAll(decorator):
class MetaClassDecorator(type):
def __new__(meta, classname, supers, classdict):
for name, elem in classdict.items():
if type(elem) is FunctionType:
classdict[name] = decorator(classdict[name])
return type.__new__(meta, classname, supers, classdict)
return MetaClassDecorator
这是我在其中使用元class:
的class
class Account(object, metaclass=decorateAll(Counter)):
def __init__(self, initial_amount):
self.amount = initial_amount
def withdraw(self, towithdraw):
self.amount -= towithdraw
def deposit(self, todeposit):
self.amount += todeposit
def balance(self):
return self.amount
当我传递给装饰器 metaclass 一个像这样实现的装饰器时,一切似乎都很好:
def Counter(fun):
fun.count = 0
def wrapper(*args):
fun.count += 1
print("{0} Executed {1} times".format(fun.__name__, fun.count))
return fun(*args)
return wrapper
但是当我使用以这种方式实现的装饰器时:
class Counter():
def __init__(self, fun):
self.fun = fun
self.count = 0
def __call__(self, *args, **kwargs):
print("args:", self, *args, **kwargs)
self.count += 1
print("{0} Executed {1} times".format(self.fun.__name__, self.count))
return self.fun(*args, **kwargs)
我收到这个错误:
line 32, in __call__
return self.fun(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'initial_amount'
为什么?将两个装饰器实现与其他函数一起使用不会给我带来问题。我认为问题与我试图装饰的方法是 class 方法这一事实有关。我错过了什么吗?
您需要将 Counter
实现为可调用描述符。在描述符上执行 __get__
时,您模拟将描述符绑定到传递给它的实例。加上按 method/object 存储计数。
此代码:
import collections
import functools
import types
def decorateAll(decorator):
class MetaClassDecorator(type):
def __new__(meta, classname, supers, classdict):
for name, elem in classdict.items():
if type(elem) is types.FunctionType:
classdict[name] = decorator(classdict[name])
return type.__new__(meta, classname, supers, classdict)
return MetaClassDecorator
class Counter(object):
def __init__(self, fun):
self.fun = fun
self.cache = {None: self}
self.count = collections.defaultdict(int)
def __get__(self, obj, cls=None):
if obj is None:
return self
try:
return self.cache[obj]
except KeyError:
pass
print('Binding {} and {}'.format(self.fun, obj))
cex = self.cache[obj] = functools.partial(self.__call__, obj)
return cex
def __call__(self, obj, *args, **kwargs):
print("args:", obj, *args, **kwargs)
self.count[obj] += 1
print("{0} Exec {1} times".format(self.fun.__name__, self.count[obj]))
return self.fun(obj, *args, **kwargs)
class Account(object, metaclass=decorateAll(Counter)):
def __init__(self, initial_amount):
self.amount = initial_amount
def withdraw(self, towithdraw):
self.amount -= towithdraw
def deposit(self, todeposit):
self.amount += todeposit
def balance(self):
return self.amount
a = Account(33.5)
print(a.balance())
产生以下输出:
Binding <function Account.__init__ at 0x000002250BCD8B70> and <__main__.Account object at 0x000002250BCE8BE0>
args: <__main__.Account object at 0x000002250BCE8BE0> 33.5
__init__ Exec 1 times
Binding <function Account.balance at 0x000002250BCD8D90> and <__main__.Account object at 0x000002250BCE8BE0>
args: <__main__.Account object at 0x000002250BCE8BE0>
balance Exec 1 times
33.5
调用描述符的 __call__
方法,通过模拟绑定创建 functools.partial
.
类型的对象,将计数存储在每个方法上
我在这个 metaclass 装饰器上应用装饰器的实现有问题,我写道:
def decorateAll(decorator):
class MetaClassDecorator(type):
def __new__(meta, classname, supers, classdict):
for name, elem in classdict.items():
if type(elem) is FunctionType:
classdict[name] = decorator(classdict[name])
return type.__new__(meta, classname, supers, classdict)
return MetaClassDecorator
这是我在其中使用元class:
的classclass Account(object, metaclass=decorateAll(Counter)):
def __init__(self, initial_amount):
self.amount = initial_amount
def withdraw(self, towithdraw):
self.amount -= towithdraw
def deposit(self, todeposit):
self.amount += todeposit
def balance(self):
return self.amount
当我传递给装饰器 metaclass 一个像这样实现的装饰器时,一切似乎都很好:
def Counter(fun):
fun.count = 0
def wrapper(*args):
fun.count += 1
print("{0} Executed {1} times".format(fun.__name__, fun.count))
return fun(*args)
return wrapper
但是当我使用以这种方式实现的装饰器时:
class Counter():
def __init__(self, fun):
self.fun = fun
self.count = 0
def __call__(self, *args, **kwargs):
print("args:", self, *args, **kwargs)
self.count += 1
print("{0} Executed {1} times".format(self.fun.__name__, self.count))
return self.fun(*args, **kwargs)
我收到这个错误:
line 32, in __call__
return self.fun(*args, **kwargs)
TypeError: __init__() missing 1 required positional argument: 'initial_amount'
为什么?将两个装饰器实现与其他函数一起使用不会给我带来问题。我认为问题与我试图装饰的方法是 class 方法这一事实有关。我错过了什么吗?
您需要将 Counter
实现为可调用描述符。在描述符上执行 __get__
时,您模拟将描述符绑定到传递给它的实例。加上按 method/object 存储计数。
此代码:
import collections
import functools
import types
def decorateAll(decorator):
class MetaClassDecorator(type):
def __new__(meta, classname, supers, classdict):
for name, elem in classdict.items():
if type(elem) is types.FunctionType:
classdict[name] = decorator(classdict[name])
return type.__new__(meta, classname, supers, classdict)
return MetaClassDecorator
class Counter(object):
def __init__(self, fun):
self.fun = fun
self.cache = {None: self}
self.count = collections.defaultdict(int)
def __get__(self, obj, cls=None):
if obj is None:
return self
try:
return self.cache[obj]
except KeyError:
pass
print('Binding {} and {}'.format(self.fun, obj))
cex = self.cache[obj] = functools.partial(self.__call__, obj)
return cex
def __call__(self, obj, *args, **kwargs):
print("args:", obj, *args, **kwargs)
self.count[obj] += 1
print("{0} Exec {1} times".format(self.fun.__name__, self.count[obj]))
return self.fun(obj, *args, **kwargs)
class Account(object, metaclass=decorateAll(Counter)):
def __init__(self, initial_amount):
self.amount = initial_amount
def withdraw(self, towithdraw):
self.amount -= towithdraw
def deposit(self, todeposit):
self.amount += todeposit
def balance(self):
return self.amount
a = Account(33.5)
print(a.balance())
产生以下输出:
Binding <function Account.__init__ at 0x000002250BCD8B70> and <__main__.Account object at 0x000002250BCE8BE0>
args: <__main__.Account object at 0x000002250BCE8BE0> 33.5
__init__ Exec 1 times
Binding <function Account.balance at 0x000002250BCD8D90> and <__main__.Account object at 0x000002250BCE8BE0>
args: <__main__.Account object at 0x000002250BCE8BE0>
balance Exec 1 times
33.5
调用描述符的 __call__
方法,通过模拟绑定创建 functools.partial
.