How to find class of bound method during class construction in Python 3.1?
我想编写一个修饰器,使类的方法对其他方可见;但是,我描述的问题独立于这个细节。代码大致如下:
1 2 3 4 5 6 7 8 9 10 11 | def CLASS_WHERE_METHOD_IS_DEFINED( method ): ??? def foobar( method ): print( CLASS_WHERE_METHOD_IS_DEFINED( method ) ) class X: @foobar def f( self, x ): return x ** 2 |
我这里的问题是,当装饰器
在上面的代码中,任何人都可以在
当调用decorator时,它是以一个函数作为参数来调用的,而不是以一个方法来调用的——因此,如果decorator想检查和内省它的方法,那么它将毫无用处,因为它只是一个函数,不包含任何关于封闭类的信息。我希望这能解决你的"谜语",尽管是负面的!
可能会尝试其他方法,例如对嵌套堆栈帧进行深入的自省,但这些方法非常简单、脆弱,而且肯定不会延续到pynie等python3的其他实现中;因此,我强烈建议避免使用这些方法,以支持您已经在考虑的类装饰器解决方案,并且更清晰、更具Soli性。d.
正如我在其他一些答案中提到的,由于python3.6,这个问题的解决方案非常简单,这要归功于
我们可以使用它来定义一个可以通过以下方式访问类的decorator:
1 2 3 4 5 6 7 8 9 10 | class class_decorator: def __init__(self, fn): self.fn = fn def __set_name__(self, owner, name): # do something with"owner" (i.e. the class) print(f"decorating {self.fn} and using {owner}") # then replace ourself with the original method setattr(owner, name, self.fn) |
然后可以用作普通的装饰:
1 2 3 4 5 6 7 8 | >>> class A: ... @class_decorator ... def hello(self, x=42): ... return x ... decorating <function A.hello at 0x7f9bedf66bf8> and using <class '__main__.A'> >>> A.hello <function __main__.A.hello(self, x=42)> |
这是一篇很老的文章,但是内省不是解决这个问题的方法,因为使用元类和一些使用描述符的聪明的类构造逻辑可以更容易地解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import types # a descriptor as a decorator class foobar(object): owned_by = None def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): # a proxy for `func` that gets used when # `foobar` is referenced from by a class return self.func(*args, **kwargs) def __get__(self, inst, cls=None): if inst is not None: # return a bound method when `foobar` # is referenced from by an instance return types.MethodType(self.func, inst, cls) else: return self def init_self(self, name, cls): print("I am named '%s' and owned by %r" % (name, cls)) self.named_as = name self.owned_by = cls def init_cls(self, cls): print("I exist in the mro of %r instances" % cls) # don't set `self.owned_by` here because # this descriptor exists in the mro of # many classes, but is only owned by one. print('') |
实现这一点的关键是元类——它搜索在创建的类上定义的属性,以查找
仅对定义了描述符的类调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import inspect class MetaX(type): def __init__(cls, name, bases, classdict): # The classdict contains all the attributes # defined on **this** class - no attribute in # the classdict is inherited from a parent. for k, v in classdict.items(): if isinstance(v, foobar): v.init_self(k, cls) # getmembers retrieves all attributes # including those inherited from parents for k, v in inspect.getmembers(cls): if isinstance(v, foobar): v.init_cls(cls) |
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # for compatibility import six class X(six.with_metaclass(MetaX, object)): def __init__(self): self.value = 1 @foobar def f(self, x): return self.value + x**2 class Y(X): pass # PRINTS: # I am named 'f' and owned by <class '__main__.X'> # I exist in the mro of <class '__main__.X'> instances # I exist in the mro of <class '__main__.Y'> instances print('CLASS CONSTRUCTION OVER ') print(Y().f(3)) # PRINTS: # 10 |