Déclaration de variable Python

86

Apprendre Python , et a quelques doutes fondamentaux.

1.J'ai vu la déclaration de variable (chemin ici) comme

class writer:
    path = ""

parfois, pas de déclaration explicite mais initialiser via __init__.

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

Je comprends le but de __init__, mais est-il conseillé de déclarer une variable dans toute autre fonction.

2. Comment puis-je créer une variable pour contenir un type personnalisé?

class writer:
    path = "" # string value
    customObj = ??
bsr
la source
12
Et, concernant la 2ème question: en Python, les variables n'ont pas de type: les valeurs en ont. Ainsi, n'importe quelle variable peut contenir vos objets personnalisés.
jpaugh
4
Cela vous aidera si vous arrêtez de considérer les noms / identificateurs comme des variables . Ce sont des références à des objets
John La Rooy
2
@gnibbler Ou des noms pour eux ...
detly
1
@detly, je pense que les noms sont un mauvais choix. Les choses n'ont généralement qu'un seul nom alors qu'un objet peut avoir plusieurs références
John La Rooy
2
@JohnClements, mais python ne fonctionne pas comme les mathématiques. Si vous voulez comprendre python, vous devez savoir que les objets avec un __name__attribut ont un "nom". Tous les objets n'ont pas d' __name__attribut, mais vous pouvez toujours y faire référence. Si nous commençons à appeler une «référence» un «nom», nous rendons un mauvais service aux débutants sur la piste.
John La Rooy

Réponses:

202

D'accord, tout d'abord.

Il n'y a pas de "déclaration de variable" ou "d'initialisation de variable" en Python.

Il y a simplement ce que nous appelons «affectation», mais nous devrions probablement simplement appeler «nommer».

L'affectation signifie "ce nom sur le côté gauche se réfère maintenant au résultat de l'évaluation du côté droit, indépendamment de ce à quoi il faisait référence auparavant (le cas échéant)".

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

En tant que tels, les noms de Python (un meilleur terme que «variables», sans doute) n'ont pas de types associés; les valeurs font. Vous pouvez réappliquer le même nom à n'importe quoi quel que soit son type, mais l'objet a toujours un comportement qui dépend de son type. Le nom est simplement un moyen de faire référence à la valeur (objet). Cela répond à votre deuxième question: vous ne créez pas de variables pour contenir un type personnalisé. Vous ne créez pas de variables pour contenir un type particulier. Vous ne "créez" pas du tout de variables. Vous donnez des noms aux objets.

Deuxième point: Python suit une règle très simple en ce qui concerne les classes, qui est en fait beaucoup plus cohérente que ce que font des langages comme Java, C ++ et C #: tout ce qui est déclaré à l'intérieur du classbloc fait partie de la classe . Ainsi, les fonctions ( def) écrites ici sont des méthodes, c'est-à-dire une partie de l'objet de classe (non stocké par instance), tout comme en Java, C ++ et C #; mais d'autres noms ici font également partie de la classe. Encore une fois, les noms ne sont que des noms, et ils n'ont pas de types associés, et les fonctions sont également des objets en Python. Donc:

class Example:
    data = 42
    def method(self): pass

Les classes sont aussi des objets , en Python.

Alors maintenant, nous avons créé un objet nommé Example, qui représente la classe de toutes les choses qui sont Examples. Cet objet a deux attributs fournis par l'utilisateur (en C ++, «membres»; en C #, «champs ou propriétés ou méthodes»; en Java, «champs ou méthodes»). L'un d'eux est nommé dataet stocke la valeur entière 42. L'autre est nommé methodet stocke un objet fonction. (Il y a plusieurs autres attributs que Python ajoute automatiquement.)

Cependant, ces attributs ne font toujours pas vraiment partie de l'objet. Fondamentalement, un objet est juste un ensemble de noms supplémentaires (les noms d'attributs), jusqu'à ce que vous arriviez à des choses qui ne peuvent plus être divisées. Ainsi, les valeurs peuvent être partagées entre différentes instances d'une classe, ou même entre des objets de différentes classes, si vous le configurez délibérément.

Créons une instance:

x = Example()

Nous avons maintenant un objet distinct nommé x, qui est une instance de Example. Les dataet methodne font pas réellement partie de l'objet, mais nous pouvons toujours les rechercher via à xcause de la magie que Python fait dans les coulisses. Lorsque nous recherchons method, en particulier, nous obtiendrons à la place une "méthode liée" (lorsque nous l'appelons, elle xest passée automatiquement en selfparamètre, ce qui ne peut pas arriver si nous recherchons Example.methoddirectement).

Que se passe-t-il lorsque nous essayons d'utiliser x.data?

Lorsque nous l'examinons, il est d'abord recherché dans l'objet. S'il ne se trouve pas dans l'objet, Python regarde dans la classe.

Cependant, lorsque nous attribuons à x.data , Python créera un attribut sur l'objet. Il ne remplacera pas l'attribut de la classe.

Cela nous permet de faire l' initialisation des objets . Python appellera automatiquement la __init__méthode de la classe sur les nouvelles instances lors de leur création, le cas échéant. Dans cette méthode, nous pouvons simplement assigner des attributs pour définir les valeurs initiales de cet attribut sur chaque objet:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Maintenant, nous devons spécifier a namelorsque nous créons un Example, et chaque instance a la sienne name. Python ignorera l'attribut de classe Example.namechaque fois que nous recherchons .nameune instance, car l'attribut de l'instance sera trouvé en premier.

Une dernière mise en garde: la modification (mutation) et l'assignation sont des choses différentes!

En Python, les chaînes sont immuables. Ils ne peuvent pas être modifiés. Quand vous faites:

a = 'hi '
b = a
a += 'mom'

Vous ne modifiez pas la chaîne «hi» d'origine. C'est impossible en Python. Au lieu de cela, vous créez une nouvelle chaîne 'hi mom'et faites acesser d'être un nom pour 'hi ', et commencez à être un nom à la 'hi mom'place. Nous avons fait bun nom pour 'hi 'ainsi, et après avoir réappliqué le anom, il best toujours un nom pour 'hi ', car 'hi 'il existe toujours et n'a pas été changé.

Mais les listes peuvent être modifiées:

a = [1, 2, 3]
b = a
a += [4]

Maintenant, bc'est [1, 2, 3, 4] aussi, parce que nous avons fait bun nom pour la même chose qui anommait, puis nous avons changé cette chose. Nous n'avons pas créé de nouvelle liste pour ato name, car Python traite simplement +=différemment les listes.

Ceci est important pour les objets car si vous aviez une liste comme attribut de classe et que vous utilisiez une instance pour modifier la liste, alors le changement serait "vu" dans toutes les autres instances. Cela est dû au fait que (a) les données font en fait partie de l'objet de classe, et non d'un objet d'instance; (b) parce que vous modifiiez la liste et ne faisiez pas une simple affectation, vous n'avez pas créé un nouvel attribut d'instance masquant l'attribut de classe.

Karl Knechtel
la source
12
C'est pourquoi nous les appelons des «identifiants» . :-)
Martijn Pieters
@Karl_Knechtel dans votre exemple de chaîne à la fin, qu'arrive-t-il à «hi» si nous ne le nommons jamais «b»? Est-ce une fuite de mémoire?
heretoinfinity
2
@heretoinfinity: non; Python utilise le garbage collection pour libérer les objets inaccessibles
John Clements
La déclaration de variable est bien expliquée dans cette vidéo: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan
Regardez cette vidéo pour la réponse youtube.com/…
Thao N
23

Cela peut avoir 6 ans de retard, mais dans Python 3.5 et supérieur, vous déclarez un type de variable comme celui-ci:

variable_name: type_name

ou ca:

variable_name # type: shinyType

Donc, dans votre cas (si vous avez une CustomObjectclasse définie), vous pouvez faire:

customObj: CustomObject

Voir ceci ou cela pour plus d'informations.

O. Aroesti
la source
22

Il n'est pas nécessaire de déclarer de nouvelles variables en Python. Si nous parlons de variables dans des fonctions ou des modules, aucune déclaration n'est nécessaire. Il suffit d' attribuer une valeur à un nom où vous en avez besoin: mymagic = "Magic". Les variables en Python peuvent contenir des valeurs de n'importe quel type, et vous ne pouvez pas limiter cela.

Votre question concerne spécifiquement les classes, les objets et les variables d'instance. La façon idiomatique de créer des variables d'instance se trouve dans la __init__méthode et nulle part ailleurs - alors que vous pouvez créer de nouvelles variables d'instance dans d'autres méthodes, ou même dans du code non lié, c'est juste une mauvaise idée. Cela rendra votre code difficile à raisonner ou à maintenir.

Donc par exemple:

class Thing(object):

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

Facile. Désormais, les instances de cette classe ont un magicattribut:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

La création de variables dans l'espace de noms de la classe elle-même conduit à un comportement totalement différent. C'est fonctionnellement différent, et vous ne devriez le faire que si vous avez une raison spécifique. Par exemple:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

Essayez maintenant:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

Ou:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

En effet, l'espace de noms de la classe elle-même est différent de l'espace de noms des objets créés à partir de celle-ci. Je vous laisse le soin de faire des recherches un peu plus.

Le message à retenir est que Python idiomatique doit (a) initialiser les attributs d'objet dans votre __init__méthode et (b) documenter le comportement de votre classe si nécessaire. Vous n'avez pas besoin de vous soucier de la documentation complète de niveau Sphinx pour tout ce que vous écrivez, mais au moins quelques commentaires sur les détails que vous ou quelqu'un d'autre pourriez avoir besoin pour le récupérer.

detly
la source
12

À des fins de cadrage, j'utilise:

custom_object = None
rouge chaud
la source
1

Les variables ont une portée, donc oui, il est approprié d'avoir des variables spécifiques à votre fonction. Vous n'avez pas toujours besoin d'être explicite sur leur définition; généralement, vous pouvez simplement les utiliser. Seulement si vous voulez faire quelque chose de spécifique au type de la variable, comme ajouter pour une liste, vous devez les définir avant de commencer à les utiliser. Exemple typique de ceci.

list = []
for i in stuff:
  list.append(i)

En passant, ce n'est pas vraiment un bon moyen de configurer la liste. Il vaudrait mieux dire:

list = [i for i in stuff] # list comprehension

... mais je m'éloigne du sujet.

Votre autre question. L'objet personnalisé doit être une classe elle-même.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
PuceJust
la source