在函数属性中访问self

布伦丹·亚伯

我正在尝试添加一个装饰器,该装饰器将可调用属性添加到函数中,这些函数返回的对象与该函数的返回值略有不同,但将在某个时候执行该函数。

我遇到的问题是,当函数对象传递到装饰器中时,它是未绑定的,并且不包含隐式self参数。当我调用创建的属性函数(即string())时,我无权访问self,也无法将其传递给原始函数。

def deco(func):
    """
    Add an attribute to the function takes the same arguments as the
    function but modifies the output.
    """
    def string(*args, **kwargs):
        return str(func(*args, **kwargs))
    func.string = string
    return func


class Test(object):

    def __init__(self, value):
        self._value = 1

    @deco
    def plus(self, n):
        return self._value + n

当我执行装饰器创建的属性时,这是我得到的错误,因为其中args不包含self引用。

>>> t = Test(100)
>>> t.plus(1)         # Gets passed self implicitly
101
>>> t.plus.string(1)  # Does not get passed self implicitly
...
TypeError: plus() takes exactly 2 arguments (1 given)

有没有一种方法可以创建这样的装饰器,以获取参考self还是有一种方法可以绑定添加的属性函数(string()),以便它可以使用隐式self参数来调用?

me

您可以在此处使用描述符

class deco(object):

    def __init__(self, func):
        self.func = func
        self.parent_obj = None

    def __get__(self, obj, type=None):
        self.parent_obj = obj
        return self

    def __call__(self, *args, **kwargs):
        return self.func(self.parent_obj, *args, **kwargs)

    def string(self, *args, **kwargs):
        return str(self(*args, **kwargs))


class Test(object):

    def __init__(self, value):
        self._value = value

    @deco
    def plus(self, n):
        return self._value + n

以便:

>>> test = Test(3)
>>> test.plus(1)
4
>>> test.plus.string(1)
'4'

这值得解释。deco是一个装饰器,但它也是一个描述符描述符是定义替代行为的对象,当该对象作为其父对象的属性查找时,将调用该行为。有趣的是,界限方法本身是使用描述符协议实现的

满嘴 让我们看看运行示例代码时会发生什么。首先,当定义plus方法时,我们应用deco装饰器。现在通常我们将函数视为修饰器,并且该函数的返回值是修饰的结果。在这里,我们使用一个类作为装饰器。其结果是,Test.plus不是一个函数,而是一个实例的的deco类型。该实例包含对plus我们希望包装函数的引用

deco类有一个__call__方法,允许它的实例表现得像功能。此实现只是将给定的参数传递给plus它具有引用功能。请注意,第一个参数将是对Test实例的引用

棘手的部分来自于实现test.plus.string(1)为此,我们需要引用testplus实例是属性实例。为此,我们使用描述符协议。也就是说,我们定义了一个__get__方法,只要deco实例被访问为某个父类实例的属性,方法就会被调用发生这种情况时,它将父对象存储在其内部。然后,我们可以简单地实现plus.stringdeco的方法,并使用对deco实例中存储的父对象的引用来获取所属test实例plus

这是很多魔术,所以这里有一个免责声明:尽管这看起来很酷,但是实现这样的东西可能不是一个好主意。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章