是否有可能为一个Python装饰器装饰一个实例方法/类方法知道类函数将绑定到?



对于顶级函数:

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中找到的唯一相关信息,而它在包装器中是__qualname__属性,其中包含类名。


如果你想知道我为什么要这样做:我试图为某个类创建某种基于方法名称(方法都是属性)的模式,并将此模式存储在dict中,我希望键是类对象本身。用类型签名表示:

SchemaSource = TypeVar('SchemaSource', bound=Any)
Method = Callable[..., Any]  # will be bound to SchemaSource
Schema = Dict[Type[SchemaSource], Dict[str, Method]]
当然,我可以稍后检查__dict__类,或者使用__init_subclass__钩子,但是因为我想在模式中包含一些方法,所以我认为用单个源来修饰函数是提供这些信息的好方法。

正如jasonharper提到的,当decorator被调用时,这个类还不存在,所以它接收到的函数只是一个普通的函数(除了它的名字提到了它将绑定到的类)。


对于我的问题,我最终做了attrs风格,使用一个额外的装饰器来装饰类。

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

相关内容

最新更新