使用 wrapt 包更改类型

type changed using wrapt package

这个

<class 'assign.A'>
<class 'BoundFunctionWrapper'>

由以下代码打印:

from wrapt import FunctionWrapper


class A(FunctionWrapper):
    A = None

    def __init__(self, f):
        super(A, self).__init__(f, self.wrapper)

    def wrapper(self, wrapped, instance, args, kwargs):
        return wrapped(*args, **kwargs)


@A
def f():
    pass


print(type(f))
A.A = f
print(type(A.A))

如果将 f 分配给 class 的静态变量,则类型只更改 。我本来希望类型保持 A。 怎么会这样?

我检查了一下,这将是 Python 2 中的默认行为,而不是 Python 3 中的默认行为:

>>> class X(object): # Python 2
    def g():pass
>>> X.g
<unbound method X.g>

wrapt 行为的原因:

  1. 同样的模块也存在于Python 2.
  2. 我在 wrapt.wrappers 的代码中看到没有检查为这种特殊情况创建不同的行为形式 2 到 3。

解释

__get__ 行为发生了变化。

Python 2:

>>> class X(object):
    def g():pass

>>> X.g
<unbound method X.g>
>>> class B: # error: Must inherit form object (see comments)
    def __get__(self, *args):
        print(args)


>>> X.b = B()
>>> X.b
<__main__.B instance at 0x029C0440>

Python 3:

>>> class X(object):
    def g():pass

>>> X.g
<function g at 0x030430C0>
>>> class B:
    def __get__(self, *args):
        print(args)


>>> X.b = B()
>>> X.b
(None, <class 'X'>)

推荐

  1. 我想说这可能是一个错误。根据 pypi , wrapt 渴望正确。所以,我想这应该讨论一下。请在 github 和 link 这个问题上开一个问题(告诉我是否应该这样做)。

  2. 使用属性 __bound_function_wrapper__ 为绑定方法创建自己的包装器。在 wrapt.wrappers 中显示上下文的代码:

    class FunctionWrapper(_FunctionWrapperBase):
    
        __bound_function_wrapper__ = BoundFunctionWrapper
    

    所以,如果你真的想创建一个自己的包装器 class 你可以这样做:

    class OwnBoundWrapper(wrapt.BoundFunctionWrapper): 
        # additional code here
    A.__bound_function_wrapper__ = OwnBoundWrapper
    

我猜这是(大部分)预期的行为。

您可以阅读 this for more details, but basically, as you mentioned the fact that FunctionWrapper is a descriptor(有一个 __get__ 方法)是导致您观察到的行为的原因。

当你做类似

的事情时,描述符协议用于return方法而不仅仅是裸函数
some_var = instance.method

当从 class:

检索实例方法时,python 2 和 3 确实具有不同的行为

python3:

In [1]: class Test:
...:     def a(self):
...:         pass
...:

In [2]: Test.a
Out[2]: <function __main__.Test.a>

python2:

In [1]: class Test:
...:     def a(self):
...:         pass
...:

In [2]: Test.a
Out[2]: <function __main__.Test.a>

其中 python2 return 是一个未绑定的方法,python3 只是 return 的函数。但是,可能出于某种原因,当在 class 上访问时,人们可能仍想装饰它们。做这样的事情的一个例子是创建一个混合 属性,它有一个名字,但在 class 上的行为与 class.

的实例不同。