Make variable common to all subclasses - python

I want a class temp with a variable a, and its two subclasses c1 and c2. If a is changed in c1, it should also be reflected in c2 and vice-versa. For this, I tried:
class temp(ABC):
a=1
def f(self):
pass
class c1(temp):
def f(self):
print(self.a)
class c2(temp):
def f(self):
print(self.a)
o1=c1()
o2=c2()
o1.f()
o2.f()
o1.a+=1
o1.f()
o2.f()
It gives me output:
1
1
2
1
whereas I want it to be
1
1
2
2
I also tried super.a instead of self.a, but it gives me an error. How can I achieve the desired target? Thanks...

Rather than incrementing o1.a, you need to increment the static variable itself.
Ie temp.a += 1
class temp():
a=1
def f(self):
pass
class c1(temp):
def f(self):
print(self.a)
class c2(temp):
def f(self):
print(self.a)
o1=c1()
o2=c2()
o1.f()
o2.f()
temp.a+=1
o1.f()
o2.f()
>>> 1
1
2
2

Related

Increment class property attribute in setter

I understand the concept of getter/setter in python. The thing I am not able to understand is, I need to add new value to the variable itself and I am not sure how I can achieve this with #property decorator.
The old code instantiates some variables and self increment them. I am trying to refactor the code and move those variables to a class and add #property/setter so that I can access them as attributes.
Old Code:
class ExistingCode(object):
a = 0
b = 0
c = 0
d = 0
bunch of other code..
a += 12
b += 12
c += 12
d += 12
What I am trying to do is:
class Variables(object):
def __init__(self):
a = 0
b = 0
c = 0
d = 0
#property
def a(self):
return self.a
#a.setter
def a(self, x)
a = x
......
I am getting "RuntimeError: maximum recursion depth exceeded". Please help.
I found the issue. The main problem was that I was using the same name for the attribute and the property as mentioned by #martineau. Also, I missed self in many places. Below is the working example.
class Variables(object):
def __init__(self):
self.a = 0
#property
def a(self):
return self.__a
#a.setter
def a(self, x):
self.__a = x

How to use #classmethod in python with inheritance [duplicate]

I would expect the following code to print 012345 but it prints 012012. Why? I would expect the calls to incr to be accessing the same variables since they are inherited from the same class but they are clearly different variables.
class a(object):
var = 0
#classmethod
def incr(cls):
print cls.var
cls.var+=1
class b(a):
def func(self):
super(b,self).incr()
class c(a):
def func(self):
super(c,self).incr()
t = a()
t1 = b()
t2 = c()
t1.func()
t1.func()
t1.func()
t2.func()
t2.func()
t2.func()
They are inherited from the same class, but the cls passed to the classmethod via super is the current class where the method was called from. super accesses the base class version of the method, but the cls for the call is the class where the super call was made.
This is one of the subtle differences between doing:
def func(self):
super(c, self).incr() # same as a.__dict__['incr'].__get__(self, type(self))()
and:
def func(self):
a.incr()
You can confirm this by printing the current cls in your incr method in both cases:
def incr(cls):
print cls
...
You should never assume that all super does is make a method call bound to the parent class. It does a lot more.
Keep in mind that when the first augmented assignment += is performed, the initial value of var is read from the base class (since at this point it does not exist in the dict of the subclasses). The updated value is however written to the subclass. Calling super from the second subclass repeats the same behavior of reading the initial var value from a.
There is a way to produce the sequence 012345. You have to make sure that the var of class a is increased in the incr method, even when it is called in the subclasses.
To achieve this, increment by a.var += 1, not by cls.var += 1.
As pointed out by the other answers, the var is also inherited to b and c.
By using cls.var += 1 both subclasses increase their own var instead of a's var.
class a:
var = 0
#classmethod
def incr(cls):
print(cls.var)
a.var += 1
class b(a):
def f(self):
super().incr()
class c(a):
def f(self):
super().incr()
cb = b()
cc = c()
cb.incr()
cb.incr()
cb.incr()
cc.incr()
cc.incr()
cc.incr()
cb.incr()
cc.incr()
Produces:
0
1
2
3
4
5
6
7
Both class b and class c inherit from class a separately, and var is set to 0 each time.
One way to have class c to get the same value of var in class a as class b does, class c can inherit from class b like so:
class a(object):
var = 0
#classmethod
def incr(cls):
print cls.var
cls.var+=1
class b(a):
def func(self):
super(b,self).incr()
class c(b):
def func(self):
super(c,self).incr()
t = a()
t1 = b()
t2 = c()
t1.func()
t1.func()
t1.func()
t2.func()
t2.func()
t2.func()`

python class methods and inheritance

I would expect the following code to print 012345 but it prints 012012. Why? I would expect the calls to incr to be accessing the same variables since they are inherited from the same class but they are clearly different variables.
class a(object):
var = 0
#classmethod
def incr(cls):
print cls.var
cls.var+=1
class b(a):
def func(self):
super(b,self).incr()
class c(a):
def func(self):
super(c,self).incr()
t = a()
t1 = b()
t2 = c()
t1.func()
t1.func()
t1.func()
t2.func()
t2.func()
t2.func()
They are inherited from the same class, but the cls passed to the classmethod via super is the current class where the method was called from. super accesses the base class version of the method, but the cls for the call is the class where the super call was made.
This is one of the subtle differences between doing:
def func(self):
super(c, self).incr() # same as a.__dict__['incr'].__get__(self, type(self))()
and:
def func(self):
a.incr()
You can confirm this by printing the current cls in your incr method in both cases:
def incr(cls):
print cls
...
You should never assume that all super does is make a method call bound to the parent class. It does a lot more.
Keep in mind that when the first augmented assignment += is performed, the initial value of var is read from the base class (since at this point it does not exist in the dict of the subclasses). The updated value is however written to the subclass. Calling super from the second subclass repeats the same behavior of reading the initial var value from a.
There is a way to produce the sequence 012345. You have to make sure that the var of class a is increased in the incr method, even when it is called in the subclasses.
To achieve this, increment by a.var += 1, not by cls.var += 1.
As pointed out by the other answers, the var is also inherited to b and c.
By using cls.var += 1 both subclasses increase their own var instead of a's var.
class a:
var = 0
#classmethod
def incr(cls):
print(cls.var)
a.var += 1
class b(a):
def f(self):
super().incr()
class c(a):
def f(self):
super().incr()
cb = b()
cc = c()
cb.incr()
cb.incr()
cb.incr()
cc.incr()
cc.incr()
cc.incr()
cb.incr()
cc.incr()
Produces:
0
1
2
3
4
5
6
7
Both class b and class c inherit from class a separately, and var is set to 0 each time.
One way to have class c to get the same value of var in class a as class b does, class c can inherit from class b like so:
class a(object):
var = 0
#classmethod
def incr(cls):
print cls.var
cls.var+=1
class b(a):
def func(self):
super(b,self).incr()
class c(b):
def func(self):
super(c,self).incr()
t = a()
t1 = b()
t2 = c()
t1.func()
t1.func()
t1.func()
t2.func()
t2.func()
t2.func()`

Override inner class

Lets have an example
class A:
class B:
def f(self):
return 1
def a(self):
return A.B().f()
def b(self):
return self.B().f()
class C(A):
class B(A.B):
def f(self):
return 2
print(A().a())
print(A().b())
print(C().a())
print(C().b())
will produce
1
1
1
2
I want to do something, that it produces
1
1
2
2
How can I override class A, so that both methods, a and b returns 2?
I am working with generated code with antlr. What I want to do, is for example to override __str__() methods to have some kind of better information. It keeps generating code like MyLanguageParser.RootContext() instead of self.RootContext(), and now, what I want to override anything I need to cope methods code, where classes are being called.
Do you have any solutions? I know I am not the best with Python yet.
Maybe it is not possible and solution is to change generated code, but what I heard it is very bad idea.
Edit:
Class A is generated and is immutable
What I have thought is something like
A.B = C.B
print(A().a())
print(A().b())
print(C().a())
print(C().b())
will generate
2
2
2
2
I do not know if it is legal. Actually I will not use A class, but rest of generated code can use it. I do not know if it is safe.
Edit 2: Corrected error with lack of self
Possibly:
class A:
class B:
def f(self):
return 1
def a(self):
# `type(self)` here instead of `A`
# to get actual `C` class instead of `A`
# when `a()` calls for `C()`
return type(self).B().f()
def b(self):
return self.B().f()
class C(A):
class B(A.B):
def f(self):
return 2
Or without modifing A:
class A:
class B:
def f(self):
return 1
def a(self):
return A.B().f()
def b(self):
return self.B().f()
class C(A):
class B(A.B):
def f(self):
return 2
def a(self): # reimplement
return type(self).B().f()
Or somehow trying to rewrite A dynamically to avoid rewriting a's code:
class A:
class B:
def f(self):
return 1
def a(self):
return A.B().f()
def b(self):
return self.B().f()
class C(A):
class B(A.B):
def f(self):
return 2
def a(self):
tmp, globals()['A'] = A, type(self)
try:
return super().a()
finally:
globals()['A'] = tmp
Note, this might be bad idea. I would think if there's better way.

Different instances of A in B (Python)

In a situation like this b1 and b2 both have the same instanse of A.
class A:
def __init__(self):
self.var=1
class B:
a=A()
def __init__(self):
pass
b1=B()
b2=B()
b1.a.var=2 #changing "var" in b1 would also change it in b2
print(b2.a.var) # prints 2
What should i do to have 2 different instances of A in B?
With B defined as it is, its attribute a belongs to the class itself, not each individual instance. You would need to do something like this:
class B:
def __init__(self):
self.a = A()
to get separate instances of A for every B.
You were using what amounts to a static variable. Try this:
class A:
def __init__(self):
self.var = 1
class B:
def __init__(self):
self.a = A()
You need to initialize it on a per-instance basis instead of at the class level like you have now:
class B:
def __init__(self):
self.a = A()
You're initialising A() as a static class variable when it is first parsed.
To have one instance of A() per instance of B() it should be in the __init__ of B()
class A:
def __init__(self):
self.var=1
class B:
def __init__(self):
self.a = A()
b1=B()
b2=B()
b1.a.var=2 # changing "var" in b1 would not change it in b2
print(b2.a.var) # now prints 1

Categories

Resources