Quelle est la différence entre une «propriété» et un «attribut» Python?

146

Je suis généralement confus au sujet de la différence entre une «propriété» et un «attribut», et je ne peux pas trouver une excellente ressource pour détailler de manière concise les différences.

Carson
la source

Réponses:

184

Les propriétés sont un type particulier d'attribut. Fondamentalement, lorsque Python rencontre le code suivant:

spam = SomeObject()
print(spam.eggs)

il recherche eggsdans spamet examine ensuite eggspour voir si elle a __get__, __set__ou __delete__méthode - si elle le fait, il est une propriété. S'il s'agit d' une propriété, au lieu de simplement renvoyer l' eggsobjet (comme pour tout autre attribut), il appellera la __get__méthode (puisque nous faisions la recherche) et retournera tout ce que cette méthode renvoie.

Plus d'informations sur le modèle de données et les descripteurs de Python .

Ethan Furman
la source
Il semble que les propriétés impliquent un traitement supplémentaire pour accéder à la valeur cible ... savez-vous à quel point c'est significatif / beaucoup plus lent?
martineau
@martineau: Eh bien, il y a un appel de fonction supplémentaire, mais très probablement le travail et le temps supplémentaires dépendront de combien la propriété fait (conseil général: ne pas utiliser / ne pas utiliser les propriétés pour masquer les E / S).
Ethan Furman
56

Avec une propriété, vous avez un contrôle complet sur ses méthodes getter, setter et deleter, que vous n'avez pas (si vous n'utilisez pas de mises en garde) avec un attribut.

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
neurino
la source
Ceci ("contrôle complet") peut également être fait avec des attributs "non-propriété", mais sans ces simples décorateurs.
8
J'aime que cette réponse fournisse un exemple réaliste et utile. Je pense que trop de réponses sur ce site expliquent inutilement comment les choses fonctionnent sur le back-end sans clarifier comment l'utilisateur doit interagir avec elles. Si l'on ne comprend pas pourquoi / quand utiliser certaines fonctionnalités, il est inutile de savoir comment elles fonctionnent dans les coulisses.
Tom
Cette réponse viole le "Zen of Python - Il devrait y avoir une - et de préférence une seule - façon évidente de le faire". Il existe 2 façons de définir la valeur x.
Tin Luu
@TinLuu - Il n'y a qu'une seule façon de définir la valeur de x. Il n'y a également qu'une seule façon de définir la valeur de _x. Le fait qu'ils soient la même chose est un détail de mise en œuvre. L'utilisateur avisé de cette classe n'utilise pas _x.
éclairé le
1
@TinLuu - Je pense que nous disons tous les deux la même chose des extrémités opposées de la perspective. L'utilisateur de la classe ne doit voir que x. Une manière. Si l'utilisateur de la classe découvre _x, il l'utilise à ses propres risques.
éclairé le
19

En termes généraux, une propriété et un attribut sont la même chose. Cependant, il existe un décorateur de propriété en Python qui fournit un accès getter / setter à un attribut (ou à d'autres données).

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
six8
la source
1
pouvez-vous également mentionner le résultat attendu de ce code?
Hasan Iqbal le
2
Que voulez-vous dire? N'est-ce pas au bas du code?
six8
12

La propriété vous permet d'obtenir et de définir des valeurs comme vous le feriez avec des attributs normaux, mais en dessous se trouve une méthode appelée pour la traduire en un getter et un setter pour vous. C'est vraiment juste une commodité de réduire le passe-partout de l'appel des getters et des setters.

Disons, par exemple, que vous aviez une classe qui contenait des coordonnées x et y pour quelque chose dont vous aviez besoin. Pour les définir, vous voudrez peut-être faire quelque chose comme:

myObj.x = 5
myObj.y = 10

C'est beaucoup plus facile à regarder et à penser que d'écrire:

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

Le problème est, que se passe-t-il si un jour votre classe change de telle sorte que vous devez compenser vos x et y d'une certaine valeur? Maintenant, vous devez entrer et modifier la définition de votre classe et tout le code qui l'appelle, ce qui pourrait prendre beaucoup de temps et être source d'erreurs. La propriété vous permet d'utiliser la première syntaxe tout en vous offrant la flexibilité de changement de la seconde.

En Python, vous pouvez définir des getters, des setters et des méthodes de suppression avec la fonction de propriété. Si vous voulez juste la propriété read, il existe également un décorateur @property que vous pouvez ajouter au-dessus de votre méthode.

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

falcojr
la source
Seule réponse qui avait du sens pratique!
Dude156
8

J'ai appris 2 différences sur le site de Bernd Klein, en résumé:

1. La propriété est un moyen plus pratique d'encapsuler des données.

ex: Si vous avez un attribut public de longueur Object, plus tard, votre projet vous demandera de l'encapsuler, c'est-à-dire: changez-le en privé et fournissez getter et setter => vous devez changer de nombreux codes que vous avez écrits auparavant:

#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

Si vous utilisez @property et @ lenght.setter => vous n'avez pas besoin de changer ces anciens codes

2. Une propriété peut encapsuler plusieurs attributs

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!"

Dans cet exemple, __physic_healthet __mental_healthsont privés et ne sont pas accessibles directement de l'extérieur, le seul moyen d'interagir avec les classes extérieures est via la propriétécondition

Tin Luu
la source
8

Il y a aussi une différence non évidente que j'utilise pour mettre en cache ou actualiser les données, souvent nous avons une fonction connectée à l'attribut de classe. Par exemple, je dois lire le fichier une fois et conserver le contenu attribué à l'attribut afin que la valeur soit mise en 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

Production:

func running
func value
func value

Nous avons accédé à l'attribut deux fois mais notre fonction n'a été déclenchée qu'une seule fois. La modification de l'exemple ci-dessus pour utiliser la propriété entraînera l'actualisation de la valeur de l'attribut chaque fois que vous y accédez:

class Misc():

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

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

Production:

func running
func value
func running
func value
brc
la source