Considérez la classe suivante:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
Mes collègues ont tendance à le définir comme ceci:
class Person:
name = None
age = None
def __init__(self, name, age):
self.name = name
self.age = age
La raison principale en est que leur éditeur de choix affiche les propriétés pour l'auto-complétion.
Personnellement, je n'aime pas le dernier point, car cela n'a aucun sens qu'une classe ait ces propriétés définies None
.
Lequel serait la meilleure pratique et pour quelles raisons?
__init__
fournit déjà autocomplétion , etc. De plus, en utilisantNone
empêche l'IDE pour en déduire un meilleur type de l'attribut, il est donc préférable d'utiliser un défaut raisonnable au lieu (quand possible).typing
module, qui vous permettent de fournir des astuces à l'EDI et à linter, si ce genre de chose titille votre fantaisie ...self
. Même siself.name
ou s'ilsself.age
n'étaient pas assignés dans,__init__
ils n'apparaissent pas dans l'instanceself
, ils n'apparaissent que dans la classePerson
.Réponses:
J'appelle cette dernière mauvaise pratique la règle "ceci ne fait pas ce que vous pensez qu'il fait".
La position de votre collègue peut être réécrite comme suit: "Je vais créer un groupe de variables quasi-globales statiques qui ne sont jamais consultées, mais qui prennent de la place dans les tables d'espace de noms de la classe (
__dict__
), juste pour que mon IDE fasse de même. quelque chose."la source
-OO
, pour ceux qui en ont besoin.1. Rendez votre code facile à comprendre
Le code est lu beaucoup plus souvent qu'écrit. Facilitez la tâche de votre mainteneur de code (il se peut que ce soit vous-même l'année prochaine).
Je ne connais aucune règle stricte, mais je préfère avoir un état d'instance futur clairement déclaré. Crasher avec un
AttributeError
est déjà assez grave. Ne pas voir clairement le cycle de vie d'un attribut d'instance est pire. La quantité de gymnastique mentale nécessaire pour rétablir les séquences d'appels possibles menant à l'attribut attribué peut facilement devenir non triviale et entraîner des erreurs.Donc, en général, je ne définis pas seulement tout dans le constructeur, mais je m'efforce également de limiter le nombre d'attributs mutables.
2. Ne mélangez pas les membres de niveau classe et instance
Tout ce que vous définissez directement dans la
class
déclaration appartient à la classe et est partagé par toutes les instances de la classe. Par exemple, lorsque vous définissez une fonction dans une classe, cela devient une méthode identique pour toutes les instances. Même chose pour les membres de données. Ceci est totalement différent des attributs d'instance que vous définissez habituellement dans__init__
.Les membres de données de niveau classe sont les plus utiles en tant que constantes:
la source
self
et n'ont pas besoinself
d'être passées, alors que les méthodes au niveau classe ne sont pas liées , sont des fonctions simples comme on le voit à ladef
fois et acceptent une instance comme premier argument. Donc, ce sont des choses différentes.AttributeError
c'est un bon signal qu'il y a un bug. Sinon, vous avaleriez le néant et obtiendriez des résultats dénués de sens. Particulièrement important dans le cas présent, où les attributs sont définis dans la__init__
, un attribut manquant (mais existant au niveau de la classe) ne peut être provoqué que par un héritage erroné.None
et que cette valeur n'a pas de sens au moment de la construction de l'instance, vous avez un problème d'architecture et vous devez repenser le cycle de vie de la valeur de l'attribut ou de sa valeur initiale. Notez qu'en définissant vos attributs tôt, vous pouvez détecter un tel problème avant même d'avoir écrit le reste de la classe, sans parler de l'exécution du code.Personnellement, je définis les membres dans la méthode __ init __ (). Je n'ai jamais pensé à les définir dans la partie classe. Mais ce que je fais toujours: j’initialise tous les membres de la méthode __ init__, même ceux qui n’ont pas besoin de la méthode __ init__.
Exemple:
Je pense qu'il est important de définir tous les membres au même endroit. Cela rend le code plus lisible. Que ce soit à l'intérieur de __ init __ () ou à l'extérieur, ce n'est pas si important. Mais il est important pour une équipe de s’engager plus ou moins dans le même style de codage.
Oh, et vous remarquerez peut-être que j'ai déjà ajouté le préfixe "_" aux variables membres.
la source
_
: Pour indiquer qu'il est privé! (Pourquoi tant de personnes dans ce fil confondent-elles ou confondent à moitié Python avec d'autres langues?)C'est une mauvaise pratique. Vous n'avez pas besoin de ces valeurs, elles encombrent le code et peuvent provoquer des erreurs.
Considérer:
la source
J'irai avec "un peu comme docstrings, alors" et déclarerai cela sans danger, tant que c'est toujours
None
, ou une gamme étroite d'autres valeurs, toutes immuables.Il pue l'atavisme et l'attachement excessif aux langages statiquement typés. Et ça ne sert à rien de code. Mais il a un but mineur qui reste, dans la documentation.
Il documente les noms attendus, donc si je combine le code avec quelqu'un et que l'un de nous a «nom d'utilisateur» et l'autre nom d'utilisateur, il y a un indice sur les humains que nous avons séparés et que nous n'utilisons pas les mêmes variables.
Le fait de forcer l'initialisation complète en tant que stratégie aboutit à la même chose d'une manière plus pythonique, mais s'il y a du code réel dans la
__init__
, cela fournit un endroit plus clair pour documenter les variables utilisées.Évidemment, le gros problème ici est que cela pousse les gens à s’initialiser avec des valeurs autres que
None
, ce qui peut être mauvais:laisse une trace globale et ne crée pas d'instance pour x.
Mais c’est plus bizarre en Python dans son ensemble que dans cette pratique, et nous devrions déjà être paranoïaques à ce sujet.
la source