装饰实例 method/classmethod 的 Python 装饰器是否有可能知道函数将绑定到 class?
Is it possible for a Python decorator decorating a instance method/classmethod to know the class the function will be bound to?
对于顶级函数:
def wrap(f: Callable) -> Callable:
# Some logic
return f
当这样的函数被用来修饰在 class
主体中定义的另一个函数时:
class SomeClass:
@wrap
# may also be a @classmethod
def some_method(self, *args, **kwargs):
pass
是否可以在 wrap
函数中以某种方式检查传入的方法(在本例中为 SomeClass.some_method
)并检索 SomeClass
的类型对象?
我尝试在运行时使用调试器检查 f
,但是当 f
在包装器中时,我能从 f
中找到的唯一相关信息是 __qualname__
属性,其中包含class 名字。
如果您想知道我为什么要这样做:我正在尝试为某个 class 创建某种基于方法名称(方法都是属性)的模式,并将此模式存储在 dict
中,我希望键是 class 对象本身。以类型签名表示:
SchemaSource = TypeVar('SchemaSource', bound=Any)
Method = Callable[..., Any] # will be bound to SchemaSource
Schema = Dict[Type[SchemaSource], Dict[str, Method]]
当然,我可以稍后检查 class __dict__
,或者使用例如 __init_subclass__
钩子,但是因为我想在模式中包含一些方法,我我认为修饰函数是通过单一来源提供此信息的好方法。
如jasonharper所述,在调用装饰器时class还不存在,因此它接收的函数只是一个常规函数(除了它的名字提到class 会绑定到)。
对于我的问题,我最终采用了 attrs
风格,使用额外的装饰器来装饰 class。
def include(f: Callable) -> Callable:
"""Add function `f` to SchemaBuilder."""
SchemaBuilder.append(f)
return f
class SchemaBuilder:
records: Dict[Type, Dict[Callable, Any]] = {}
temporary: List[Callable] = []
@classmethod
def append(cls, f: Callable):
"""Temporarily store the method in a list."""
cls.temporary.append(f)
@classmethod
def register(cls, target_cls: Type):
"""Associate all methods stored in the list with `target_cls`.
We rely on the fact that `target_cls` will be instantiated
(and thus this method called) as soon as all of its (immediate)
methods have been created.
"""
cls.records[target_cls] = {k: None for k in cls.temporary}
cls.temporary = []
# In use:
@SchemaBuilder.register # called later
class SomeClass:
@property
@include # called first
def some_property(self): # will be included
pass
@property
def some_other_property(self): # will not be included
pass
对于顶级函数:
def wrap(f: Callable) -> Callable:
# Some logic
return f
当这样的函数被用来修饰在 class
主体中定义的另一个函数时:
class SomeClass:
@wrap
# may also be a @classmethod
def some_method(self, *args, **kwargs):
pass
是否可以在 wrap
函数中以某种方式检查传入的方法(在本例中为 SomeClass.some_method
)并检索 SomeClass
的类型对象?
我尝试在运行时使用调试器检查 f
,但是当 f
在包装器中时,我能从 f
中找到的唯一相关信息是 __qualname__
属性,其中包含class 名字。
如果您想知道我为什么要这样做:我正在尝试为某个 class 创建某种基于方法名称(方法都是属性)的模式,并将此模式存储在 dict
中,我希望键是 class 对象本身。以类型签名表示:
SchemaSource = TypeVar('SchemaSource', bound=Any)
Method = Callable[..., Any] # will be bound to SchemaSource
Schema = Dict[Type[SchemaSource], Dict[str, Method]]
当然,我可以稍后检查 class __dict__
,或者使用例如 __init_subclass__
钩子,但是因为我想在模式中包含一些方法,我我认为修饰函数是通过单一来源提供此信息的好方法。
如jasonharper所述,在调用装饰器时class还不存在,因此它接收的函数只是一个常规函数(除了它的名字提到class 会绑定到)。
对于我的问题,我最终采用了 attrs
风格,使用额外的装饰器来装饰 class。
def include(f: Callable) -> Callable:
"""Add function `f` to SchemaBuilder."""
SchemaBuilder.append(f)
return f
class SchemaBuilder:
records: Dict[Type, Dict[Callable, Any]] = {}
temporary: List[Callable] = []
@classmethod
def append(cls, f: Callable):
"""Temporarily store the method in a list."""
cls.temporary.append(f)
@classmethod
def register(cls, target_cls: Type):
"""Associate all methods stored in the list with `target_cls`.
We rely on the fact that `target_cls` will be instantiated
(and thus this method called) as soon as all of its (immediate)
methods have been created.
"""
cls.records[target_cls] = {k: None for k in cls.temporary}
cls.temporary = []
# In use:
@SchemaBuilder.register # called later
class SomeClass:
@property
@include # called first
def some_property(self): # will be included
pass
@property
def some_other_property(self): # will not be included
pass