Как изменить значение по умолчанию для дополнительного параметра функции

Мне нужно изменить глобальную переменную S в a.py с b.py, но она используется как значение по умолчанию в функции в a.py.

a.py

S = "string"


def f(s=S):
    print(s)
    print(S)

b.py

import a


def main():
    a.S = "another string"
    a.f()


if __name__ == "__main__":
    main()

python b.py выходов

string
another string

вместо ожидаемого

another string
another string

Если я позвоню a.f в b.py вот так

a.f(a.S)

это работает, как и ожидалось, но есть ли способ изменить значение переменной по умолчанию?


person fas    schedule 26.08.2019    source источник
comment
Это связано с тем, что аргументы по умолчанию определяются во время определения функции, а не во время ее выполнения. Это тот же механизм, который управляет поведением изменяемого аргумента по умолчанию.   -  person C.Nivs    schedule 26.08.2019


Ответы (1)


Короткий ответ: вы не можете.

Причина этого в том, что аргументы функции по умолчанию создаются во время определения функции, и значения по умолчанию не предназначены для переопределения. Имя переменной привязывается один раз к значению, и это все, вы не можете повторно привязать это имя к другому значению. Во-первых, давайте посмотрим на переменные в глобальной области видимости:

# create a string in global scope
a = "string"

# b is "string"
b = a

a += " new" # b is still "string", a is a new object since strings are immutable

Теперь вы только что привязали новое имя к "string", а "string new" — это совершенно новое значение, привязанное к a, оно не меняет b, потому что str += str возвращает новое str, что делает a и b относятся к разным объектам.

То же самое происходит с функциями:

x = "123"

# this expression is compiled here at definition time
def a(f=x):
    print(f)

x = "222"
a()
# 123

Переменная f была определена со значением по умолчанию "123" во время определения. Это нельзя изменить. Даже с изменяемыми значениями по умолчанию, такими как в этом вопросе:

x = []

def a(f=x):
    print(x)

a()
[]

# mutate the reference to the default defined in the function
x.append(1)

a()
[1]

x
[1]

Аргумент по умолчанию уже определен, а имя f привязано к значению [], которое нельзя изменить. Вы можете изменить значение, связанное с f, но вы не можете привязать f к новому значению по умолчанию. Чтобы дополнительно проиллюстрировать:

x = []

def a(f=x):
    f.append(1)
    print(f)

a()
x
[1]

# re-defining x simply binds a new value to the name x
x = [1,2,3]

# the default is still the same value that it was when you defined the
# function, albeit, a mutable one
a()
[1, 1]

Возможно, лучше либо А) передать глобальную переменную в качестве аргумента функции, либо Б) использовать глобальную переменную как global. Если вы собираетесь изменить глобальную переменную, которую хотите использовать, не устанавливайте ее в качестве параметра по умолчанию и выберите более подходящее значение по умолчанию:

# some global value
x = "some default"

# I'm choosing a default of None here
# so I can either explicitly pass something or
# check against the None singleton
def a(f=None):
    f = f if f is not None else x
    print(f)

a()
some default

x = "other default"
a()
other default

a('non default')
non default
person C.Nivs    schedule 26.08.2019
comment
@c-nivs Соедините это с тем фактом, что строки неизменяемы, => на самом деле это в основном не связано - то, что вы здесь иллюстрируете, - это разница между перепривязкой и мутацией, а не разница между изменяемыми и неизменяемыми объектами (за исключением, конечно, тот факт, что вы не можете изменить неизменяемый объект). Вы можете прочитать это: nedbatchelder.com/text/names.html - person bruno desthuilliers; 27.08.2019
comment
Также вам не нужен global x - он требуется только в том случае, если вы хотите повторно привязать глобальный объект из функции. - person bruno desthuilliers; 27.08.2019
comment
@brunodeshuilliers Я удалю global. Тот факт, что строки неизменяемы, как я понял (для маленьких строк, не уверен в больших), является причиной того, что python повторно привязывает значение и не использует изменяемую ссылку. Если я неправильно понял, могу ли я внести изменения, чтобы дать более правильный ответ? - person C.Nivs; 27.08.2019
comment
1/ все строки неизменяемы, точка, и 2/ python перепривязывает имя не потому, что строка неизменяема, а потому, что вы сказали ему это сделать (binding =› присваивание). Во втором примере (со списком), если вы перепривязываете список вместо его изменения (=> замените x.append(1) на x = [1] во фрагменте), вы обнаружите, что поведение точно такое же, как и для первого (строка ) пример. FWIW, все это объясняется в статье, на которую я дал ссылку (nedbatchelder.com/ text/names.html), и вы определенно хотите прочитать эту статью (а затем отредактировать свой пост) - person bruno desthuilliers; 27.08.2019