Понимание стиля вызова Python по методу передачи аргументов функции
Я не уверен, что понимаю концепцию вызова Python по объектным стилям передачи аргументов функции (здесь объясняется http://effbot.org/zone/call-by-object.htm). Кажется, не хватает примеров, чтобы прояснить эту концепцию хорошо (или мой google-fu, вероятно, слабый!: D)
Я написал эту маленькую надуманную программу Python, чтобы попытаться понять эту концепцию.
def foo( itnumber, ittuple, itlist, itdict ):
itnumber +=1
print id(itnumber) , itnumber
print id(ittuple) , ittuple
itlist.append(3.4)
print id(itlist) , itlist
itdict['mary'] = 2.3
print id(itdict), itdict
# Initialize a number, a tuple, a list and a dictionary
tnumber = 1
print id( tnumber ), tnumber
ttuple = (1, 2, 3)
print id( ttuple ) , ttuple
tlist = [1, 2, 3]
print id( tlist ) , tlist
tdict = tel = {'jack': 4098, 'sape': 4139}
print '-------'
# Invoke a function and test it
foo(tnumber, ttuple, tlist , tdict)
print '-------'
#Test behaviour after the function call is over
print id(tnumber) , tnumber
print id(ttuple) , ttuple
print id(tlist) , tlist
print id(tdict), tdict
Выход программы
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3]
3075193004 {'sape': 4139, 'jack': 4098}
---------
146739364 2
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
---------
146739376 1
3075201660 (1, 2, 3)
3075103916 [1, 2, 3, 3.4]
3075193004 {'sape': 4139, 'jack': 4098, 'mary': 2.3}
Как вы можете видеть, кроме переданного целого, идентификатор объекта (который, как я понимаю, относится к местоположению memeory) остается неизменным.
Итак, в случае целого числа он был (эффективно) передан по значению, а другая структура данных была (фактически) передана по ссылке. Я попытался изменить список, номер и словарь, чтобы просто проверить, были ли изменены структуры данных. Число не было в списке, а
словарь были.
Я использую слово эффективно выше, поскольку стиль передачи "позывной" передачи аргументов, по-видимому, ведет себя в обоих направлениях в зависимости от структуры данных, переданной в приведенном выше коде
Для более сложных структур данных (например, массивов numpy и т.д.) есть ли быстрое правило для
распознать, какие аргументы будут передаваться по ссылке, а какие - по значению?
Ответы
Ответ 1
Ключевое различие заключается в том, что в языке C-стиля переменная представляет собой поле в памяти, в которое вы помещаете вещи. В Python переменная - это имя.
Python не является ни вызовом по ссылке, ни вызовом. Это нечто более разумное! (На самом деле, я изучил Python, прежде чем я узнал более распространенные языки, поэтому вызов по значению и вызов по ссылке кажутся мне очень странными.)
В Python есть вещи и есть имена. Списки, целые числа, строки и пользовательские объекты - это все. x
, y
и z
- имена. Запись
x = []
означает "построить новую вещь []
и присвоить ей имя x
". Запись
x = []
foo = lambda x: x.append(None)
foo(x)
означает "построить новую вещь []
с именем x
, построить новую функцию (это еще одна вещь) с именем foo
и вызвать foo
на предмет с именем x
". Теперь foo
просто добавляет None
к тому, что он получил, поэтому это сводится к "добавить None
в пустой список". Запись
x = 0
def foo(x):
x += 1
foo(x)
означает "построить новую вещь 0
с именем x
, построить новую функцию foo
и вызвать foo
на x
". Внутри foo
присваивание просто говорит "переименовать x
в 1 плюс то, что раньше было", но это не меняет вещь 0.
Ответ 2
Другие уже опубликовали хорошие ответы. Еще одна вещь, которая, я думаю, поможет:
x = expr
оценивает expr
и связывает x
с результатом. С другой стороны:
x.operate()
делает что-то в x
и, следовательно, может его изменить (в результате чего один и тот же базовый объект имеет другое значение).
В смешные случаи входят такие вещи, как:
x += expr
которые переводятся на x = x + expr
(перевязка) или x.__iadd__(expr)
(изменение), иногда очень своеобразно:
>>> x = 1
>>> x += 2
>>> x
3
(поэтому x
был отскок, так как целые числа неизменяемы)
>>> x = ([1], 2)
>>> x
([1], 2)
>>> x[0] += [3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> x
([1, 3], 2)
Здесь x[0]
, который сам изменен, был мутирован на месте; но затем Python также попытался mutate x
сам (как с x.__iadd__
), который был ошибочен, поскольку кортежи неизменяемы. Но к тому времени x[0]
уже был мутирован!
Ответ 3
Числа, строки и кортежи в Python неизменяемы; с использованием расширенного назначения будет восстановить имя.
Ваши другие типы просто мутированы и остаются тем же объектом.
Ответ 4
Кто-то исправит меня, потому что я не могу себе представить, что это питонический способ сделать это, но...
x=[0]
def foo(x)
x[0] += 1
foo(x)
foo увеличит значение x [0]
Снова я бы хотел, чтобы кто-то сказал мне правильный способ сделать это.