Python: присвоение объекта с переменным списком аргументов - PullRequest
3 голосов
/ 22 июня 2010

Есть ли способ передать переменное число аргументов в функцию и заставить его изменить эти аргументы, используя стиль передачи аргументов ( *args, **keywords )?Я пробовал несколько вещей, но либо не вижу изменений, либо выдает ошибку, выданную компилятором:

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

Это дает мне TypeError: object does not support assignment (это кортежи). Или

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

, который никак не влияет.Если нет механизма или обходного пути, я могу признать поражение.

Редактировать:

Чтобы выяснить, почему я пытаюсь пойти по этому пути, рассмотрим случай здесь:

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

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

Если я передам свой bar объект в foo, я получу изменение в состоянии.Чтобы создать декоратор, который выполняет deepcopy, который сбрасывает все такие состояния, я в настоящее время настраиваю его для N аргументов.Я хотел бы создать тот, который принимает любое количество аргументов.Отсюда и вопрос.

Ответы [ 3 ]

4 голосов
/ 22 июня 2010

Нет - Python использует вызов при совместном использовании объекта , также известный как вызов по значению.

Чтобы уточнить терминологию: вы не получаете глубокую копию объекта,но копия объекта ссылка .Примечание: это не то же самое, что вызов по ссылке !Вы можете думать об этом как о вызове по значению, и что значения являются ссылками на объекты.

Таким образом, чтобы ответить на ваш вопрос, вы получите копию аргументов (ссылки на объекты).Вы не можете изменять ссылки на объекты, как если бы они были переданы по ссылке.Вы можете сделать их новую модифицированную копию, если хотите, но, судя по вашим примерам, это не то, что вы ищете.Область вызова не увидит ваши изменения.

Если вместо этого вы измените получаемых вами объектов, клиент сможет увидеть эти изменения.

3 голосов
/ 22 июня 2010

Причина

args[0] = 4

не работает, потому что, как говорится в сообщении об ошибке, args кортеж, который является неизменным. Итак, вам нужно сначала преобразовать его в изменяемый объект, например, так:

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


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

Если вы дадите больше информации, можно будет найти более питонное решение, потому что то, что вы делаете, кажется странным Кроме того, второй код, кажется, работает очень хорошо.

Например, следующее работает просто отлично и меняет вызывающую переменную области видимости:

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


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

Причина в том же смысле: изменчивость объекта l. То же самое можно показать на вашем примере:

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


>>> b = bar('abc')
>>> foo(b)
>>> b.obj
'something else'
1 голос
/ 22 июня 2010

Если вы хотите изменить аргументы, передаваемые функциям, чтобы вы могли сделать что-то вроде этого:

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

вам не повезло по причине, указанной в ответе Марка.

Однако, если вы передаете изменяемые объекты в свою функцию, вы можете изменить эти объекты и увидеть результаты после вызова 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})
...