I'd like to modify the class variable bar
of a class B so that the creation of the class A object will be performed with bar2
instead of bar1
(where bar1
is the default). This makes it easier for the user to select if they want to create class B with parameter bar1
or bar2
when using the class. I'm not opposed to other ways (eg. method) to modify it. I just want the user to be able to do it before the class is instantiated.
Class A:
class A(object):
def __new__(cls, foo):
obj = super(A, cls).__new__(cls)
return obj
I don't have access to Class A. Class A is coming from a third party package that doesn't have an __init__
but it takes foo
as an argument of the __new__
function.
Class B:
class B(A):
bar = "bar1"
def __new__(cls, foo=bar):
print("actual",foo)
rm = super().__new__(cls,foo=foo)
return rm
Class B is a derivative class that inherits from class A. Since I want to modify the foo parameter in class A, I need to overwrite the __new__
class.
One other piece of the puzzle is that on top of B there is a long stack of child classes that inherit from B and I don't want to modify all of their init to pass along the foo variable until class A.
I want to run the following code (because it's easy from the user perspective to choose the value of 'bar' and because the modifications to the code are very little.
if __name__ == '__main__':
B.bar = 'bar2'
b = B()
print(b.bar)
I get:
actual bar1
bar2
whereas I'm expecting:
actual bar2
bar2
I modified my example to make it work but it's not pretty and I'm seeking for a more pythonic solution. This uses bar class variable as an array and uses class methods to modify it
class A(object):
def __new__(cls, foo):
obj = super(A, cls).__new__(cls)
return obj
class B(A):
_bar_array = ["bar1"]
@classmethod
def set_bar(cls, value):
cls._bar_array[0] = value
@classmethod
def get_bar(cls):
return cls._bar_array[0]
def __new__(cls, foo=_bar_array):
print("actual",foo[0])
rm = super().__new__(cls,foo=foo[0])
return rm
if __name__ == '__main__':
B.set_bar("bar2")
b = B()
print(b.get_bar())
print(B.get_bar())
As I explained in the comments, default arguments only get evaluated when they are defined not on each invocation. So just use the normal python idiom (often relied upon to make mutable default arguments work the way most people expect them to work) of setting the default value to None
(or an appropriate sentinel if None
has some other semantics) and then using the expression you were hoping to get re-evaluated within the body of the function if the default value is None:
class A(object):
def __new__(cls, foo):
print('foo seen by bar', foo)
return super(A, cls).__new__(cls)
class B(A):
bar = "bar1"
def __new__(cls, foo=None):
if foo is None:
foo = cls.bar
return super().__new__(cls, foo=foo)
b = B() # foo seen by A: bar1
B.bar = 'bar2'
b = B() # foo seen by A: bar2
If B.bar = None
is supposed to have some other meaning, then you just use another sentinel (an arbitrary object works!)
_NO_FOO_PROVIDED = object()
class B(A):
bar = "bar1"
def __new__(cls, foo=_NO_FOO_PROVIDED):
if foo is _NO_FOO_PROVIDED:
foo = cls.bar
return super().__new__(cls, foo=foo)
B()
B.bar = None
B()