代码之家  ›  专栏  ›  技术社区  ›  Brendan Abel

在函数属性中访问self

  •  6
  • Brendan Abel  · 技术社区  · 9 年前

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

    我遇到的问题是,当函数对象被传递到decorator时,它是未绑定的,并且不包含隐式 self 论点当我调用创建的属性函数(即。 string() ),我无法访问 自己 并且不能将其传递到原始函数中。

    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
    

    当我执行decorator创建的属性时,这是我得到的错误,因为 args 不包含 自己 参考

    >>> 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)
    

    有没有一种方法可以创建一个这样的装饰器,它可以引用 自己 绑定 添加的属性函数( 字符串() )所以 而且 使用隐式 自己 论点

    2 回复  |  直到 9 年前
        1
  •  6
  •   jme    9 年前

    descriptors 在这里:

    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 是一个装饰师,但它也是 descriptor 描述符是定义替代行为的对象,当对象作为其父对象的属性被查找时,该替代行为将被调用。有趣的是,边界方法本身是使用描述符协议实现的

    真是太多了。让我们看看运行示例代码时会发生什么。首先,当我们定义 plus 方法,我们应用 德科 室内装修设计师现在,我们通常将函数视为装饰器,函数的返回值是装饰结果。这里我们使用一个类作为装饰器。因此 Test.plus 不是函数,而是 例子 德科 类型此实例包含对 我们希望包装的函数。

    这个 德科 班上有一个 __call__ 方法,该方法允许它的实例像函数一样工作。此实现只是将给定的参数传递给 函数的引用。请注意,第一个参数将是对 Test 例子

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

    这很神奇,所以这里有一个免责声明:虽然这看起来很酷,但实现这样的东西可能不是一个好主意。

        2
  •  4
  •   Mazdak    9 年前

    您需要在实例化时(在创建实例方法之前)装饰函数。可以通过重写 __new__ 方法:

    class Test(object):
    
        def __new__(cls, *args_, **kwargs_):
            def deco(func):
                def string(*args, **kwargs):
                    return "my_str is :" + str(func(*args, **kwargs))
                # *1
                func.__func__.string = string
                return func
    
            obj = object.__new__(cls, *args_, **kwargs_)
            setattr(obj, 'plus', deco(getattr(obj, 'plus')))
            return obj
    
        def __init__(self, value):
            self._value = 1
    
        def plus(self, n):
            return self._value + n
    

    演示:

    >>> t = Test(100)
    >>> t.plus(1)
    >>> t.plus.string(5)
    >>> 'my_str is :6'
    

    1.由于python不允许您在设置时访问真实实例属性,因此可以使用 __func__ 方法,以便访问实例方法的实函数对象。