2009-10-18 6 views
24

Les gars, je viens de commencer python récemment et se confondre avec les paramètres facultatifs, dire que j'ai le programme comme celui-ci:Python paramètres optionnels

class B: 
    pass 

class A: 
    def __init__(self, builds = B()): 
     self.builds = builds 

Si je crée un double

b = A() 
c = A() 

et imprimer leur builds

print b.builds 
print c.builds 

Je trouve qu'ils utilisent exactement l'objet même,

<__main__.B instance at 0x68ee0> 
<__main__.B instance at 0x68ee0> 

Mais ce n'est pas ce que je veux, car si b changé un état interne construit, l'un dans l'objet c sera également modifié.

Est-il possible de recréer ces paramètres facultatifs à chaque fois en utilisant cette syntaxe de paramètres optionnels?

+15

Vous devez accepter une réponse si elle a résolu votre problème! Plusieurs réponses semblent très instructives. Même si la question est ancienne, je pense que ce serait une bonne idée de donner une réponse à votre approbation – eipxen

+0

duplication possible de ["Least Astonishment" en Python: L'argument par défaut Mutable] (http://stackoverflow.com/ questions/1132941/moins-étonnement-en-python-le-mutable-défaut-argument) –

Répondre

6

Oui; les paramètres par défaut sont évalués uniquement au moment où la fonction est définie.

Une solution possible serait d'avoir le paramètre soit une classe plutôt que d'une instance, à la

def foo(blah, klass = B): 
    b = klass() 
    # etc 
+0

En résumé, vous ne pouvez pas utiliser un objet mutable comme valeur par défaut. –

+0

Bien sûr, vous pouvez. Les classes sont mutables en Python. Cela n'a rien à voir avec mutable/immuable. –

+0

L'utilisation d'un objet mutable conduit à la confusion exacte dans cette question. Lorsque vous utilisez une classe par défaut, vous instantiez presque toujours pour créer un objet. La mutabilité de la classe n'a pas d'importance parce que vous n'utilisez pas la classe directement comme vous utilisez une liste ou un dictionnaire. –

15

vous devez effectuer les opérations suivantes:

class A: 
    def __init__(self, builds=None): 
     if builds is None: 
      builds = B() 
     self.builds = builds 

il y a une très large -spread error, utilisant des paramètres mutables comme arguments par défaut. il y a beaucoup de dupes probablement sur SO.

+1

alors trouvons un dup et fermons celui-ci? –

+2

Le problème est le suivant: Vous ne pouvez pas utiliser d'objets modifiables comme valeurs par défaut. Les doublons précédents ont tous essayé d'utiliser des listes. C'est le premier à utiliser un objet. C'est le "problème standard" d'essayer d'utiliser un objet mutable comme valeur par défaut. –

46

Vous devez comprendre comment les valeurs par défaut fonctionnent afin de les utiliser efficacement.

Les fonctions sont des objets. En tant que tels, ils ont des attributs. Donc, si je crée cette fonction:

>>> def f(x, y=[]): 
     y.append(x) 
     return y 

J'ai créé un objet. Voici ses attributs:

>>> dir(f) 
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', 
'__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__',  
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', 
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 
'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 
'func_name'] 

L'un d'eux est func_defaults. Cela semble prometteur, qu'y a-t-il là-dedans?

>>> f.func_defaults 
([],) 

C'est un tuple qui contient les valeurs par défaut de la fonction. Si une valeur par défaut est un objet, le tuple contient une instance de cet objet.

Cela conduit à un comportement contre-intuitif assez si vous pensez que f ajoute un élément à une liste, retournant une liste contenant uniquement cet élément si aucune liste est fournie:

>>> f(1) 
[1] 
>>> f(2) 
[1, 2] 

Mais si vous savez que la valeur par défaut est une instance d'objet qui est stocké dans l'un des attributs de la fonction, il est beaucoup moins contre-intuitif:

>>> x = f(3) 
>>> y = f(4) 
>>> x == y 
True 
>>> x 
[1, 2, 3, 4] 
>>> x.append(5) 
>>> f(6) 
[1, 2, 3, 4, 5, 6] 

Sachant cela, il est clair que si vous voulez une valeur par défaut du paramètre d'une fonction d'être une nouvelle liste (ou tout nouvel ob ject), vous ne pouvez pas simplement stocker une instance de l'objet dans func_defaults. Vous devez en créer un nouveau chaque fois que la fonction est appelée:

>>>def g(x, y=None): 
     if y==None: 
      y = [] 
     y.append(x) 
     return y 
Questions connexes