views:

85

answers:

3

Is there a method to pass a variable number of arguments to a function and have it change those arguments using the ( *args, **keywords ) style of argument passing? I've tried a few things but either see no change or have an error raised by the compiler:

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

This gets me TypeError: object does not support assignment (they're tuples.) Or

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

which has no effect what so ever. If there is no mechanism nor work around I can admit defeat.

Edit:

To clarify why I'm trying to go this route, consider the case here:

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

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

If I pass my bar object into foo, I get a change in the state. To create a decorator which performs a deepcopy which resets all such state I'm currently customizing it for N arguments. I'd like to create one which accepts any number of arguments. Hence, the question.

+3  A: 

The reason

args[0] = 4

doesn't work is because, as the error message says, args a tuple, which is immutable. So, you'll need it convert it to the mutable object first, for example like this:

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


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

If you give more information, it would be possible to provide more pythonic solution, because what you're doing seems strange. Also, second code seem to work just fine.

For example the following works just fine and changes calling scope variable:

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


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

The reason being exactly the same: mutability of the l object. The same can be shown on your example:

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


>>> b = bar('abc')
>>> foo(b)
>>> b.obj
'something else'
SilentGhost
This would not change the corresponding variable in the calling scope, which I think is what the OP wants.
Dave Kirby
@Dave: while I don't see how's that related to the question, whether anything in the calling scope is going to be changed or not depends on properties of the object. See my edit.
SilentGhost
So then I can't just do a deepcopy and reassignment. I'll need to come to terms with reassignment of the `__dict__` of the object, if it contains a `__dict__` that is.
wheaties
@wheaties, I'm not sure I understand your question. you could just loop over elements of input and assign to each elements `obj` attribute your custom value.
SilentGhost
That's just an example. I'm building a decorator which I'd like to be able to be used on any function which accepts most inputs. I do not know if every object will have a "obj" field.
wheaties
@wheaties: what matters is whether your objects are mutable or not.
SilentGhost
+3  A: 

No - Python uses call by object-sharing, also known as call-by-value.

To clarify the terminology: you are not receiving a deep copy of the object, but a copy of the object reference. Note: this is not the same as call-by-reference! You can think of it as call by value, and that the values are references to objects.

So to answer your question, you receive a copy of the arguments (object references). You cannot modify the object references as if they were passed by reference. You can make a new modified copy of them if you want, but judging from your examples that isn't what you are looking for. The calling scope won't see your changes.

If instead you mutate the objects you receive, the client can see those changes.

Mark Byers
That's what I was afraid to hear. Thanks, though.
wheaties
What you're linking to says: *However since the function has access to the same object as the caller (no copy is made), mutations to those objects within the function are visible to the caller*
SilentGhost
@Silent Ghost: No copy of the object is made, but a copy of the object reference is made.
Mark Byers
@Mark: which means that the mutable object could be mutated! And the resulting mutation would also be visible in caller scope.
SilentGhost
@SilentGhost: OK added a sentence about mutation. I just noticed that quite a lot of new text has been added to the question, but I have to go now... hopefully I'll get time to read it later tonight.
Mark Byers
+1  A: 

If you want to change the arguments passed to the functions, so that you could do something like this:

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

you're out of luck, for the reason stated in Mark's answer.

However, if you're passing mutable objects to your function you can change these objects and see the results after calling 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})
Pär Wieslander