class A:
p = 1
def __init__(self, p=None, **kwargs):
self.p = p
class B(A):
p = 2
a = A()
print(a.p)
b = B()
print(b.p)
As a more sensible example consider:
class Mamal:
can_fly = False
class Bat(Mamal):
can_fly = True
In the examples above, I would like 1 and 2 be printed. However, it prints None for both, though I know why. What is the solution to preserve the default value of classes?
One solution I can think of is:
class A:
p = 1
def __init__(self, p=None, **kwargs):
if p: self.p = p
if q: self.q = q
...
and if I have many attributes I should do that for all of them!? another minor problem is that the user can't pass None to the class init.
Another solution could be like:
class A:
p = 1
def __init__(self, p=1, **kwargs):
self.p = p
self.q = q
...
However again if one instantiate b like:
b = B()
the value of b.p would be also 1, while I expect it to keep 2.
I use overriding classes attributes much, but I just don't know how to preserve them from being overwritten by default values of the same or parent class.
Yet, another solution is combination of the above, like:
class A:
p = 1
def __init__(self, p=1, **kwargs):
if p != 1: self.p = p
...
or using dataclass
from dataclasses import dataclass
#dataclass
class A:
p :int = 1
#dataclass
class B(A):
p:int = 2
Just would like to know what is usual approach and consequences.
UPDATE:
If you really absolutely need both your class and your instances to have this attribute, and also want to use the class attribute as the default for an instance, I would say the correct way is like this:
_sentinel = object()
class A:
p = 1
def __init__(self, p=_sentinel):
if p is not _sentinel:
self.p = p
class B(A):
p = 2
a = A()
print(a.p) # prints 1
b = B()
print(b.p) # prints 2
b2 = B(p=None)
print(b2.p) # prints None
The sentinel object is for when you do want to be able to pass None to the constructor for whatever reason. Since we compare identity in the __init__ method, it is (practically) guaranteed that if any value is passed, it will be assigned to the instance attribute, even if that value is None.
Original answer:
The problem seems to stem from a misunderstanding of how (class-)attribute work in Python.
When you do this:
class A:
p = 1
You define a class attribute. Instances of that class will automatically have that same attribute upon initialization, unless you overwrite it, which is exactly what you do here:
def __init__(self, p=None, **kwargs):
self.p = p
This overwrites the instance's attribute .p with the value p it receives in the __init__ method. In this case, since you defined a default value None and called the constructor without passing an argument, that is what was assigned to the instance's attribute.
If you want, you can simply omit the self.p assignment in the constructor. Then your instances will have the class' default upon initialization.
EDIT:
Depending on how you want to handle it, you can simply assign the value after initialization. But I doubt that is what you want. You probably don't need class attributes at all. Instead you may just want to define the default values in your __init__ method signature and assign them there.
If you really need that class attribute as well, you can do what you did, but more precisely by testing for if p is not None:.
I would set the default value of the p argument to the value that you want:
class A:
def __init__(self, p=1, **kwargs):
self.p = p
class B(A):
def __init__(self, p=2, **kwargs):
super().__init__(p, **kwargs)
a = A()
print(a.p)
b = B()
print(b.p)
Then from the constructor of B you can call the one from A by using super().__init__
You can use class properties from the class:
class A:
p = 1
class B(A):
p = 2
a = A()
print(a.p)
b = B()
print(b.p)
prints 1 and 2, like you wanted.
It is clearer to access them from the class directly, though:
print(A.p)
print(B.p)
You can set the instance one, without changing what is associated in the class.
class B(A):
def change(self, x):
self.p = x
b.change(3)
print(B.p) #2
print(b.p) #3
Related
I am very sorry for the confusing title, I did not know how else to phrase the question.
Let's say I have a class, A. It is described as shown:
class A:
def __init__(self, argument):
self.value = argument
def submethod(self, argumentThatWillBeAClass):
print(dir(argumentThatWillBeAClass))
And then I initialize it as shown below:
classAInstance = A('42.0')
Now, I have a class, B. Let's add a submethod that calls A's submethod with B as an argument.
class B:
def __init__(self, argumentThatIsAClassAInstance):
self.classAInstance = argumentThatIsAClassAInstance
def submethod(self):
self.classAInstance.submethod(self)
Let's initialize it with classInstance:
classBInstance = B(classAInstance)
My desired result is that all the attributes of B are printed when B.submethod is called. Is this possible, and if not, how would I achieve something like this?
Now, I have a class, B. Let's add a submethod that calls A's submethod
with B as an argument.
But that isn't what your code does. On the following line:
self.classAInstance.submethod(self)
You are calling the method (I don't know what you mean by "sub" method, these are all just normal methods) with *an instance of B, not B.
Two different ways you could do this:
self.classAInstance.submethod(type(self))
Or:
self.classAInstance.submethod(B)
The semantics aren't exactly the same, since the first dynamically retreives the instance, if some other class inherits from B, it will call dir on that class. The second always prints dir(B), regardless of inheritance.
So:
class A:
def method(self, klass: type) -> None:
print(dir(klass))
class B:
def __init__(self, a: A) -> None:
self.a = a
def method(self) -> None:
self.a.method(type(self))
b = B(A())
As one potential solution, you can use inheritance. This allows class B to inherit everything from class A
class A:
def __init__(self, argument):
self.value = argument
def submethod(self, argumentThatWillBeAClass):
print(dir(argumentThatWillBeAClass))
class B(A):
def __init__(self, value):
super().__init__(value)
def submethod(self, argumentThatWillBeAClass): # You can override the method and do extra code too.
super().submethod(argumentThatWillBeAClass) # Calls A's submethod function
I want to get all class variable names of all sub-classes in my parent class (in python). While I managed to do that I'm not certain if there is a cleaner way to achieve that.
Pseudo code to explain the problem :
class Parent:
all_names = []
var_1 = 1
def __init__(self):
print(self.all_names)
class ChildA(Parent):
var_2 = 2
class ChildB(ChildA):
var_3 = 3
p = Parent()
ca = ChildA()
cb = ChildB()
>>> ["var_1"]
>>> ["var_1","var_2"]
>>> ["var_1","var_2","var_3"]
So what I am currently doing is that I use the __new__ function to set those values recursively :
class Parent(object):
test_1 = 1
signals = []
def __new__(cls, *args, **kwargs):
# create obj ref
_obj = super().__new__(cls, *args, **kwargs)
signals = []
# walk every base and add values
def __walk(base):
for key, value in vars(base).items():
if <condition>
signals.append(value.__name__)
for base in base.__bases__:
__walk(base)
__walk(cls)
signals.reverse() # to reorder the list
_obj.signals = signals
return _obj
For more context: I am trying to develop a Signal-System but to check whether an instance has a Signal I somehow need to add them to the Root-Parent-Class. Yes I could Make all Subclasses inherit from a helper class but I don't want to do that and instead have it all bundled in as few classes as possible.
Also if there are any bugs/risks with my implementation please let me know I just recently discovered __new__.
(Python 3.x)
You could walk up the method resolution order and check for any keys in each class's __dict__ that do not appear in object.__dict__ and are not callable or private keys.
class Parent:
var_p = 1
def __init__(self):
self.blah = 0
class_vars = []
for cls in self.__class__.mro()[:-1]:
class_vars.extend([
k for k, v in cls.__dict__.items()
if k not in object.__dict__ and not k.startswith('_')
and not callable(v)
])
print(class_vars)
def test(self):
return True
class ChildA(Parent):
var_a = 2
class ChildB(ChildA):
var_b = 3
p = Parent()
ca = ChildA()
cb = ChildB()
# prints:
['var_p']
['var_a', 'var_p']
['var_b', 'var_a', 'var_p']
I have two python classes
class A:
"""
This is a class retaining some constants
"""
C=1
class B:
VAR = None
def __init__(self):
b.VAR = A
def f(self, v=VAR ):
print(v.C)
clb = B()
clb .f()
AttributeError: 'NoneType' object has no attribute 'C'
So what I am trying to do is populate the B::VAR class variable in the B::init() with the reference of class A, and after that in the B::f() to have access to A::C by using default argument v (that retains VAR).
I intend to use v as a default value for the code inside B::f() and if needed to change it when calling the function.
Is my scenario possible?
Thank you,
Yes, this is possible:
class A:
"""
This is a class retaining some constants
"""
C = 1
class B:
VAR = None
def __init__(self):
self.VAR = A
def f(self, v=None):
if v is None:
v = self.VAR
print(v.C)
clb = B()
clb.f()
You issue is that the default arguments v=VAR is an old reference to the B.VAR which is None, not the updated value of the object clb.VAR.
This diagram show that the old version of f() have a default value for v that point to None, because this is computed at the definition of the method, when the class B is defined, before any creation of clb: B object, where VAR is a class attribute.
My suggestion is to set v at runtime using the VAR of the object throught self, which is changed in the __init__ to A.
class A:
C = 1
class B:
VAR = None
def __init__(self):
B.VAR = A
#classmethod
def f(cls):
print(cls.VAR.C)
clb = B()
clb.f()
This is another way to do it. However, I'm wondering what it is you're actually trying to do, because this seems really strange
Being new to OOP, I wanted to know if there is any way of inheriting one of multiple classes based on how the child class is called in Python. The reason I am trying to do this is because I have multiple methods with the same name but in three parent classes which have different functionality. The corresponding class will have to be inherited based on certain conditions at the time of object creation.
For example, I tried to make Class C inherit A or B based on whether any arguments were passed at the time of instantiating, but in vain. Can anyone suggest a better way to do this?
class A:
def __init__(self,a):
self.num = a
def print_output(self):
print('Class A is the parent class, the number is 7',self.num)
class B:
def __init__(self):
self.digits=[]
def print_output(self):
print('Class B is the parent class, no number given')
class C(A if kwargs else B):
def __init__(self,**kwargs):
if kwargs:
super().__init__(kwargs['a'])
else:
super().__init__()
temp1 = C(a=7)
temp2 = C()
temp1.print_output()
temp2.print_output()
The required output would be 'Class A is the parent class, the number is 7' followed by 'Class B is the parent class, no number given'.
Thanks!
Whether you're just starting out with OOP or have been doing it for a while, I would suggest you get a good book on design patterns. A classic is Design Patterns by Gamma. Helm. Johnson and Vlissides.
Instead of using inheritance, you can use composition with delegation. For example:
class A:
def do_something(self):
# some implementation
class B:
def do_something(self):
# some implementation
class C:
def __init__(self, use_A):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A() if use_A else B()
def do_something(self):
# delegate to A or B instance:
self.instance.do_something()
Update
In response to a comment made by Lev Barenboim, the following demonstrates how you can make composition with delegation appear to be more like regular inheritance so that if class C has has assigned an instance of class A, for example, to self.instance, then attributes of A such as x can be accessed internally as self.x as well as self.instance.x (assuming class C does not define attribute x itself) and likewise if you create an instance of C named c, you can refer to that attribute as c.x as if class C had inherited from class A.
The basis for doing this lies with builtin methods __getattr__ and __getattribute__. __getattr__ can be defined on a class and will be called whenever an attribute is referenced but not defined. __getattribute__ can be called on an object to retrieve an attribute by name.
Note that in the following example, class C no longer even has to define method do_something if all it does is delegate to self.instance:
class A:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am A')
class B:
def __init__(self, x):
self.x = x
def do_something(self):
print('I am B')
class C:
def __init__(self, use_A, x):
# assign an instance of A or B depending on whether argument use_A is True
self.instance = A(x) if use_A else B(x)
# called when an attribute is not found:
def __getattr__(self, name):
# assume it is implemented by self.instance
return self.instance.__getattribute__(name)
# something unique to class C:
def foo(self):
print ('foo called: x =', self.x)
c = C(True, 7)
print(c.x)
c.foo()
c.do_something()
# This will throw an Exception:
print(c.y)
Prints:
7
foo called: x = 7
I am A
Traceback (most recent call last):
File "C:\Ron\test\test.py", line 34, in <module>
print(c.y)
File "C:\Ron\test\test.py", line 23, in __getattr__
return self.instance.__getattribute__(name)
AttributeError: 'A' object has no attribute 'y'
I don't think you can pass values to the condition of the class from inside itself.
Rather, you can define a factory method like this :
class A:
def sayClass(self):
print("Class A")
class B:
def sayClass(self):
print("Class B")
def make_C_from_A_or_B(make_A):
class C(A if make_A else B):
def sayClass(self):
super().sayClass()
print("Class C")
return C()
make_C_from_A_or_B(True).sayClass()
which output :
Class A
Class C
Note: You can find information about the factory pattern with an example I found good enough on this article (about a parser factory)
I have 2 classes with some functions:
class A:
def __init__(self, one=1, two=2):
self.one = one
self.two = two
def do_smt(self):
...
class B:
def __init__(self, value="test"):
self.value = value
def do_smt(self):
...
I have a third class that has to use the 2 classes is doing this.
class C:
def __init__(self, one=1, two=2, value="test"):
self.A = A(one, two)
self.B = B(value)
def do_smt(self):
...
Now I do this: new_class = C()
But what if the default value of class A or B changes, then I also need to change that in class C. Is there a way to write class C in a way that it knows which arguments are the default ones? It need to handle no arguments but also arguments that other classes expect.
You can use inspect.signature to obtain the parameters of the __init__ method of each "base" class of class C, and let C.__init__ accept variable keyword arguments, so that it can iterate through the "base" classes and pass to the __init__ method of each just what it needs and what the given keyword arguments have. Use itertools.islice to ignore the first parameter, which is always self:
import inspect
from itertools import islice
class C:
bases = A, B
params = {}
for cls in bases:
params[cls] = inspect.signature(cls.__init__).parameters
def __init__(self, **kwargs):
for cls in self.bases:
setattr(self, cls.__name__, cls(**{key: kwargs[key] for key in
islice(self.params[cls], 1, None) if key in kwargs}))
so that:
c = C(one=3,value='hi')
print(c.A.one)
print(c.A.two)
print(c.B.value)
outputs:
3
2
hi
You could use some sentinel value (here None) and pass parameters only if they are provided as something meaningful:
class C:
def __init__(self, one=None, two=None, value=None):
if one is two is None:
self.A = A()
else:
self.A = A(one, two)
if value is None:
self.B = B()
else:
self.B = B(value)
That way, A and B's defaults take care of themselves.
One solution is to factor the default values to constants:
DEFAULT_ONE = 1
DEFAULT_TWO = 2
class A:
def __init__(self, one=DEFAULT_ONE, two=DEFAULT_TWO):
pass
Use the constants in class C as well.
Before the call class A and B, define init values to variables
Try add these before calls in class C init:
self.initA_one = A.one
self.initA_two = A.two
self.initB_value = B.value
And continue
self.A = A (.,.)
self.B = B (.)
EDIT:
this was what i meant.
class C():
def __init__(self, one=-1, two=-2, value="detest"):
self.initA_one = A().one
self.initA_two = A().two
self.initB = B().value
self.A = A(one, two)
self.B = B(value)
def do_smt(self):
print()
new_class = C()
print(f'default A.one is {new_class.initA_one}, new value A.one is {new_class.A.one}.')
print(f'default A.two is {new_class.initA_two}, new value A.two is {new_class.A.two}.')
print(f'default B.value is {new_class.initB}, new B.value is {new_class.B.value}')
gives
default A.one is 1, new value A.one is -1.
default A.two is 2, new value A.two is -2.
default B.value is test, new B.value is detest
I am not sure if this fits exactly what you want, but basically you can let C decide what to give to A, B and let A, B decide what to use, using **kwds method parameters in A and B.
One of the differences, with the sample class C2, is that, if C has a different default value it overrides A, B.
There is also another alternative, under C3, where you use a guard value (not using None to allow that to be a default) to only pass on arguments that were given to C3.
class A:
def __init__(self, one=1, two=2, **kwds):
self.one = one
self.two = two
def do_smt(self):
pass
class B:
def __init__(self, value="test", **kwds):
self.value = value
class C:
def __init__(self, one=1, two=2, value="test"):
self.A = A(one, two)
self.B = B(value)
class C2:
""" your default values override those of A, B"""
def __init__(self, one=1, two=2, value="test"):
locals_ = locals()
locals_.pop("self")
self.A = A(**locals_)
self.B = B(**locals_)
undefined = NotImplemented
class C3:
""" your default values dont affect A and Bs"""
def __init__(self, one=undefined, two=undefined, value="test"):
locals_ = {k:v for k,v in locals().items() if k != "self" and v is not undefined}
self.A = A(**locals_)
self.B = B(**locals_)
#can still use it locally
self.one = one if one is not undefined else 11
self.two = two if two is not undefined else 22
c= C()
print("c.A.one:", c.A.one)
print("c.B.value:", c.B.value)
c2= C2()
print("c2.A.one:", c2.A.one)
print("c2.B.value:", c2.B.value)
c3= C3()
print("c3.A.one:", c3.A.one)
print("c3.one:", c3.one)
print("c3.B.value:", c3.B.value)
output:
c.A.one: 1
c.B.value: test
c2.A.one: 1
c2.B.value: test
c3.A.one: 1
c3.one: 11
c3.B.value: test
You could even have a variant of C that uses **kwds itself and pass those on to A, B in case they find value in it.
class C4:
""" your default values dont affect A and Bs
and you can pass in anything.
Neither two or value are known to C and that's OK"""
def __init__(self, one=undefined, **kwds):
locals_ = locals()
locals_ = {k:v for k,v in locals().items() if k not in ("self","kwds") and v is not undefined}
locals_.update(**kwds)
self.A = A(**locals_)
self.B = B(**locals_)
#can still use it locally
self.one = one if one is not undefined else 11
c4= C4(value="somevalue")
print("c4.A.one:", c4.A.one)
print("c4.A.two:", c4.A.two)
print("c4.one:", c4.one)
print("c4.B.value:", c4.B.value)
output:
c4.A.one: 1
c4.A.two: 2
c4.one: 11
c4.B.value: somevalue
I think the best (and pythonic) way to write such classes is always using None as default for any optional argument in any class. In class A and B you then check whether the respective argument is None and if so, replace by your real default value. That way the default for each attribute is defined in only one place and class C doesn't need to know the default value.