Проблема с функциями, которые имеют параметры по умолчанию - PullRequest
1 голос
/ 04 февраля 2011

У меня есть вопрос о функциях, которые имеют параметры по умолчанию.

import sys
from random import randint

my_list = [1,2,3]
def Veli(a, b = my_list):
    my_list.append(randint(1,1500))
    print a + b[-1]

print "Number of Refs:", sys.getrefcount(my_list)
print "Def args:", Veli.func_defaults
Veli(1) # This is 4
Veli(1) # We almost always obtain different result because of randint
Veli(1) # Again different results.
print Veli.func_defaults
print "Number of Refs:", sys.getrefcount(my_list)
Veli(1)
my_list = [-5] # old my_list has different address 
print "Number of Refs:", sys.getrefcount(my_list) # Less than previous
print "Def args:", Veli.func_defaults
Veli(1) # Now gives same results.
print "Def args:", Veli.func_defaults
Veli(1) # Same result again...

Выводы: (Некоторые числа зависят от того, какие значения randint вернул, конечно.)

Количество ссылок: 3Def args: ([1, 2, 3],)3221119740([1, 2, 3, 321, 1118, 739],)Количество ссылок: 3303Количество ссылок: 2Def args: ([1, 2, 3, 321, 1118, 739, 302],)303Def args: ([1, 2, 3, 321, 1118, 739, 302],)303

Следующий код дает вам число меньше, чем предыдущий, потому что b и my_list больше не ссылаются на один и тот же адрес.

print "#ref:", sys.getrefcount(my_list) # Less than previous

Теперь у нас есть способ (единственный способ?), Чтобы достичь b,аргумент функции по умолчанию:

Veli.func_defaults[0]

Извините за мое длинное объяснение.Вот мои вопросы:

  1. Это проблема?Словарь моего модуля раздавить переменную my_list, теперь переменная my_list имеет адрес по умолчанию, чем предыдущий.Затем функция, которая использует глобальную переменную с именем my_list в своем теле, изменяется (растет), а мой аргумент по умолчанию - нет.Почему b не видит глобальную переменную с именем my_list при выполнении выражения a + b[-1]?Я знаю, b имеет другой адрес с my_list (потому что взаимные объекты (например, список) теперь гарантированно ссылаются на другой, уникальный, недавно созданный список ), но почему Python реализован так, что bне может видеть глобальные переменные, когда b аргументы функции?Не могли бы вы объяснить всесторонне?

  2. Есть ли способ получить те же результаты с Veli.func_defaults [0]?Этот код выполняется, скажем, я хочу изменить параметры по умолчанию моей функции с именем Veli.Я не могу сделать это с my_list, потому что my_list и b имеют разные адреса.Одним из способов является изменение элементов списка, Veli.func_defaults [0].Есть ли другой способ (ы)?

  3. (Это не так связано с кодом выше) Как можно получить адреса переменных?Например, как можно получить адрес b?Я использую встроенную функцию, такую ​​как __hash__, но должен быть более подходящий способ.

Примечания:

a) Эти коды могут быть бесполезны по любой причине, но я хочу узнать мнения.б) Python 2.6.6 (r266: 84292, 15 сентября 2010 г., 15:52:39)[GCC 4.4.5] на linux2

Ответы [ 2 ]

1 голос
/ 04 февраля 2011

Я предлагаю вам взглянуть на этот существующий вопрос , поскольку ваш вопрос по существу связан с той же проблемой (хотя и с несколько иной точкой зрения на эту тему).

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

Чтобы получить «ленивое связывание», вы хотите написать свою функцию так:

def Veli(a, b=None):
    if b is None:
        b = my_list # Gets value of my_list at call time
    my_list.append(randint(1,1500))
    print a + b[-1]
1 голос
/ 04 февраля 2011
  1. Аргументы по умолчанию - это границы во время определения функции. Строка def Veli(a, b = my_list): помещает ссылку на объект, my_list случайно ссылающийся на в то время в func_defaults. Python никогда использует передачу по ссылке - переменные содержат ссылки, и эти ссылки всегда передаются по значению. Так что то, что на самом деле сохраняется в b, ссылка (указатель) копируется, и никто не помнит, откуда оно пришло или не пытается его обновить.
  2. Не совсем уверен, что вы спрашиваете, пожалуйста, уточните. b будет Veli.func_defaults[0], если второй аргумент не был передан, но, очевидно, отличается, если он передан (ну, если, конечно, вызывающий не получает доступ к func_defaults ... вы должны предполагать, что он не делает). Вы может, как предложено в другом ответе, установить b по умолчанию на None и использовать глобальный my_list, если b is None - это даст вам свежую, обновленную копию своей ссылки на каждый вызов. Возможно, вам следует написать класс и оставить значение по умолчанию в качестве атрибута self и применить обычную идиому (... = None): if ... is None: use = default.
  3. Встроенная функция id. Фактически, его реализация определила, что это возвращает (не обязательно должен быть адресом), при условии, что это целое число, которое представляет идентичность объекта, то есть различные объекты (с перекрывающимися временами жизни) имеют различный id и один и тот же объект всегда дает то же самое id в течение своей жизни. Простое возвращаемое значение, которое выбирает CPython, - это адрес.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...