Comment fonctionne le décorateur @property?


982

Je voudrais comprendre comment fonctionne la propertyfonction intégrée . Ce qui m'embrouille, c'est qu'il propertypeut également être utilisé comme décorateur, mais il ne prend des arguments que lorsqu'il est utilisé comme fonction intégrée et non lorsqu'il est utilisé comme décorateur.

Cet exemple provient de la documentation :

class C(object):
    def __init__(self):
        self._x = None

    def getx(self):
        return self._x
    def setx(self, value):
        self._x = value
    def delx(self):
        del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

propertyLes arguments « sont getx, setx, delxet une chaîne de doc.

Dans le code ci property- dessous est utilisé comme décorateur. Son objet est la xfonction, mais dans le code ci-dessus, il n'y a pas de place pour une fonction objet dans les arguments.

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Et comment les décorateurs x.setteret sont-ils x.deletercréés? Je suis confus.



3
propertyest en fait une classe (pas une fonction), bien qu'elle appelle probablement la __init__()méthode lorsque vous créez un objet, bien sûr. L'utilisation à help(property)partir du terminal est utile. helpest également une classe pour une raison quelconque.
Brōtsyorfuzthrāx

Je pense que ce lien fournit un bon exemple: [propriété] ( journaldev.com/14893/python-property-decorator )
Sheng Bi

4
@Shule thread de 2 ans, mais quand même: Tout est une classe. Même des cours.
Artemis ne fait toujours pas confiance au SE

2
Cela me déroutait aussi. J'ai finalement trouvé un article qui a pu le décomposer pour moi. J'espère que ça aidera quelqu'un d'autre. programiz.com/python-programming/property Je ne suis en aucun cas affilié au site.
jjwdesign

Réponses:


1013

La property()fonction renvoie un objet descripteur spécial :

>>> property()
<property object at 0x10ff07940>

C'est cet objet qui a des méthodes supplémentaires :

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

Ceux - ci agissent comme décorateurs aussi . Ils retournent un nouvel objet de propriété:

>>> property().getter(None)
<property object at 0x10ff079f0>

c'est une copie de l'ancien objet, mais avec l'une des fonctions remplacées.

Souvenez-vous que la @decoratorsyntaxe n'est que du sucre syntaxique; la syntaxe:

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

signifie vraiment la même chose que

def foo(self): return self._foo
foo = property(foo)

de sorte que foola fonction est remplacée parproperty(foo) , que nous avons vu ci-dessus est un objet spécial. Ensuite, lorsque vous utilisez @foo.setter(), ce que vous faites, c'est appeler la property().setterméthode que je vous ai montrée ci-dessus, qui renvoie une nouvelle copie de la propriété, mais cette fois avec la fonction setter remplacée par la méthode décorée.

La séquence suivante crée également une propriété complète, à l'aide de ces méthodes de décoration.

Nous créons d'abord quelques fonctions et un propertyobjet avec juste un getter:

>>> def getter(self): print('Get!')
... 
>>> def setter(self, value): print('Set to {!r}!'.format(value))
... 
>>> def deleter(self): print('Delete!')
... 
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Ensuite, nous utilisons la .setter()méthode pour ajouter un setter:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Enfin, nous ajoutons un deleter avec la .deleter()méthode:

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Last but not least, l' propertyobjet agit comme un objet descripteur , il a donc .__get__(), .__set__()et les .__delete__()méthodes de crochet en attribut d'instance d' obtenir, le réglage et la suppression:

>>> class Foo: pass
... 
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

Le Descriptor Howto comprend un exemple d'implémentation Python pur du property()type:

class Property:
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

11
Très bien. Vous pouvez ajouter le fait qu'après Foo.prop = propvous pouvez faire Foo().prop = 5; pront Foo().prop; del Foo().propavec le résultat souhaité.
glglgl

12
Les objets de méthode sont créés à la volée et peuvent réutiliser le même emplacement de mémoire si disponible.
Martijn Pieters

1
@MarkusMeskanen: J'utilise plutôt type()car l'accès aux attributs et aux méthodes sont destinés à être utilisés comme points d'extension par les fonctions et opérateurs standard.
Martijn Pieters

2
@MarkusMeskanen: parce que l'objet est immuable, et si vous le mutiez en place, vous ne pourriez pas le spécialiser dans une sous-classe.
Martijn Pieters

5
@MarkusMeskanen: voir Python surchargeant getter sans setter ; s'il @human.name.gettermodifiait l' propertyobjet en place plutôt que de renvoyer un nouveau, l' human.nameattribut serait modifié, changeant le comportement de cette superclasse.
Martijn Pieters

202

La documentation indique que ce n'est qu'un raccourci pour créer des propriétés en lecture seule. Donc

@property
def x(self):
    return self._x

est équivalent à

def getx(self):
    return self._x
x = property(getx)

20
Le contexte complet (réponse la plus votée) est bon, mais cette réponse était pratiquement utile pour comprendre pourquoi quelqu'un d'autre avait utilisé @propertycomme décorateur dans sa classe.
ijoseph

1
@property peut également être utilisé lorsque vous souhaitez ajouter un attribut à une classe et devez conserver la compatibilité avec les objets précédemment créés de cette classe (par exemple, qui peuvent être enregistrés dans un fichier pickle).
AndyP

112

Voici un exemple minimal de la façon dont @propertypeut être mis en œuvre:

class Thing:
    def __init__(self, my_word):
        self._word = my_word 
    @property
    def word(self):
        return self._word

>>> print( Thing('ok').word )
'ok'

Sinon wordreste une méthode au lieu d'une propriété.

class Thing:
    def __init__(self, my_word):
        self._word = my_word
    def word(self):
        return self._word

>>> print( Thing('ok').word() )
'ok'

1
À quoi ressemblerait cet exemple si la fonction / propriété word () devait être définie dans init ?
JJ

5
Quelqu'un peut-il expliquer pourquoi je self.word = my_wordprint( Thing('ok').word ) = 'ok'
créerais

1
@SilverSlash Ceci n'est qu'un exemple simple, un vrai cas d'utilisation impliquerait une méthode plus compliquée
AlexG

pouvez-vous s'il vous plaît m'expliquer comment l'impression Thing('ok').wordappelle la fonction en interne lors de l'exécution?
Vicrobot

83

La première partie est simple:

@property
def x(self): ...

est le même que

def x(self): ...
x = property(x)
  • qui, à son tour, est la syntaxe simplifiée pour créer un propertyavec juste un getter.

L'étape suivante serait d'étendre cette propriété avec un setter et un deleter. Et cela se produit avec les méthodes appropriées:

@x.setter
def x(self, value): ...

renvoie une nouvelle propriété qui hérite de tout de l'ancien xplus du setter donné.

x.deleter fonctionne de la même manière.


49

Ce qui suit:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Est le même que:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, _x_set, _x_del, 
                    "I'm the 'x' property.")

Est le même que:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x

    def _x_set(self, value):
        self._x = value

    def _x_del(self):
        del self._x

    x = property(_x_get, doc="I'm the 'x' property.")
    x = x.setter(_x_set)
    x = x.deleter(_x_del)

Est le même que:

class C(object):
    def __init__(self):
        self._x = None

    def _x_get(self):
        return self._x
    x = property(_x_get, doc="I'm the 'x' property.")

    def _x_set(self, value):
        self._x = value
    x = x.setter(_x_set)

    def _x_del(self):
        del self._x
    x = x.deleter(_x_del)

C'est la même chose que:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

4
Les premier et dernier exemples de code sont identiques (textuellement).
Adomas Baliuka

48

Ci-dessous est un autre exemple sur la façon dont @propertypeut aider quand on doit refactoriser le code qui est pris à partir d' ici (je le résume seulement ci-dessous):

Imaginez que vous avez créé une classe Moneycomme celle-ci:

class Money:
    def __init__(self, dollars, cents):
        self.dollars = dollars
        self.cents = cents

et un utilisateur crée une bibliothèque en fonction de cette classe où il / elle utilise par exemple

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

Supposons maintenant que vous décidiez de changer votre Moneyclasse et de vous débarrasser des attributs dollarset centsmais décidez plutôt de suivre uniquement le montant total de cents:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

Si l'utilisateur mentionné ci-dessus essaie maintenant d'exécuter sa bibliothèque comme auparavant

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))

cela entraînera une erreur

AttributeError: l'objet 'Money' n'a pas d'attribut 'dollars'

Cela signifie que maintenant, tous ceux qui s'appuient sur votre Moneyclasse d' origine devraient changer toutes les lignes de code où dollarset qui centssont utilisées, ce qui peut être très douloureux ... Alors, comment cela pourrait-il être évité? En utilisant @property!

Voilà comment:

class Money:
    def __init__(self, dollars, cents):
        self.total_cents = dollars * 100 + cents

    # Getter and setter for dollars...
    @property
    def dollars(self):
        return self.total_cents // 100

    @dollars.setter
    def dollars(self, new_dollars):
        self.total_cents = 100 * new_dollars + self.cents

    # And the getter and setter for cents.
    @property
    def cents(self):
        return self.total_cents % 100

    @cents.setter
    def cents(self, new_cents):
        self.total_cents = 100 * self.dollars + new_cents

quand nous appelons maintenant de notre bibliothèque

money = Money(27, 12)

print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 27 dollar and 12 cents.

cela fonctionnera comme prévu et nous n'avons pas eu à changer une seule ligne de code dans notre bibliothèque! En fait, nous n'aurions même pas besoin de savoir que la bibliothèque dont nous dépendons a changé.

Aussi, ça settermarche bien:

money.dollars += 2
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 12 cents.

money.cents += 10
print("I have {} dollar and {} cents.".format(money.dollars, money.cents))
# prints I have 29 dollar and 22 cents.

Vous pouvez @propertyégalement utiliser dans les classes abstraites; Je donne ici un exemple minimal .


votre résumé est très bon, l'exemple que prend le site Web est un peu étrange .. Un débutant demanderait .. pourquoi ne pouvons-nous pas simplement nous en tenir à self.dollar = dollars? nous avons fait tellement de choses avec @property, mais il semble qu'aucune fonctionnalité d'extraction ne soit ajoutée.
Sheng Bi

1
@ShengBi: Ne vous concentrez pas autant sur l'exemple réel mais plus sur le principe sous-jacent: si - pour une raison quelconque - vous devez refactoriser le code, vous pouvez le faire sans affecter le code de quelqu'un d'autre.
Cleb

21

J'ai lu tous les articles ici et j'ai réalisé que nous pourrions avoir besoin d'un exemple réel. Pourquoi, en fait, nous avons @property? Considérez donc une application Flask dans laquelle vous utilisez un système d'authentification. Vous déclarez un utilisateur modèle dans models.py:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(64), unique=True, index=True)
    username = db.Column(db.String(64), unique=True, index=True)
    password_hash = db.Column(db.String(128))

    ...

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

Dans ce code, nous avons "caché" l'attribut passworden utilisant @propertyce qui déclenche l' AttributeErrorassertion lorsque vous essayez d'y accéder directement, tandis que nous avons utilisé @ property.setter pour définir la variable d'instance réelle password_hash.

Maintenant, auth/views.pynous pouvons instancier un utilisateur avec:

...
@auth.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,
                    username=form.username.data,
                    password=form.password.data)
        db.session.add(user)
        db.session.commit()
...

Remarquez l'attribut passwordqui provient d'un formulaire d'inscription lorsqu'un utilisateur remplit le formulaire. La confirmation du mot de passe se produit sur le front-end avec EqualTo('password', message='Passwords must match')(au cas où vous vous poseriez la question, mais il s'agit d'un formulaire différent lié aux formulaires Flask).

J'espère que cet exemple vous sera utile


18

Ce point a été effacé par beaucoup de gens là-haut, mais voici un point direct que je cherchais. C'est ce qui me semble important pour commencer avec le décorateur @property. par exemple:-

class UtilityMixin():
    @property
    def get_config(self):
        return "This is property"

L'appel de la fonction "get_config ()" fonctionnera comme ceci.

util = UtilityMixin()
print(util.get_config)

Si vous remarquez que je n'ai pas utilisé de crochets "()" pour appeler la fonction. C'est la chose de base que je cherchais pour le décorateur @property. Pour que vous puissiez utiliser votre fonction comme une variable.


1
point très utile qui permet de condenser ce concept abstrait.
Info5ek

18

Commençons par les décorateurs Python.

Un décorateur Python est une fonction qui permet d'ajouter des fonctionnalités supplémentaires à une fonction déjà définie.

En Python, tout est un objet. Les fonctions en Python sont des objets de première classe, ce qui signifie qu'elles peuvent être référencées par une variable, ajoutées dans les listes, passées comme arguments à une autre fonction, etc.

Considérez l'extrait de code suivant.

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

def say_bye():
    print("bye!!")

say_bye = decorator_func(say_bye)
say_bye()

# Output:
#  Wrapper function started
#  bye
#  Given function decorated

Ici, nous pouvons dire que la fonction décorateur a modifié notre fonction say_hello et y a ajouté quelques lignes de code supplémentaires.

Syntaxe Python pour décorateur

def decorator_func(fun):
    def wrapper_func():
        print("Wrapper function started")
        fun()
        print("Given function decorated")
        # Wrapper function add something to the passed function and decorator 
        # returns the wrapper function
    return wrapper_func

@decorator_func
def say_bye():
    print("bye!!")

say_bye()

Let's Conclu tout qu'avec un scénario de cas, mais avant cela, parlons de quelques principes Oups.

Les getters et setters sont utilisés dans de nombreux langages de programmation orientés objet pour garantir le principe de l'encapsulation des données (est considéré comme le regroupement des données avec les méthodes qui opèrent sur ces données.)

Ces méthodes sont bien sûr le getter pour récupérer les données et le setter pour changer les données.

Selon ce principe, les attributs d'une classe sont rendus privés pour les cacher et les protéger des autres codes.

Ouais , @property est fondamentalement une manière pythonique d'utiliser les getters et les setters.

Python a un grand concept appelé propriété qui rend la vie d'un programmeur orienté objet beaucoup plus simple.

Supposons que vous décidiez de créer une classe capable de stocker la température en degrés Celsius.

class Celsius:
def __init__(self, temperature = 0):
    self.set_temperature(temperature)

def to_fahrenheit(self):
    return (self.get_temperature() * 1.8) + 32

def get_temperature(self):
    return self._temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    self._temperature = value

Code refactorisé, voici comment nous aurions pu le réaliser avec propriété.

En Python, property () est une fonction intégrée qui crée et renvoie un objet de propriété.

Un objet de propriété a trois méthodes, getter (), setter () et delete ().

class Celsius:
def __init__(self, temperature = 0):
    self.temperature = temperature

def to_fahrenheit(self):
    return (self.temperature * 1.8) + 32

def get_temperature(self):
    print("Getting value")
    return self.temperature

def set_temperature(self, value):
    if value < -273:
        raise ValueError("Temperature below -273 is not possible")
    print("Setting value")
    self.temperature = value

temperature = property(get_temperature,set_temperature)

Ici,

temperature = property(get_temperature,set_temperature)

aurait pu être décomposé car,

# make empty property
temperature = property()
# assign fget
temperature = temperature.getter(get_temperature)
# assign fset
temperature = temperature.setter(set_temperature)

Point à noter:

  • get_temperature reste une propriété au lieu d'une méthode.

Vous pouvez maintenant accéder à la valeur de la température en écrivant.

C = Celsius()
C.temperature
# instead of writing C.get_temperature()

Nous pouvons continuer et ne pas définir les noms get_temperature et set_temperature car ils sont inutiles et polluent l'espace de noms de classe.

La manière pythonique de traiter le problème ci-dessus consiste à utiliser @property .

class Celsius:
    def __init__(self, temperature = 0):
        self.temperature = temperature

    def to_fahrenheit(self):
        return (self.temperature * 1.8) + 32

    @property
    def temperature(self):
        print("Getting value")
        return self.temperature

    @temperature.setter
    def temperature(self, value):
        if value < -273:
            raise ValueError("Temperature below -273 is not possible")
        print("Setting value")
        self.temperature = value

Points à noter -

  1. Une méthode utilisée pour obtenir une valeur est décorée avec "@property".
  2. La méthode qui doit fonctionner comme le passeur est décorée avec "@ temperature.setter", Si la fonction avait été appelée "x", il faudrait la décorer avec "@ x.setter".
  3. Nous avons écrit "deux" méthodes avec le même nom et un nombre différent de paramètres "def temperature (self)" et "def temperature (self, x)".

Comme vous pouvez le voir, le code est nettement moins élégant.

Maintenant, parlons d'un scénario pratique réel.

Disons que vous avez conçu une classe comme suit:

class OurClass:

    def __init__(self, a):
        self.x = a


y = OurClass(10)
print(y.x)

Supposons maintenant que notre classe est devenue populaire parmi les clients et qu'ils ont commencé à l'utiliser dans leurs programmes. Ils ont fait toutes sortes d'affectations à l'objet.

Et un jour fatidique, un client de confiance est venu vers nous et a suggéré que "x" doit être une valeur entre 0 et 1000, c'est vraiment un scénario horrible!

En raison des propriétés, c'est facile: nous créons une version de propriété de "x".

class OurClass:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

C'est super, n'est-ce pas: vous pouvez commencer avec l'implémentation la plus simple imaginable, et vous êtes libre de migrer plus tard vers une version de propriété sans avoir à changer l'interface! Les propriétés ne remplacent donc pas seulement les getters et les setter!

Vous pouvez vérifier cette implémentation ici


2
Votre classe Celsius va être récursivement infinie lors de la configuration (ce qui signifie lors de l'instanciation).
Ted Petrou

1
@Ted Petrou Je ne t'ai pas eu? Comment cela va-t-il se répéter à l'infini lors du réglage?
Divyanshu Rawat

Ce n'est en fait pas clair ... les gens demandent pourquoi, mais l'exemple n'est pas convaincant ...
Sheng Bi

1
C'est juste un commentaire, mon opinion personnelle. Votre réponse pourrait être vraiment bonne. alors laisse tomber.
Sheng Bi

1
par rapport aux meilleures réponses votées, celle-ci est conçue pour les humains; Merci.
Info5ek

6

property est une classe derrière @property décorateur.

Vous pouvez toujours vérifier ceci:

print(property) #<class 'property'>

J'ai réécrit l'exemple de help(property)pour montrer que la @propertysyntaxe

class C:
    def __init__(self):
        self._x=None

    @property 
    def x(self):
        return self._x

    @x.setter 
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

c = C()
c.x="a"
print(c.x)

est fonctionnellement identique à la property()syntaxe:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, v):
        self._x = v

    def d(self):
        del self._x

    prop = property(g,s,d)

c = C()
c.x="a"
print(c.x)

Il n'y a aucune différence sur la façon dont nous utilisons la propriété comme vous pouvez le voir.

Pour répondre à la question, le @propertydécorateur est implémenté via la propertyclasse.


Donc, la question est d'expliquer propertyun peu la classe. Cette ligne:

prop = property(g,s,d)

C'était l'initialisation. Nous pouvons le réécrire comme ceci:

prop = property(fget=g,fset=s,fdel=d)

Le sens de fget, fsetet fdel:

 |    fget
 |      function to be used for getting an attribute value
 |    fset
 |      function to be used for setting an attribute value
 |    fdel
 |      function to be used for del'ing an attribute
 |    doc
 |      docstring

L'image suivante montre les triplets que nous avons, de la classe property:

entrez la description de l'image ici

__get__, __set__Et __delete__sont là pour être surchargée . Il s'agit de l'implémentation du modèle de descripteur en Python.

En général, un descripteur est un attribut d'objet avec un «comportement de liaison», dont l'attribut d'accès a été outrepassé par des méthodes du protocole de descripteur.

Nous pouvons également utiliser la propriété setter, getteret des deleterméthodes pour lier la fonction à la propriété. Vérifiez l'exemple suivant. La méthode s2de la classe Cmettra la propriété doublée .

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x


    x=property(g)
    x=x.setter(s)
    x=x.deleter(d)      


c = C()
c.x="a"
print(c.x) # outputs "a"

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x) # outputs "aa"

1

Une propriété peut être déclarée de deux manières.

  • Création des méthodes getter et setter pour un attribut, puis transmission de celles-ci comme argument à la fonction de propriété
  • Utilisation du décorateur @property .

Vous pouvez jeter un œil à quelques exemples que j'ai écrits sur les propriétés en python .


pouvez-vous mettre à jour votre réponse en disant que la propriété est une classe afin que je puisse voter.
prosti


0

Voici un autre exemple:

##
## Python Properties Example
##
class GetterSetterExample( object ):
    ## Set the default value for x ( we reference it using self.x, set a value using self.x = value )
    __x = None


##
## On Class Initialization - do something... if we want..
##
def __init__( self ):
    ## Set a value to __x through the getter / setter... Since __x is defined above, this doesn't need to be set...
    self.x = 1234

    return None


##
## Define x as a property, ie a getter - All getters should have a default value arg, so I added it - it will not be passed in when setting a value, so you need to set the default here so it will be used..
##
@property
def x( self, _default = None ):
    ## I added an optional default value argument as all getters should have this - set it to the default value you want to return...
    _value = ( self.__x, _default )[ self.__x == None ]

    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Get x = ' + str( _value ) )

    ## Return the value - we are a getter afterall...
    return _value


##
## Define the setter function for x...
##
@x.setter
def x( self, _value = None ):
    ## Debugging - so you can see the order the calls are made...
    print( '[ Test Class ] Set x = ' + str( _value ) )

    ## This is to show the setter function works.... If the value is above 0, set it to a negative value... otherwise keep it as is ( 0 is the only non-negative number, it can't be negative or positive anyway )
    if ( _value > 0 ):
        self.__x = -_value
    else:
        self.__x = _value


##
## Define the deleter function for x...
##
@x.deleter
def x( self ):
    ## Unload the assignment / data for x
    if ( self.__x != None ):
        del self.__x


##
## To String / Output Function for the class - this will show the property value for each property we add...
##
def __str__( self ):
    ## Output the x property data...
    print( '[ x ] ' + str( self.x ) )


    ## Return a new line - technically we should return a string so it can be printed where we want it, instead of printed early if _data = str( C( ) ) is used....
    return '\n'

##
##
##
_test = GetterSetterExample( )
print( _test )

## For some reason the deleter isn't being called...
del _test.x

Fondamentalement, le même que l'exemple C (objet), sauf que j'utilise x à la place ... Je n'initialise pas non plus dans __init - ... eh bien ... je le fais, mais il peut être supprimé car __x est défini comme faisant partie de la classe....

La sortie est:

[ Test Class ] Set x = 1234
[ Test Class ] Get x = -1234
[ x ] -1234

et si je commente self.x = 1234 en init alors la sortie est:

[ Test Class ] Get x = None
[ x ] None

et si je définit _default = None sur _default = 0 dans la fonction getter (car tous les getters devraient avoir une valeur par défaut mais elle n'est pas transmise par les valeurs de propriété de ce que j'ai vu afin que vous puissiez la définir ici, et ce n'est vraiment pas mauvais car vous pouvez définir la valeur par défaut une fois et l'utiliser partout) ie: def x (self, _default = 0):

[ Test Class ] Get x = 0
[ x ] 0

Remarque: La logique getter est là juste pour que la valeur soit manipulée par elle pour s'assurer qu'elle est manipulée par elle - la même chose pour les instructions d'impression ...

Remarque: Je suis habitué à Lua et je peux créer dynamiquement 10+ assistants lorsque j'appelle une seule fonction et j'ai créé quelque chose de similaire pour Python sans utiliser de propriétés et cela fonctionne dans une certaine mesure, mais, même si les fonctions sont créées avant étant utilisé, il y a toujours des problèmes avec leur appel avant d'être créé, ce qui est étrange car il n'est pas codé de cette façon ... Je préfère la flexibilité des méta-tables Lua et le fait que je puisse utiliser de vrais setters / getters au lieu d'accéder directement à une variable ... j'aime la rapidité avec laquelle certaines choses peuvent être construites avec Python - par exemple les programmes gui. bien que celle que je conçois ne soit pas possible sans beaucoup de bibliothèques supplémentaires - si je la code dans AutoHotkey, je peux accéder directement aux appels dll dont j'ai besoin, et la même chose peut être faite en Java, C #, C ++,

Remarque: La sortie de code dans ce forum est cassée - j'ai dû ajouter des espaces à la première partie du code pour que cela fonctionne - lors du copier / coller, assurez-vous de convertir tous les espaces en onglets .... J'utilise des onglets pour Python car dans un fichier de 10 000 lignes, la taille du fichier peut être de 512 Ko à 1 Mo avec des espaces et de 100 à 200 Ko avec des tabulations, ce qui équivaut à une énorme différence de taille de fichier et à une réduction du temps de traitement ...

Les onglets peuvent également être ajustés par utilisateur - donc si vous préférez une largeur de 2 espaces, 4, 8 ou tout ce que vous pouvez faire, cela est utile pour les développeurs ayant des déficits de vue.

Remarque: Toutes les fonctions définies dans la classe ne sont pas mises en retrait correctement en raison d'un bogue dans le logiciel du forum - assurez-vous de le mettre en retrait si vous copiez / collez


-3

Une remarque: pour moi, pour Python 2.x, @propertyne fonctionnait pas comme annoncé quand je n'ai pas hérité de object:

class A():
    pass

mais a fonctionné quand:

class A(object):
    pass

pour Python 3, a toujours fonctionné.


5
En effet, dans Python 2, une classe qui n'hérite pas objectest une classe à l'ancienne et les classes à l'ancienne ne prennent pas en charge le protocole de descripteur (qui est ce qui propertyimplémente pour fonctionner comme il le fait). En Python 3, les classes à l'ancienne n'existent plus; toutes les classes sont ce que nous
appelions des
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.