简体   繁体   English

装饰实例方法/类方法的 Python 装饰器是否有可能知道 class function 将绑定到?

[英]Is it possible for a Python decorator decorating a instance method/classmethod to know the class the function will be bound to?

For a top-level function:对于顶级 function:

def wrap(f: Callable) -> Callable:
    # Some logic
    return f

When such a function is used to decorate another function defined within a class body:当这样的 function 用于装饰在class主体中定义的另一个 function 时:

class SomeClass:
    @wrap
    # may also be a @classmethod
    def some_method(self, *args, **kwargs):
        pass

Is it possible within the wrap function to somehow inspect the passed-in method ( SomeClass.some_method in this case) and retrieve the type object of SomeClass ?是否有可能在wrap function 以某种方式检查传入的方法(在这种情况下为SomeClass.some_method )并检索SomeClass的 object 类型?

I tried using a debugger to inspect f during runtime, but the only relevant info I could find from f while it's in the wrapper was the __qualname__ attribute, which contained the class name.我尝试在运行时使用调试器检查f ,但我可以从f在包装器中找到的唯一相关信息是__qualname__属性,其中包含 class 名称。


In case you want to know why I am trying to do this: I am trying to create some kind of schema that is based on the method names (the methods are all properties) for a certain class, and store this schema in a dict where I would like the keys to be the class objects themselves.如果您想知道我为什么要这样做:我正在尝试为某个 class 创建某种基于方法名称(方法都是属性)的模式,并将此模式存储在dict中我希望键是 class 对象本身。 Expressed in type signatures:以类型签名表示:

SchemaSource = TypeVar('SchemaSource', bound=Any)
Method = Callable[..., Any]  # will be bound to SchemaSource
Schema = Dict[Type[SchemaSource], Dict[str, Method]]

Of course, I could inspect the class __dict__ later, or use for example an __init_subclass__ hook, but because I would like to include some methods in the schema, I figured that decorating the functions would be a good way to provide this information with a single source.当然,我可以稍后检查 class __dict__ ,或者使用例如__init_subclass__钩子,但是因为我想在模式中包含一些方法,所以我认为装饰函数将是提供此信息的好方法资源。

As jasonharper mentions, the class doesn't exist yet by the time the decorator is called, and so the function it receives is just a regular function (except that its name mentions the class it will be bound to). As jasonharper mentions, the class doesn't exist yet by the time the decorator is called, and so the function it receives is just a regular function (except that its name mentions the class it will be bound to).


For my problem, I ended up doing it attrs -style, using an additional decorator to decorate the class as well.对于我的问题,我最终使用了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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM