python内置@property如何接收重载的setter函数?

时间:2018-10-02 12:19:00

标签: python annotations built-in

使用@property定义内置的python属性时,如果属性对象已重载(具有相同的名称),则该属性对象如何将setter与getter方法区分开?

class A:

    def __init__(self):
        self._x = 12

    @property
    def x(self) -> int:
        return self._x

    @notifiable # strangely this stacks on both setter and getter
    @x.setter
    def x(self, val: int):
        self._x = val

如果定义了自定义属性装饰器,请说:

class my_property:
    def __init__(self, getter):
        print("__init__ getter %s:" % getter)

    def setter(self, setter: FunctionType):
        print("setter: %s" % setter)


class B:
    def __init__(self):
        self._val = 44

    @my_property
    def x(self):
        return self._val

    @x.setter
    def x(self, val):
        self._val = val

执行代码将导致以下输出

__init__ getter <function B.x at 0x7ff80c5e1620>:
setter: <function B.x at 0x7ff80c5e1620>

传递给装饰器的getter和setter函数是相同的函数,但是它们应该是不同的功能。

如果我使用这样的注释:

class C:
    def __init__(self):
        self._val = 44

    @my_property
    def x(self):
        return self._val

    @x.setter
    def set_x(self, val):
        self._val = val

按预期打印了不同的功能。

__init__ getter <function C.x at 0x7f529132c6a8>:
setter: <function C.set_x at 0x7f529132c6a8>

python如何使用内置的@property解决此问题?装饰器与用户装饰器的处理方式不同吗?

1 个答案:

答案 0 :(得分:1)

之所以看到这里的内容,是因为您没有在任何地方引用getter。这意味着一旦__init__方法结束,就不再引用第一个B.x(即refcount为零),因此该函数被释放。由于原始的getter函数已经发布,因此Python可以自由地将完全相同的内存地址重用于另一个对象/函数,在这种情况下就是这种情况。

如果您修改my_property以保留对原始getter方法的引用,例如:

class my_property:
    def __init__(self, getter):
        print("__init__ getter %s:" % getter)
        self.getter = getter

    def setter(self, setter: FunctionType):
        print("setter: %s" % setter)

您将看到函数名称(Bx)仍然相同(没关系,因为python不使用函数名称来唯一标识一个函数),但是两个函数的内存地址不同:

__init__ getter <function B.x at 0x7f9870d60048>
setter: <function B.x at 0x7f9870d600d0>
  

装饰器与用户装饰器的处理方式不同吗?

不,属性只是一个普通的装饰器。但是,如果您想重新实现属性装饰器,您可能会对descriptor protocol感兴趣(该页面中有@property的纯python重新实现)。