1

there is example code:

# b.py
c = False
def d(i=c):
    print(i, c)

I want to write a.py to let the output of b.d to be True, True:

# a.py
import b
b.c = True
b.d()

but the output is False, True.

so, why and how to get it?


write after answer

to why:

# `inspect` may be useful
import inspect
v = True
def f(i=v):
    print(i, v)

s = inspect.signature(f)
s.parameters
Out[6]: mappingproxy({'i': <Parameter "i=True">})
4
  • 1
    You can't do what you want. Your best bet is to write (ugh) def d(i=None): and then inside the function, if i is None: i = c. This is for the same reason that using an empty list as a default argument can cause "unexpected" results. Commented Jun 8, 2018 at 7:13
  • my mistake! change now Commented Jun 8, 2018 at 7:19
  • 1
    You are changing the value of c in the module b, but the d function in module b has already been parsed and evaluated and its default for the argument i was already set to False. Python would be significantly slower if it had to always re-evaluate its function signatures. Commented Jun 8, 2018 at 7:21
  • I get it, so there is no easy way to change STRICT = True in handle_error(strict=STRICT) and let handle_error raise error. Commented Jun 8, 2018 at 7:27

2 Answers 2

5

This is unnecessarily complicated -- we can boil down your question to a few lines:

default = "Before"

def foo(bar=default):
    print(bar)

foo()              # "Before"
default = "After"
foo()              # "Before"

The behavior it seems you expect is that after default = "After", calling foo() will print "After". But it continues to print "Before".

Python will evaluate the default argument for a function once and "lock it in". Reassigning the name of default to something else later has no effect (as we see in the snippet above).

Instead, you can use an approach that's commonly suggested when people want lists as default arguments:

default = "Before"

def foo(bar=None):
    if bar is None:
        bar = default
    print(bar)

foo()              # "Before"
default = "After"
foo()              # "After"

In this case, you're not trying to change the default argument, but rather change what is assigned to bar when no argument is specified. Each time you call foo() with no argument, it'll be assigned None and then the logic inside the function will look up and use the value of the global default.

Sign up to request clarification or add additional context in comments.

Comments

3

That's because the default value is evaluated once at the creation of the function.

def d(i=c):
    print(i, c)

Here, after the import b line, the d function becomes

def d(i=False):
    print(i, c)

So changing c has no effect on the default value of i in d.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.