Class/metaclass 派生的方法装饰器 class
Class/metaclass method decorator for derived class
我有一个元class,它定义了一个 class 级别属性,该属性对于每个子 class 应该是唯一的,但在每个子 class 的实例之间共享。
class MetaValidator(type):
def __new__(
cls, name, bases, dct
):
new_cls = super().__new__(cls, name, bases, dct)
new_cls.valid_funcs = []
return new_cls
现在我想实现一个装饰器,将装饰的 class 方法附加到派生的 class 中的 valid_funcs。然而,因为派生 class 仍在定义中,所以我没有对派生装饰器的引用,所以我最终附加到基础 class。这是我的代码:
class Validator(object, metaclass=MetaValidator):
@classmethod
def add(cls, f):
cls.valid_funcs.append(f)
return f
def _validate(self, **kwargs):
for f in self.valid_funcs:
params = inspect.signature(f).parameters.keys()
f_kwargs = {name: kwargs[name] for name in params}
f(**f_kwargs)
def validate(self, **kwargs):
self._validate(**kwargs)
class A(Validator):
@staticmethod
@Validator.add
def test_func(x):
return x
class B(Validator):
@staticmethod
@Validator.add
def test_func(x, y):
return x, y
a = A()
a.validate(x="In A")
b = B()
b.validate(x="In B", y=" Called with arg y")
print(Validator.valid_funcs)
print(a.valid_funcs)
print(b.valid_funcs)
这会打印:
[<function A.test_func at 0x7f0189d4fc80>,
<function B.test_func at 0x7f0189d4fd08>]
[]
[]
我要:
[]
[<function A.test_func at 0x7f0189d4fc80>]
[<function B.test_func at 0x7f0189d4fd08>]
class 对象 当执行 class 主体中函数的装饰器时。 class 主体首先执行, 然后 创建 class。
与其让装饰器寻找要改变的 class 属性,不如向装饰函数对象添加一个属性。 metaclass、 或 _validate()
实现 然后会查找具有此属性的任何对象,并在 class 对象被添加后将它们添加到列表中已创建。
我假设您希望保留装饰者将装饰项目添加到列表中的顺序:
from itertools import count
class Validator(metaclass=MetaValidator):
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
在元class中:
class MetaValidator(type):
def __new__(cls, name, bases, dct):
new_cls = super().__new__(cls, name, bases, dct)
registered = []
for v in dct.values():
id = getattr(v, '_validator_function_id', None)
if id is None and isinstance(v, (staticmethod, classmethod)):
# unwrap staticmethod or classmethod decorators
id = getattr(v.__func__, '_validator_function_id', None)
if id is not None:
registered.append((id, v))
new_cls.valid_funcs = [f for _, f in sorted(registered)]
return new_cls
请注意,如果您使用的是 Python 3.6 或更新版本,则您根本不再需要元class。您可以将相同的逻辑放入 class.__init_subclass__
method.
请注意,这会注册 未绑定对象。对于 staticmethod
个对象,这意味着调用将失败并显示:
TypeError: <staticmethod object at 0x10d1b7048> is not a callable object
在这种情况下,您可能想注册 __func__
属性,或者使用 .__get__
将对象 'bind' 指向某物(staticmethod
无论如何都会忽略绑定上下文)`.
如果您在 _validate()
方法中显式绑定,那么您实际上不必使用 staticmethod
个对象:
def _validate(self, **kwargs):
for f in self.valid_funcs:
bound = f.__get__(self)
signature = inspect.signature(bound)
bound(**{name: kwargs[name] for name in signature.parameters})
现在 @validator.add
将与 staticmethod
、classmethod
和常规函数一起使用。
如果您有 _validate()
方法,请查找这些方法,然后可以 为您完成绑定 。您可以选择仅使用 dir()
和 getattr()
:
来支持继承
from operator import itemgetter
from itertools import count
class Validator:
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
def _list_validators(self):
objects = (getattr(self, name) for name in dir(self))
return sorted(
(o for o in objects if hasattr(o, '_validator_function_id')),
key=attrgetter('_validator_function_id'))
def _validate(self, **kwargs):
for f in self._list_validators():
signature = inspect.signature(f)
f(**{name: kwargs[name] for name in signature.parameters})
getattr()
给你一个绑定对象,不需要进一步绑定。
虽然让 metaclass __new__
处理向 valid_funcs
添加函数是一个选项,但另一个选项是将 valid_funcs
注入到 class body 在 class 之前甚至存在,使用 __prepare__
:
class MetaValidator(type):
@classmethod
def __prepare__(cls, name, bases, **kwds):
ns = super().__prepare__(name, bases, **kwds)
ns['valid_funcs'] = []
return ns
def register(func_list):
def inner_register(func):
func_list.append(func)
return func
return inner_register
class A(metaclass=MetaValidator):
@register(valid_funcs)
def method(self):
...
不过,我可能会跳过所有元class 内容并要求classes 自己做valid_funcs = []
。 metaclass 的额外复杂性不值得仅仅为每个 class.
保存一行样板文件
我有一个元class,它定义了一个 class 级别属性,该属性对于每个子 class 应该是唯一的,但在每个子 class 的实例之间共享。
class MetaValidator(type):
def __new__(
cls, name, bases, dct
):
new_cls = super().__new__(cls, name, bases, dct)
new_cls.valid_funcs = []
return new_cls
现在我想实现一个装饰器,将装饰的 class 方法附加到派生的 class 中的 valid_funcs。然而,因为派生 class 仍在定义中,所以我没有对派生装饰器的引用,所以我最终附加到基础 class。这是我的代码:
class Validator(object, metaclass=MetaValidator):
@classmethod
def add(cls, f):
cls.valid_funcs.append(f)
return f
def _validate(self, **kwargs):
for f in self.valid_funcs:
params = inspect.signature(f).parameters.keys()
f_kwargs = {name: kwargs[name] for name in params}
f(**f_kwargs)
def validate(self, **kwargs):
self._validate(**kwargs)
class A(Validator):
@staticmethod
@Validator.add
def test_func(x):
return x
class B(Validator):
@staticmethod
@Validator.add
def test_func(x, y):
return x, y
a = A()
a.validate(x="In A")
b = B()
b.validate(x="In B", y=" Called with arg y")
print(Validator.valid_funcs)
print(a.valid_funcs)
print(b.valid_funcs)
这会打印:
[<function A.test_func at 0x7f0189d4fc80>,
<function B.test_func at 0x7f0189d4fd08>]
[]
[]
我要:
[]
[<function A.test_func at 0x7f0189d4fc80>]
[<function B.test_func at 0x7f0189d4fd08>]
class 对象 当执行 class 主体中函数的装饰器时。 class 主体首先执行, 然后 创建 class。
与其让装饰器寻找要改变的 class 属性,不如向装饰函数对象添加一个属性。 metaclass、 或 _validate()
实现 然后会查找具有此属性的任何对象,并在 class 对象被添加后将它们添加到列表中已创建。
我假设您希望保留装饰者将装饰项目添加到列表中的顺序:
from itertools import count
class Validator(metaclass=MetaValidator):
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
在元class中:
class MetaValidator(type):
def __new__(cls, name, bases, dct):
new_cls = super().__new__(cls, name, bases, dct)
registered = []
for v in dct.values():
id = getattr(v, '_validator_function_id', None)
if id is None and isinstance(v, (staticmethod, classmethod)):
# unwrap staticmethod or classmethod decorators
id = getattr(v.__func__, '_validator_function_id', None)
if id is not None:
registered.append((id, v))
new_cls.valid_funcs = [f for _, f in sorted(registered)]
return new_cls
请注意,如果您使用的是 Python 3.6 或更新版本,则您根本不再需要元class。您可以将相同的逻辑放入 class.__init_subclass__
method.
请注意,这会注册 未绑定对象。对于 staticmethod
个对象,这意味着调用将失败并显示:
TypeError: <staticmethod object at 0x10d1b7048> is not a callable object
在这种情况下,您可能想注册 __func__
属性,或者使用 .__get__
将对象 'bind' 指向某物(staticmethod
无论如何都会忽略绑定上下文)`.
如果您在 _validate()
方法中显式绑定,那么您实际上不必使用 staticmethod
个对象:
def _validate(self, **kwargs):
for f in self.valid_funcs:
bound = f.__get__(self)
signature = inspect.signature(bound)
bound(**{name: kwargs[name] for name in signature.parameters})
现在 @validator.add
将与 staticmethod
、classmethod
和常规函数一起使用。
如果您有 _validate()
方法,请查找这些方法,然后可以 为您完成绑定 。您可以选择仅使用 dir()
和 getattr()
:
from operator import itemgetter
from itertools import count
class Validator:
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
def _list_validators(self):
objects = (getattr(self, name) for name in dir(self))
return sorted(
(o for o in objects if hasattr(o, '_validator_function_id')),
key=attrgetter('_validator_function_id'))
def _validate(self, **kwargs):
for f in self._list_validators():
signature = inspect.signature(f)
f(**{name: kwargs[name] for name in signature.parameters})
getattr()
给你一个绑定对象,不需要进一步绑定。
虽然让 metaclass __new__
处理向 valid_funcs
添加函数是一个选项,但另一个选项是将 valid_funcs
注入到 class body 在 class 之前甚至存在,使用 __prepare__
:
class MetaValidator(type):
@classmethod
def __prepare__(cls, name, bases, **kwds):
ns = super().__prepare__(name, bases, **kwds)
ns['valid_funcs'] = []
return ns
def register(func_list):
def inner_register(func):
func_list.append(func)
return func
return inner_register
class A(metaclass=MetaValidator):
@register(valid_funcs)
def method(self):
...
不过,我可能会跳过所有元class 内容并要求classes 自己做valid_funcs = []
。 metaclass 的额外复杂性不值得仅仅为每个 class.