添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

从装饰器中获取Python函数的所属类

10 人关注

我在PY中有一个装饰器。 它是一个方法,以函数为参数。 我想根据传递的函数来创建一个目录结构。 我在父目录中使用模块名,但想在子目录中使用类名。 我不知道如何获得拥有fn对象的类的名称。

我的装饰师。

def specialTest(fn):
    filename = fn.__name__
    directory = fn.__module__
    subdirectory = fn.__class__.__name__ #WHERE DO I GET THIS
    
1 个评论
在装饰器中给出一个特殊的参数: is_member_func ,如果它是真的,则得到第一个参数,即自我。
python
class
function
decorator
inspect
Corey Coogan
Corey Coogan
发布于 2011-10-07
3 个回答
David Wolever
David Wolever
发布于 2021-06-17
已采纳
0 人赞同

If fn is an instancemethod , then you can use fn.im_class .

>>> class Foo(object): ... def bar(self): ... pass >>> Foo.bar.im_class __main__.Foo

请注意,这将 not 饰器中工作,因为一个函数只被转化为一个实例方法 之后 类被定义(即,如果 @specialTest 被用来装饰 bar ,它将不会起作用;如果它甚至是可能的,在那个时候做它将必须通过检查调用堆栈或同样不快乐的东西来完成)。

这正是我所担心的。 谢谢。
由于某些原因,直到我回到代码中才发现这一点。 这是有可能的,我只需要把与FN交互的代码移到封装的函数内,其中FN是一个实例。
这在定义时是不可能的,然而你可以在类被创建后列出装饰的函数:为了实现这一点,装饰器必须在类上放置一些标记属性
@kolypto 你的意思是在一个标记属性上 包裹的功能 ...因为到目前为止的问题是装饰器无法访问该类?
@Anentropic,对:有些代码必须对创建的类上的方法进行后处理,为了分辨哪些方法被装饰了--我们需要以某种方式标记它们。例如,用一个自定义属性
Cat Plus Plus
Cat Plus Plus
发布于 2021-06-17
0 人赞同

在Python 2中,你可以在方法对象上使用 im_class 属性。在Python 3中,它将是 __self__.__class__ (或 type(method.__self__) )。

outis
outis
发布于 2021-06-17
0 人赞同

Getting the Class Name

如果你想要的只是类的名字(而不是类本身),它可以作为函数的一部分(部分)来使用 资格名称属性 ( __qualname__ ).

import os.path
def decorator(fn):
    filename = fn.__name__
    directory = fn.__module__
    subdirectory = fn.__qualname__.removesuffix('.' + fn.__name__).replace('.', os.path.sep)
    return fn
class A(object):
    @decorator
    def method(self):
    class B(object):
        @decorator
        def method(self):

如果该方法的类是一个内部类,qualname将包括外部类。上面的代码通过用本地路径分隔符替换所有的点分隔符来处理这个问题。

当装饰器被调用时,除了它的名字之外,类的任何东西都无法访问,因为类本身还没有被定义。

Getting the Class At Method Call

如果需要类本身,并且访问可以延迟到被装饰的方法被(第一次)调用时,装饰器可以像往常一样包装函数,然后包装器可以访问实例和类。如果该方法只需被调用一次,包装器也可以删除自己并取消装饰。

import types
def once(fn):
    def wrapper(self, *args, **kwargs):
        # do something with the class
        subdirectory = type(self).__name__
        # undecorate the method (i.e. remove the wrapper)
        setattr(self, fn.__name__, types.MethodType(fn, self))
        # invoke the method
        return fn(self, *args, **kwargs)
    return wrapper
class A(object):
    @once
    def method(self):
a = A()
a.method()
a.method()

请注意,这只有在方法被调用时才会起作用。

Getting the Class After Class Definition

如果你需要在装饰方法没有被调用的情况下获得类的信息,你可以存储一个引用到包装器上的装饰器(方法#3)。,然后扫描所有类的方法(在感兴趣的类被定义之后),寻找那些引用装饰器的方法。

def decorator(fn):
    def wrapper(self, *args, **kwargs):
        return fn(self, *args, **kwargs)
    wrapper.__decorator__ = decorator
    wrapper.__name__ = 'decorator + ' + fn.__name__
    wrapper.__qualname__ = 'decorator + ' + fn.__qualname__
    return wrapper
def methodsDecoratedBy(cls, decorator):
    for method in cls.__dict__.values():
        if     hasattr(method, '__decorator__') \
           and method.__decorator__ == decorator:
            yield method
import sys, inspect
def allMethodsDecoratedBy(decorator)
    for name, cls in inspect.getmembers(sys.modules, lambda x: inspect.isclass(x)):
        for method in methodsDecoratedBy(cls, decorator):
            yield method

这基本上使装饰器成为一般编程意义上的注解(而非Python中的函数注解,这只是针对函数参数和返回值)。一个问题是装饰器必须是最后应用的,否则类属性就不会存储相关的包装器,而是存储另一个外部包装器。这个问题可以通过存储(并随后检查)包装器上的所有装饰器来部分解决。

def decorator(fn):
    def wrapper(self, *args, **kwargs):
        return fn(self, *args, **kwargs)
    wrapper.__decorator__ = decorator
    if not hasattr(fn, '__decorators__'):
        if hasattr(fn, '__decorator__'):
            fn.__decorators__ = [fn.__decorator__]
        else:
            fn.__decorators__ = []
    wrapper.__decorators__ = [decorator] + fn.__decorators__
    wrapper.__name__ = 'decorator(' + fn.__name__ + ')'
    wrapper.__qualname__ = 'decorator(' + fn.__qualname__ + ')'
    return wrapper
def methodsDecoratedBy(cls, decorator):
    for method in cls.__dict__.values():
        if hasattr(method, '__decorators__') and decorator in method.__decorators__:
            yield method

此外,任何你不控制的装饰者都可以通过装饰它们来使其合作,这样它们就会像decorator那样把自己储存在包装纸上。

def bind(*values, **kwvalues):
    def wrap(fn):
        def wrapper(self, *args, **kwargs):
            nonlocal kwvalues
            kwvalues = kwvalues.copy()
            kwvalues.update(kwargs)
            return fn(self, *values, *args, **kwvalues)
        wrapper.__qualname__ = 'bind.wrapper'
        return wrapper
    wrap.__qualname__ = 'bind.wrap'
    return wrap
def registering_decorator(decorator):
    def wrap(fn):
        decorated = decorator(fn)
        decorated.__decorator__ = decorator
        if not hasattr(fn, '__decorators__'):
            if hasattr(fn, '__decorator__'):
                fn.__decorators__ = [fn.__decorator__]
            else:
                fn.__decorators__ = []
        if not hasattr(decorated, '__decorators__'):
            decorated.__decorators__ = fn.__decorators__.copy()
        decorated.__decorators__.insert(0, decorator)
        decorated.__name__ = 'reg_' + decorator.__name__ + '(' + fn.__name__ + ')'
        decorated.__qualname__ = decorator.__qualname__ + '(' + fn.__qualname__ + ')'
        return decorated
    wrap.__qualname__ = 'registering_decorator.wrap'
    return wrap
class A(object):
    @decorator
    def decorated(self):
    @bind(1)
    def add(self, a, b):
        return a + b
    @registering_decorator(bind(1))
    @decorator
    def args(self, *args):
        return args
    @decorator
    @registering_decorator(bind(a=1))
    def kwargs(self, **kwargs):
        return kwargs
A.args.__decorators__
A.kwargs.__decorators__
assert not hasattr(A.add, '__decorators__')
a = A()
a.add(2)

另一个问题是扫描所有的类是低效的。你可以通过使用一个额外的类装饰器来注册所有的类来检查方法装饰器,从而使其更有效率。然而,这种方法是很脆的;如果你忘记装饰类,它就不会被记录在注册表中。

class ClassRegistry(object):
    def __init__(self):
        self.registry = {}
    def __call__(self, cls):
        self.registry[cls] = cls
        cls.__decorator__ = self
        return cls
    def getRegisteredClasses(self):
        return self.registry.values()
class DecoratedClassRegistry(ClassRegistry):
    def __init__(self, decorator):
        self.decorator = decorator
        super().__init__()
    def isDecorated(self, method):
        return (    hasattr(method, '__decorators__') \
                and self.decorator in method.__decorators__) \
            or (    hasattr(method, '__decorator__') \
                and method.__decorator__ == self.decorator)
    def getDecoratedMethodsOf(self, cls):
        if cls in self.registry:
            for method in cls.__dict__.values():
                if self.isDecorated(method):
                    yield method
    def getAllDecoratedMethods(self):
        for cls in self.getRegisteredClasses():
            for method in self.getDecoratedMethodsOf(cls):
                yield method

使用方法。

decoratedRegistry = DecoratedClassRegistry(decorator)
@decoratedRegistry
class A(object):
    @decoratedRegistry
    class B(object):
        @decorator
        def decorated(self):
        def func(self):
    @decorator
    def decorated(self):
    @bind(1)
    def add(self, a, b):
        return a + b
    @registering_decorator(bind(1))
    @decorator
    def args(self, *args):
        return args
    @decorator
    @registering_decorator(bind(a=1))
    def kwargs(self, **kwargs):
        return kwargs
decoratedRegistry.getRegisteredClasses()