This is another default argument . The fact is that when you write
def foo(value=VALUE):
The code inside the function is compiled and converted into a function object. It is at this time - not during a call! - that the arguments are saved by default. Thus, by the time you defined B , it is already too late: the default value foo already set and changing VALUE will have no effect.
If this seems weird, suppose foo is a global function:
default = 3 def foo(x=default): pass
Then any other code anywhere could ruin foo by doing
global default default = 4
This is possibly just as confusing.
To make the search run at runtime rather than compile time, you must put them inside a function:
def foo(value=None): self.value = self.VALUE if value is None else value
or (not quite the same, but prettier)
self.value = value or self.VALUE
(This is different from the fact that it will treat any false value as a sentinel, that is, 0 , [] , {} , etc. will all be overwritten VALUE .)
EDIT: @mgilson pointed out another way to do this:
def foo(**kwargs): self.value = kwargs.get("value", self.VALUE)
This is more accurate because it does not require you to make a control value (for example, None or object() ), but it has significantly changed the specification of the foo argument, since now it will accept arbitrary keyword arguments for your call.