简体   繁体   中英

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

I can't get my head around why do name of getter/setter methods have to have the same name fo the property.

I've tried reading Aaron Hall's answer here , but I still couldn't find an explanation (or missed it) about why we have to use the respective names.

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 

Edit: What I found confusing is that if I rename my method as:

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

And I try running:

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

I am expecting that this does not work because getter, setter, and deleter methods create a copy of the property, and changing the name of the method is like modifying attributes of a different property. It is like doing: models = model.setter(models).

But i am not sure if I get this correctly

In short: they do not have to, but it is recommended.

If I understand your question correctly, you want to know why it is good practice to name

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

It is a good idea to do this because it avoids confusion and is consistent with the second way to define properties. Consider this snippet:

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)

You define a separate function for getting, setting, deleting, etc. and use them to create an instance of the property class. You can now modify your hidden variable through these functions like

my_car = Car();
my_car.prop = 'BMW'      #call the setter
print(my_car.prop)       #call the getter
del my_car.prop          #call the deleter

This is clean, you know the variable you are working with. As a counterexample, look at this snippet:

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

As you can see, it is possible that you use different names for the setters and deleters compared to the property. If you try to do the same things as before, you end up with hard-to-understand-code:

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

You seemingly use 3 attributes to modify the hidden variable. This pollutes the namespace unnecessarily and is generally not recommended. Therefore it makes sense to stick to a single name, which is consistent with the former property definition.

The function name needs to be the same because @model.setter doesn't update an existing property; it replaces it with a new property based on the existing one. When you use the property methods as decorators, the name of the property is the name of the function that gets decorated last.

Remember,

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

is roughly equivalent to

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

model = model.setter(tmp)

That is, the name bound by the def statement ( model ) is bound to the result of model.setter instead. If you use a different name, then you've changed the name of the property.


The straightforward way is to provide the getter (and optional setter and deleter) all at the same time:

def foo_get(self):
    ...

def foo_set(self, v):
    ...

foo = property(foo_get, foo_set)

Since only the getter is necessary to create the property, you can specify it alone, then create a new property combining the old one with a setter.

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

The decorator syntax allows you to skip naming the getters and setters explicitly:

# 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):
    ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM