Force subclass python init to use subclass variables, not parent class

In the following code:

class A(object): VALUE = 1 def __init__(self, value=VALUE): self.value = value class B(A): VALUE = 2 

I would expect the value of B (). should be equal to 2:

 B().value = 1 

Is there an elegant way to define a class hierarchy where child classes can simply declare the class variables that they want to override and use the default values ​​for instance variables? I still want to allow them to change at the level of each instance, for example.

 b = B(value=3) 
+4
source share
2 answers

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.

+10
source

The default value should not be specified in the func declaration line, otherwise, when the python reads this line, the default value will be focused, and the more you change the VALUE value later, the default value will not be changed.

You can write it as follows:

 class A: VALUE = 1 def __init__(self, value=None): if value is None: value = self.VALUE self.value = value class B(A): VALUE = 2 print(A().value) print(B().value) 
+1
source

All Articles