2010-06-22 7 views
3

Existe-t-il une méthode pour passer un variable number of arguments à une fonction et pour qu'elle change ces arguments en utilisant le style d'argument (*args, **keywords) qui passe? J'ai essayé quelques petites choses, mais pas de changement soit voir ou avoir fait une erreur soulevée par le compilateur:Python: Affectation d'objets avec liste d'arguments variables

def foo(*args): 
    args[0] = 4 

Cela me reçoit TypeError: object does not support assignment (ils sont tuples.) Ou

def foo(*args): 
    plusOne = [ item+1 for item in args ] 
    args = plusOne 

qui n'a pas effectuez quoi que ce soit. S'il n'y a pas de mécanisme ni de travail, je peux admettre ma défaite.

Edit:

Pour expliquer pourquoi je suis en train d'emprunter cette voie, considérons le cas ici:

class bar(object): 
    def __init__(self,obj): 
     self.obj = obj 

def foo(input): 
    input.obj = "something else" 

Si je passe mon objet bar dans foo, je reçois un changement dans l'état . Pour créer un décorateur qui effectue un deepcopy qui réinitialise tous ces états, je le personnalise actuellement pour N arguments. Je voudrais en créer un qui accepte n'importe quel nombre d'arguments. D'où la question.

+0

connexes: http://stackoverflow.com/questions/575196/in -python-pourquoi-peut-une-fonction-modifier-certains-arguments-comme-perçu-par-l'appelant-b – SilentGhost

+0

Merci pour le lien. – wheaties

Répondre

4

Non - Python utilise call by object-sharing, également connu comme appel par valeur.

Pour clarifier la terminologie: vous ne recevez pas une copie complète de l'objet, mais une copie de l'objet référence. Note: ce n'est pas la même chose que call-by-reference! Vous pouvez le considérer comme un appel par valeur, et que les valeurs sont des références à des objets. Donc, pour répondre à votre question, vous recevez une copie des arguments (références d'objet). Vous ne pouvez pas modifier les références d'objet comme si elles avaient été transmises par référence. Vous pouvez en faire une nouvelle copie modifiée si vous le souhaitez, mais à en juger par vos exemples, ce n'est pas ce que vous cherchez. La portée de l'appel ne verra pas vos modifications.

Si au contraire vous muter les objets que vous recevez, le client peut voir ces changements.

+0

C'est ce que j'avais peur d'entendre. Merci quand même. – wheaties

+0

Ce que vous liez à dit: * Cependant puisque la fonction a accès au même objet que l'appelant (aucune copie n'est faite), les mutations à ces objets dans la fonction sont visibles à l'appelant * – SilentGhost

+0

@Silent Ghost: Aucune copie de l'objet est faite, mais une copie de la référence de l'objet est faite. –

3

La raison

args[0] = 4 

ne fonctionne pas est parce que, comme le message d'erreur indique, args un tuple, qui est immuable. Ainsi, vous aurez besoin de le convertir à l'objet mutable premier, par exemple comme ceci:

>>> def foo(*args): 
    print(args) 
    args = list(args) 
    args[0] = 42 
    print(args) 


>>> foo(23) 
(23,) 
[42] 

Si vous donnez plus d'informations, il serait possible de fournir une solution plus pythonique, parce que ce que vous faites semble étrange. En outre, le deuxième code semble fonctionner correctement.

Par exemple, les travaux suivants très bien et les changements d'appel variable portée:

>>> def spam(*a): 
    a[0][0] = 42 


>>> l = [23, 32] 
>>> spam(l) 
>>> l 
[42, 32] 

La raison d'être exactement la même chose: mutabilité de l'objet l. La même chose peut être indiquée sur votre exemple:

>>> def foo(*input): 
    input[0].obj = "something else" 


>>> b = bar('abc') 
>>> foo(b) 
>>> b.obj 
'something else' 
+1

Cela ne changerait pas la variable correspondante dans la portée d'appel, ce que je pense est ce que le PO veut. –

+0

@Dave: bien que je ne vois pas comment cela est lié à la question, si quelque chose dans la portée de l'appel va être modifié ou non dépend des propriétés de l'objet. Voir ma modification. – SilentGhost

+0

Alors je ne peux pas faire une copie profonde et une réaffectation. Je devrai accepter la réaffectation du '__dict__' de l'objet, s'il contient un' __dict__'. – wheaties

1

Si vous souhaitez modifier les arguments passés aux fonctions, afin que vous puissiez faire quelque chose comme ceci:

>>> x, y = (10, 17) 
>>> foo(x, y) 
>>> print (x, y) 
(11, 18) 

vous êtes hors de la chance, pour la raison indiquée dans la réponse de Mark.

Cependant, si vous passez des objets mutables à votre fonction, vous pouvez modifier ces objets et de voir les résultats après avoir appelé foo:

def foo(*args): 
    for arg in args: 
     arg['value'] += 1 

>>> d1 = {'value': 1} 
>>> d2 = {'value': 2} 
>>> foo(d1, d2) 
>>> print (d1, d2) 
({'value': 2}, {'value': 3}) 
Questions connexes