2010-06-04 13 views
10

Une question simple, peut-être, mais je ne peux pas tout à fait exprimer ma requête Google pour trouver la réponse ici. J'ai eu l'habitude de faire des copies d'objets quand comme si je les passe dans les constructeurs d'objets,:Python copie-t-il une valeur ou une référence lors de l'instanciation de l'objet?

... 
def __init__(self, name): 
    self._name = name[:] 
... 

Cependant, quand je courais le code de test suivant, il semble ne pas être nécessaire, que Python fait des copies profondes des valeurs d'objet lors de l'instanciation d'objet:

>>> class Candy(object): 
...  def __init__(self, flavor): 
...    self.flavor = flavor 
... 
>>> flav = "cherry" 
>>> a = Candy(flav) 
>>> a 
<__main__.Candy object at 0x00CA4670> 
>>> a.flavor 
'cherry' 
>>> flav += ' and grape' 
>>> flav 
'cherry and grape' 
>>> a.flavor 
'cherry' 

Alors, quelle est la vraie histoire ici? Merci!

EDIT:

Merci à @Olivier pour sa grande réponse. Le code ci-dessous donne un meilleur exemple que Python ne copie par référence:

>>> flav = ['a','b'] 
>>> a = Candy(flav) 
>>> a.flavor 
['a', 'b'] 
>>> flav[1] = 'c' 
>>> flav 
['a', 'c'] 
>>> a.flavor 
['a', 'c'] 

Répondre

13

Il est parce que les chaînes sont immuables .

L'opérateur += plutôt prêter à confusion, en fait réaffecte la variable est appliquée à, si l'objet est immuable:

s = 'a' 
ids = id(s) 
s += 'b' 
ids == id(s) # False, because s was reassigned to a new object 

Ainsi, dans votre cas, au début, à la fois le point flav et a.flavor au même objet chaîne:

flav --------\ 
       'cherry' 
a.flavor ----/ 

Mais quand vous écrivez flav += 'and grape' la variable flav obtient réaffectés à un nouvel objet chaîne:

flav --------> 'cherry and grape' 
a.flavor ----> 'cherry' # <-- that string object never changes 

Il est source de confusion, parce que généralement, lorsque vous appelez un opérateur sur une variable, il ne change pas la variable. Mais juste dans le cas d'un objet immuable, il réattribue la variable. Donc, la réponse finale à votre question est oui, il est logique de copier les objets lors de l'instanciation, surtout si vous attendez un objet mutable (ce qui est souvent le cas). Si l'objet était immuable, cela ne nuira pas à le copier de toute façon.

+3

Pour clarifier, 'self.flavour = flavor' copie par référence, mais comme les chaînes sont immuables, l'objet référencé ne peut pas être changé. Par conséquent, 'flav + = 'et grape' crée en fait un nouvel objet chaîne et fait' flav' le référencer. – taleinat

+0

@taleinat: merci, j'ai mis à jour la réponse, en espérant qu'elle ne prête pas à confusion. –

+0

Merci les gars pour vos réponses. Je suppose que je me demande encore si copier des objets lors de l'instanciation est une bonne idée, car je ne vois pas beaucoup d'endroits où cela est fait. Des suggestions là-bas? – daveslab

1

il semble ne pas être nécessaire

Apparait? Votre question est entièrement sur la conception et la signification. Ce n'est pas une question de préférence ou d'habitude.

Le contrat de classe inclut-il la possibilité de modifier un argument mutable? Si oui, ne faites PAS de copie. Le contrat de classe affirme-t-il qu'un argument mutable ne sera pas modifié? Si oui, vous devez faire une copie.

La réponse à vos questions est entièrement par la définition du contrat pour la classe.

+0

Je vois la zone de votre confusion. J'aurais dû avoir une phrase en haut disant que mon but était de m'assurer que les variables passées à '__init__' étaient copiées par valeur, et non par référence dans l'objet. Ce que je voulais dire par «Cela ne semble pas nécessaire», c'est qu'il semblait que je n'avais pas besoin d'utiliser mes méthodes de copie parce que Python le faisait quand même. J'étais incorrect. – daveslab

+1

@daveslab: Ne pas commenter. Corrigez la question. Veuillez ** mettre à jour ** la question pour indiquer clairement quel est votre objectif. D'autres personnes lisent ces questions en essayant d'apprendre Python et une bonne conception de programme. Veuillez corriger la question pour être parfaitement clair. –