Qual é a diferença entre uma "propriedade" e um "atributo" do Python?


146

Em geral, estou confuso sobre a diferença entre uma "propriedade" e um "atributo" e não consigo encontrar um ótimo recurso para detalhar concisamente as diferenças.

Respostas:


184

Propriedades são um tipo especial de atributo. Basicamente, quando o Python encontra o seguinte código:

spam = SomeObject()
print(spam.eggs)

parece-se eggsem spam, em seguida, examina eggspara ver se ele tem uma __get__, __set__ou __delete__método - se isso acontecer, é uma propriedade. Se for uma propriedade, em vez de apenas retornar o eggsobjeto (como seria para qualquer outro atributo), ele chamará o __get__método (desde que estávamos fazendo pesquisa) e retornará o que esse método retornar.

Mais informações sobre o modelo de dados e descritores do Python .


Parece que as propriedades envolvem processamento adicional para acessar o valor-alvo ... você sabe o quanto é significativo / mais lento?
martineau

@martineau: Bem, há uma chamada de função extra, mas provavelmente o trabalho e o tempo extras dependerão do quanto a propriedade estiver fazendo (conselhos gerais: não use / use propriedades para ocultar a E / S).
precisa

56

Com uma propriedade, você tem controle total sobre seus métodos getter, setter e deleter, que você não possui (se não estiver usando advertências) com um atributo.

class A(object):
    _x = 0
    '''A._x is an attribute'''

    @property
    def x(self):
        '''
        A.x is a property
        This is the getter method
        '''
        return self._x

    @x.setter
    def x(self, value):
        """
        This is the setter method
        where I can check it's not assigned a value < 0
        """
        if value < 0:
            raise ValueError("Must be >= 0")
        self._x = value

>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
  File "ex.py", line 15, in <module>
    a.x = -1
  File "ex.py", line 9, in x
    raise ValueError("Must be >= 0")
ValueError: Must be >= 0

Isso ("controle completo") também pode ser feito com atributos "não-propriedade", apenas sem decoradores simples.

8
Eu gosto que esta resposta forneça um exemplo realista e útil. Sinto que muitas respostas neste site explicam desnecessariamente como as coisas funcionam no back-end sem esclarecer como o usuário deve interagir com elas. Se alguém não entende por que / quando usar alguma funcionalidade, não faz sentido saber como ela funciona nos bastidores.
Tom

Esta resposta viola o "Zen do Python - deve haver uma - e de preferência apenas uma - maneira óbvia de fazê-lo". Existem 2 maneiras de definir o valor x.
Tin Luu

@TinLuu - Existe apenas uma maneira de definir o valor de x. Também há apenas uma maneira de definir o valor de _x. O fato de serem a mesma coisa é um detalhe de implementação. O usuário sábio desta classe não usa _x.
iluminado

1
@TinLuu - Acho que estamos dizendo a mesma coisa de lados opostos da perspectiva. O usuário da classe deve ver apenas x. Mão única. Se o usuário da classe descobrir sobre _x, eles o usarão por sua conta e risco.
iluminado

19

Em termos gerais, uma propriedade e um atributo são a mesma coisa. No entanto, existe um decorador de propriedades no Python que fornece acesso ao getter / setter a um atributo (ou outros dados).

class MyObject(object):
    # This is a normal attribute
    foo = 1

    @property
    def bar(self):
        return self.foo

    @bar.setter
    def bar(self, value):
        self.foo = value


obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo

1
você também pode mencionar o resultado esperado desse código?
Hasan Iqbal

2
O que você quer dizer? Isso não está na parte inferior do código?
six8

12

A propriedade permite que você obtenha e defina valores como faria com atributos normais, mas abaixo existe um método chamado de conversão para um getter e setter para você. É realmente apenas uma conveniência reduzir o clichê de chamar getters e setters.

Digamos, por exemplo, que você teve uma aula que realizou algumas coordenadas xey para algo que você precisava. Para defini-los, você pode fazer algo como:

myObj.x = 5
myObj.y = 10

É muito mais fácil olhar e pensar do que escrever:

myObj.setX(5)
myObj.setY(10)

O problema é: e se um dia sua classe mudar de forma que você precise compensar x e y por algum valor? Agora você precisaria alterar sua definição de classe e todo o código que a chama, o que poderia ser realmente demorado e propenso a erros. A propriedade permite que você use a sintaxe anterior, oferecendo a flexibilidade de alterar o último.

No Python, você pode definir getters, setters e excluir métodos com a função de propriedade Se você deseja apenas a propriedade read, também existe um decorador @property que você pode adicionar acima do seu método.

http://docs.python.org/library/functions.html#property


Apenas responda que fazia sentido prático!
Dude156

8

Eu aprendi duas diferenças no site de Bernd Klein, em resumo:

1. Propriedade é uma maneira mais conveniente de encapsular dados.

ex: se você tem um atributo público de comprimento de objeto, mais tarde, seu projeto exige que você o encapsule, ou seja: mude para privado e forneça getter e setter => você precisará alterar muitos dos códigos que escreveu antes:

#Old codes
obj1.length=obj1.length+obj2.length
#New codes(Using private attibutes and getter and setter)
obj1.set_lenght(obj1.get_length()+obj2.get_length()) #=> this is ugly

Se você usar @property e @ lenght.setter =>, não precisará alterar esses códigos antigos

2. Uma propriedade pode encapsular vários atributos

class Person:
  def __init__(self, name, physic_health, mental_health):
    self.name=name
    self.__physic_health=physic_health #physic_health is real value in range [0, 5.0]
    self.__mental_health=mental_health #mental_health is real value in range [0, 5.0]
  @property
  def condition(self):
    health=self.__physic_health+self.__mental_health
    if(health<5.0):
      return "I feel bad!"
    elif health<8.0:
      return "I am ok!"
    else:
      return "Great!"

Neste exemplo, __physic_healthe __mental_healthsão privados e não podem ser acessados ​​diretamente de fora, a única maneira de a classe externa interagir com eles é através da propriedadecondition


8

Também há uma diferença não óbvia que eu uso para armazenar em cache ou atualizar dados; geralmente, temos uma função conectada ao atributo de classe. Por exemplo, eu preciso ler o arquivo uma vez e manter o conteúdo atribuído ao atributo para que o valor seja armazenado em cache:

class Misc():
        def __init__(self):
            self.test = self.test_func()

        def test_func(self):
            print 'func running'
            return 'func value'

cl = Misc()
print cl.test
print cl.test

Resultado:

func running
func value
func value

Acessamos o atributo duas vezes, mas nossa função foi acionada apenas uma vez. Alterar o exemplo acima para usar a propriedade fará com que o valor do atributo seja atualizado sempre que você o acessar:

class Misc():

    @property
    def test(self):
        print 'func running'
        return 'func value'

cl = Misc()
print cl.test
print cl.test

Resultado:

func running
func value
func running
func value
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.