【问题标题】:Why is Class property.setter not a case of method overloading?为什么 Class property.setter 不是方法重载的情况?
【发布时间】:2018-09-02 13:34:46
【问题描述】:

在 Python 中方法重载是不可能的! 你能解释一下为什么Python中的Properties.setter不是方法重载的情况吗?

class newOne():
    def __init__(self):
            self.__x = 0
    @property
    def val(self):
            return self.__x
    @val.setter
    def val(self,value):
            self.__x = value

在上面的代码中,我有两个具有相同名称“val”(但参数集不同)的方法,它们的行为不同。

【问题讨论】:

  • 你明白属性是描述符吗?这意味着什么?我不打算解释这一点,这是理解其余部分所必需的知识。但基本上,属性有点神奇,这是一种特殊情况,这里没有实际的方法重载。该类只有一个val 属性。
  • class A:def f(): print(1); def f(): print(2); A().f() -> prints 2。第二个定义实际上覆盖了第一个。然而 val.setter 实际上是将函数定义组合到 property 对象中,因此第一个定义实际上并没有“丢失”。然后描述符开始发挥作用。

标签: python properties


【解决方案1】:

这不是方法重载,因为没有两个具有不同签名的方法可以被调用。如果是方法重载,你可以这样做:

obj = newOne()

print(obj.val())
obj.val(5)

但这不起作用,因为valproperty 而不是重载方法。


那么那里发生了什么?为什么我们要定义两个同名的方法,如果不重载,它们会发生什么?

魔法发生在装饰器中。作为先决条件,您必须知道这一点

@deco
def func(...):
    ...

等价于

def func(...):
    ...

func = deco(func)

所以,首先发生的事情是 @property 装饰器将您的 getter 函数转换为一个属性,并以该函数作为它的 getter 函数:

class newOne:
    @property
    def val(self):
        return self.__x

print(newOne.val)
print(newOne.val.fget)
# output:
# <property object at 0x002B1F00>
# <function newOne.val at 0x0052EAE0>

在此之后,@val.setter 装饰器创建一个带有 getter setter 函数的 new 属性:

class newOne:
    @property
    def val(self):
        return self.__x

    @val.setter
    def val(self, value):
        self.__x = value

print(newOne.val)
print(newOne.val.fget)
print(newOne.val.fset)
# output:
# <property object at 0x0221B7B0>
# <function newOne.val at 0x0226EB70>
# <function newOne.val at 0x0226EAE0>

(getter 和 setter 函数具有相同的名称,因为它们都被定义为 def val(...),但它们仍然是不同的函数。这就是它们具有不同 id 的原因。)

所以最后你有一个带有getter 和setter 函数的val 属性。你确实没有有一个重载的方法。

有关属性如何工作以及如何调用 getter 和 setter 函数的详细信息,请参阅descriptor documentation

【讨论】:

    【解决方案2】:

    首先,@property 装饰器创建一个名为 val 的描述符,并将其放在一边,以便在定义后添加到类中。然后,@val.setter 装饰采用 its 函数并简单地有效地将对它的引用添加到val 描述符。

    你的代码大致相当于

    d = {}
    
    def __init__(self):
        self.__x = 0
    
    d['__init__'] = __init__
    
    def val(self):
        return self.__x
    
    d['val'] = property(val)
    
    def val(self, value):
        self.__x = value
    
    # Not d['val'].__set__ = val, as previously stated
    d['val'] = property(fget=d['val'], fset=val)
    
    newOne = type('newOne', (object,), d)
    
    # These are all "local" to, or part of the implementation of,
    # the class statement, so they don't stick around in the current
    # namespace.
    del __init__, val, d  # These are all "local" to or part of the imple
    

    【讨论】:

    • –1 名称并非无关紧要。
    • @val.setter 的调用等效于d['val'] = property(fget=d['val'], fset=val) 而不是d['val'].__set__ = val。这可能有助于解释。在这里,您可以使 getter 和 setter 的名称不同,这可能会使其更加清晰。但是 wim 是正确的,而不是像 OP 这样的类定义,getter 和 setter 必须具有相同的名称,因此最终只有一个属性包含两者。
    • 对;我的意思是在我完成答案后仔细检查。它似乎应该是无关紧要的:)
    • @AlexHall @val.setter 是否真的重新定义了属性,或者这只是您在这个伪 Python 中可以做的最接近的?
    • 它实际上创建了一个新属性。像type(self)(...) 这样的东西。我真的不明白为什么它需要这样做,而不是改变现有的属性,但这就是它的作用。 耸耸肩
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-11
    相关资源
    最近更新 更多