85 votes

Quand et comment utiliser la fonction intégrée property() en python ?

Il me semble qu'à l'exception d'un peu de sucre syntaxique, property() ne fait rien de bon.

Bien sûr, c'est bien d'être capable d'écrire a.b=2 au lieu de a.setB(2) mais cacher le fait que a.b=2 n'est pas une simple affectation semble être une recette pour des problèmes, soit parce qu'un résultat inattendu peut se produire, tel que a.b=2 cause réellement a.b pour être 1 . Ou une exception est levée. Ou un problème de performance. Ou tout simplement une confusion.

Pouvez-vous me donner un exemple concret d'une bonne utilisation de ce système ? (l'utiliser pour patcher un code problématique ne compte pas ;-)

150voto

Alex Martelli Points 330805

Dans les langages qui reposent sur les getters et setters, comme Java, ils ne sont pas censés faire autre chose que ce qu'ils disent. x.getB() n'a rien fait d'autre que de retourner la valeur actuelle de l'attribut logique b ou si x.setB(2) n'a rien fait d'autre que la petite quantité de travail interne nécessaire pour faire x.getB() retourner 2 .

Cependant, il n'y a pas d'obligation linguistique garanties sur ce comportement attendu, c'est-à-dire les contraintes imposées par le compilateur sur le corps des méthodes dont le nom commence par get o set Il s'agit plutôt de faire appel au bon sens, aux conventions sociales, aux "guides de style" et aux tests.

Le comportement de x.b les accès et les affectations tels que x.b = 2 Dans les langages qui ont des propriétés (un ensemble de langages qui inclut Python, mais qui n'est pas limité à celui-ci), l'utilisation des propriétés est la suivante exactement comme pour les méthodes getter et setter dans, par exemple, Java : les mêmes attentes, le même manque de garanties renforcées par le langage.

La première victoire des propriétés est la syntaxe et la lisibilité. Devoir écrire, par exemple ,

x.setB(x.getB() + 1)

au lieu de l'évident

x.b += 1

crie vengeance aux dieux. Dans les langages qui prennent en charge les propriétés, il n'y a absolument aucune raison de forcer les utilisateurs de la classe à passer par les girations d'un tel texte byzantin, ce qui a un impact sur la lisibilité de leur code sans le moindre avantage.

En Python, l'utilisation de propriétés (ou d'autres descripteurs) à la place de getters et setters présente un autre grand avantage : si et quand vous réorganisez votre classe de manière à ce que le setter et le getter sous-jacents ne soient plus nécessaires, vous pouvez (sans rompre l'API publiée de la classe) simplement éliminer ces méthodes et la propriété qui s'appuie sur elles, ce qui fait que b un attribut normal "stocké" de x plutôt qu'une classe "logique" obtenue et définie par calcul.

En Python, faire les choses directement (quand c'est possible) au lieu de passer par des méthodes est une optimisation importante, et l'utilisation systématique des propriétés vous permet d'effectuer cette optimisation quand c'est possible (en exposant toujours directement les "attributs stockés normaux", et seulement ceux qui nécessitent un calcul lors de l'accès et/ou de la configuration via des méthodes et des propriétés).

Ainsi, si vous utilisez des getters et setters au lieu de propriétés, en plus d'avoir un impact sur la lisibilité du code de vos utilisateurs, vous êtes également gaspiller gratuitement des cycles de machine (et l'énergie qui va à leur ordinateur pendant ces cycles;-), à nouveau sans aucune raison valable.

Votre seul argument contre les propriétés est par exemple qu'"un utilisateur extérieur ne s'attendrait pas à des effets secondaires à la suite d'une affectation, habituellement" ; mais vous ne tenez pas compte du fait que le même utilisateur (dans un langage tel que Java où les getters et setters sont omniprésents) ne s'attendrait pas non plus à des "effets secondaires" (observables) à la suite de l'appel d'un setter (et encore moins d'un getter;-). Ce sont des attentes raisonnables et c'est à vous, en tant qu'auteur de la classe, d'essayer de les satisfaire -- que votre setter et votre getter soient utilisés directement ou à travers une propriété ne fait aucune différence. Si vous avez des méthodes avec des effets secondaires importants et observables, faites-le. no les nommer getThis , setThat et ne les utilisez pas via les propriétés.

La plainte selon laquelle les propriétés " cachent l'implémentation " est totalement injustifiée : la plupart des tous de la POO consiste à mettre en œuvre le masquage de l'information - en rendant une classe responsable de la présentation d'une interface logique au monde extérieur et en la mettant en œuvre en interne du mieux qu'elle peut. Les récupérateurs et les régleurs, exactement comme les propriétés, sont des outils pour atteindre cet objectif. Les propriétés font simplement un meilleur travail (dans les langages qui les supportent;-).

36voto

Daniel Roseman Points 199743

L'idée est de vous permettre d'éviter d'avoir à écrire des getters et setters jusqu'à ce que vous en ayez réellement besoin.

Donc, pour commencer, vous écrivez :

class MyClass(object):
    def __init__(self):
        self.myval = 4

De toute évidence, vous pouvez maintenant écrire myobj.myval = 5 .

Mais plus tard, vous décidez que vous avez besoin d'un passeur, car vous voulez faire quelque chose d'intelligent en même temps. Mais vous ne voulez pas avoir à changer tout le code qui utilise votre classe - donc vous enveloppez le setter dans la classe @property décorateur, et tout fonctionne.

15voto

Lee B Points 1748

mais cacher le fait que a.b=2 n'est pas une simple affectation simple affectation semble être une recette d'ennuis

Vous ne cachez pas ce fait ; ce fait n'a jamais existé au départ. C'est Python, un langage de haut niveau, pas de l'assembleur. Peu de déclarations "simples" dans ce langage se résument à des instructions uniques du CPU. Lire la simplicité dans une affectation, c'est lire des choses qui ne sont pas là.

Lorsque vous dites x.b = c, tout ce que vous devez penser est probablement "quoi qu'il se soit passé, x.b devrait maintenant être c".

5voto

Reinout van Rees Points 5483

La raison principale est tout simplement que c'est plus beau. C'est plus pythique. En particulier pour les bibliothèques, quelque chose.getValue() est moins joli que quelque chose.value.

Dans plone (un CMS assez important), vous aviez l'habitude d'avoir document.setTitle() qui fait beaucoup de choses comme stocker la valeur, l'indexer à nouveau et ainsi de suite. Faire simplement document.title = 'quelque chose' est plus agréable. Vous savez que beaucoup de choses se passent dans les coulisses de toute façon.

3voto

gnibbler Points 103484

Vous avez raison, c'est juste du sucre syntaxique. Il se peut qu'il n'y ait pas de bonnes utilisations, selon votre définition du code problématique.

Considérons que vous avez une classe Foo qui est largement utilisée dans votre application. Cette application a pris de l'ampleur et, de plus, disons que c'est une application web qui est devenue très populaire.

Vous identifiez que Foo est à l'origine d'un goulot d'étranglement. Il est peut-être possible d'ajouter un peu de cache à Foo pour l'accélérer. L'utilisation des propriétés vous permettra de le faire sans modifier le code ou les tests en dehors de Foo.

Oui, bien sûr, ce code est problématique, mais vous venez d'économiser beaucoup de dollars en le réparant rapidement.

Et si Foo se trouve dans une bibliothèque pour laquelle vous avez des centaines ou des milliers d'utilisateurs ? Vous n'aurez pas à leur dire de procéder à une refonte coûteuse lorsqu'ils passeront à la dernière version de Foo.

Les notes de version ont une ligne sur Foo au lieu d'un paragraphe sur le guide de portage.

Les programmeurs Python expérimentés n'attendent pas grand-chose de la part de a.b=2 autre que a.b==2 mais ils savent que même cela peut ne pas être vrai. Ce qui se passe à l'intérieur de la classe est une affaire personnelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X