为什么 getter/setter 方法的名称必须与原始 属性 的名称相同?

Why do getter/setter methods have to have the same name(s) as name of original property?

我不明白为什么 getter/setter 方法的名称必须与 属性.

的名称相同

我已经尝试阅读 Aaron Hall 的回答 here,但我仍然找不到(或没有找到)关于为什么我们必须使用各自名称的解释。

class Car(object):
    def __init__(self, color='blue'):
        self.color = color
        self.current_model = 'Opel'

    @property
    def model(self, new_model):
        return self.current_model 


    @model.setter
    def model(self, new_model): 
        if new_model == 'Audi':
            raise ValueError ('NO AUDI ALLOWED')
        else:
            self.current_model = new_model

    # model = model.setter(models) but doing this works

    @model.getter
    def model(self):
        return self.current_model 

编辑: 我感到困惑的是,如果我将我的方法重命名为:

@model.setter
def model_new(self, new_model): 
    if new_model == 'Audi':
        raise ValueError ('NO AUDI ALLOWED')
    else:
        self.current_model = new_model

然后我尝试 运行:

audi = Car()
audi.model = 'BMW' # will get AttributeError: can't set attribute

我预计这不起作用,因为 getter、setter 和删除方法创建了 属性 的副本,更改方法的名称就像修改不同 属性 的属性。就像做:models = model.setter(models).

但是我不确定我是否理解正确

简而言之:他们不必,但建议这样做。

如果我对你的问题的理解正确,你想知道为什么命名是个好习惯

class Car(object):
    def __init__(self, color='blue', model='Opel'):
        self.color = color
        self._current_model = model

    @property
    def model(self):                               #THIS FUNCTION...
        return self._current_model 

    @model.setter                                  
    def model(self, new_model):                    #...LIKE THIS FUNCTION
        if new_model == 'Audi':
            raise ValueError ('NO AUDI ALLOWED')
        else:
            self._current_model = new_model

这样做是个好主意,因为它避免了混淆,并且与第二种定义属性的方式一致。考虑这个片段:

class Car:

    def __init__(self, color='blue', model='Opel'):
        self.color = color
        self._current_model = model

    def g(self):
        print('Getting value')
        return self._current_model

    def s(self, model):
        print('Setting value')
        self._current_model = model

    def d(self):
        print('Deleting value')
        del self._current_model

    prop = property(g, s, d)

您为获取、设置、删除等定义一个单独的函数,并使用它们创建 属性 class 的实例。您现在可以通过

这些函数修改您的隐藏变量
my_car = Car();
my_car.prop = 'BMW'      #call the setter
print(my_car.prop)       #call the getter
del my_car.prop          #call the deleter

这很干净,你知道你正在使用的变量。作为反例,请看这个片段:

class Car:

    def __init__(self, color='blue', model='Opel'):
        self.color = color
        self._current_model = model

    @property
    def g(self):
        print('Getting value')
        return self._current_model

    @g.setter
    def s(self, model):
        print('Setting value')
        self._current_model = model

    @g.deleter                         # this could be s.deleter as well
    def d(self):
        print('Deleting value')
        del self._current_model

如您所见,与 属性 相比,您可能对设置器和删除器使用了不同的名称。如果您尝试做与以前相同的事情,您最终会得到难以理解的代码:

mycar = Car();
mycar.s = 'BMW'
print(mycar.g)
del mycar.d

你好像用了3个属性来修改隐藏变量。这会不必要地污染命名空间,通常不推荐使用。因此,坚持使用一个名称是有意义的,这与以前的 属性 定义一致。

函数名称需要相同,因为 @model.setter 不会更新现有的 属性;它 替换为基于现有 属性 的新版本。当您使用 属性 方法作为装饰器时,属性 的名称是最后装饰的函数的名称。

记住,

@model.setter
def model(self, new_model): 
    if new_model == 'Audi':
        raise ValueError ('NO AUDI ALLOWED')
    else:
        self.current_model = new_model

大致相当于

def tmp(self, new_model): 
    if new_model == 'Audi':
        raise ValueError ('NO AUDI ALLOWED')
    else:
        self.current_model = new_model

model = model.setter(tmp)

也就是说,def语句绑定的名称(model)改为绑定到model.setter的结果。如果您使用不同的名称,那么您已经更改了 属性.

的名称

最简单的方法是同时提供 getter(以及可选的 setter 和删除器):

def foo_get(self):
    ...

def foo_set(self, v):
    ...

foo = property(foo_get, foo_set)

因为创建 属性 只需要 getter,你可以单独指定它,然后创建一个新的 属性 结合旧的 setter .

foo = property(foo_get)
foo = foo.setter(foo_set)

装饰器语法允许您跳过显式命名 getter 和 setter:

# Instead of foo = property(foo_get), define a function named foo,
# pass it to property, and rebind the name foo to the new property
@property
def foo(self):
    ...

# Instead of foo.setter(foo_set), define *another* function named foo,
# pass it too foo.setter, and rebind the name foo to the new, augmented property. Note that this works because Python will evaluate `@foo.setter` to
# get a reference to the bound property method
# before it evaluates the following def statement.
@foo.setter
def foo(self, v):
    ...