Share attributes/ variables between classes python - python

class Base(object):
def __init__(self):
print("Base created")
a = "baseclass"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
b = "child a"
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
c = "child b"
print(a)
print(b)
ChildA()
ChildB()
NameError: name 'a' is not defined

Your issue here is that you're trying to access local variables instead of instance attributes.
Here's how you should do it:
class Base():
def __init__(self):
print("Base created")
# Note that you should use `self.a` instead of `a`
self.a = "baseclass"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
# Use `self.b` instead of `b`
self.b = "child a"
# Did you forget to inherit from `ChildA`?
class ChildB(ChildA):
def __init__(self):
# The `super()` call can be simplified
super().__init__()
# Use `self.c` instead of `c'
self.c = "child b"
# Access the instance variables using `self.`
print(self.c)
print(self.b)
print(self.a)
ChildA()
ChildB()
From the console output:
Base created
Base created
child b
child a
baseclass

Related

What is happening here in Python with OOPs while trying Diamond Shape problem

I am learning OOPs with python. created below code to replicate the diamond shape problem in multiple inheritence. I am running the below code in jupyter notebook and output is generated at same.
class parent:
def __init__(self):
self.a=2
self.b=4
def form1(self):
print("calling parent from1")
print('p',self.a+self.b)
class child1(parent):
def __init__(self):
self.a=50
self.b=4
def form1(self):
print('bye',self.a-self.b)
def callchildform1(self):
print("calling parent from child1")
super().form1()
class child2(parent):
def __init__(self):
self.a=3
self.b=4
def form1(self):
print('hi',self.a*self.b)
def callchildform1(self):
print("calling parent from child2")
super().form1()
class grandchild(child1,child2):
def __init__(self):
self.a=10
self.b=4
def callingparent(self):
super().form1()
g=grandchild()
g.form1()
g.callchildform1()
g.callingparent()
The output is below
bye 6
calling parent from child1
hi 40
bye 6
I can understand the "bye 6" output both the times but how it is printing "hi 40". I am new so anybody can explain what is happening here.
You may find the __mro__ attribute of a class informative. Here, MRO stands for Method Resolution Order.
Consider this modification to your code:
class Parent:
def __init__(self):
self.a = 2
self.b = 4
def print_name(self):
print("parent")
def form1(self):
print("calling parent form1")
print('p', self.a + self.b)
class Child1(Parent):
def __init__(self):
self.a = 50
self.b = 4
def print_name(self):
print("child1")
def print_super_name(self):
super().print_name()
def form1(self):
print('bye', self.a - self.b)
def callchildform1(self):
print("calling parent from child1")
super().form1()
class Child2(Parent):
def __init__(self):
self.a = 3
self.b = 4
def print_name(self):
print("child2")
def form1(self):
print('hi', self.a * self.b)
def callchildform1(self):
print("calling parent from child2")
super().form1()
class Grandchild(Child1, Child2):
def __init__(self):
self.a = 10
self.b = 4
def print_name(self):
print("grandchild")
def print_super_name(self):
super().print_name()
def print_super_super_name(self):
super().print_super_name()
def callingparent(self):
super().form1()
g = Grandchild()
print("When I print the name of my class it is:")
g.print_name()
print("When I print my superclass name, it is:")
g.print_super_name()
print("When I print the name of the superclass of my superclass, it is:")
g.print_super_super_name()
print("When you call methods on me, they will be executed from my class and my parent classes in the following order:")
print(Grandchild.__mro__)
g.form1()
g.callchildform1()
g.callingparent()
The output is:
When I print the name of my class it is:
grandchild
When I print my superclass name, it is:
child1
When I print the name of the superclass of my superclass, it is:
child2
When you call methods on me, they will be executed from my class and my parent classes in the following order:
(<class '__main__.Grandchild'>, <class '__main__.Child1'>, <class '__main__.Child2'>, <class '__main__.Parent'>, <class 'object'>)
bye 6
calling parent from child1
hi 40
bye 6
When you run g.callchildform1() Python looks for the definition of callchildform1 in Grandchild. It isn't there, so the next place it looks is Child1. You can see from the example and from the method resolution order that when an instance of Grandchild calls a method defined in Child1 which calls super(), the search for the called method will begin in Child2.

Using super within nested class

I am puzzled with the following error in python 2.7.12
Suppose we have a class definition within a class, something similar to this:
class C(object):
def __init__(self):
print "class C"
class D(object):
def __init__(self):
print "class D"
class A(D):
class B(C):
def __init__(self):
# Strangely here B is "not defined", why?
super(B, self).__init__()
print "class B"
def __init__(self):
super(D, self).__init__()
print "class A"
def do_something(self):
b_class = self.B()
print "b_class within A : {}".format(b_class)
a_class = A()
a_class.do_something()
but if we we extract the definition of class B outside the scope of class A,
everything works well.
Do we need to use "super" differently when called within a nested class? I fail to understand why its usage would be different within or outside the nested class. Any pointers?
The problem is not the subclass or superclass, but the nesting. B itself is not defined, only A.B is.
Note that in Python there is almost never a good reason to nest classes, though.
You need to address B by its full name, A.B:
class C(object):
def __init__(self):
print "class C"
class D(object):
def __init__(self):
print "class D"
class A(D):
class B(C):
def __init__(self):
super(A.B, self).__init__()
print "class B"
def __init__(self):
super(D, self).__init__()
print "class A"
def do_something(self):
b_class = self.B()
print "b_class within A : {}".format(b_class)
>>> a_class = A()
>>> a_class.do_something()
class A
class C
class B
b_class within A : <__main__.B object at 0x7f0cac98cbd0>

How to contain an object and call its method in a class?

I have two classes (ClassA and ClassB) and ClassA contains one object, b, that is an instance of ClassB. The question is that I can't call the b's method in Class A.
class ClassB(object):
def __init__(self):
print('Class B init ...')
def show(self):
print('Showing class b')
class ClassA(object):
#__classb = ClassB()
def __init__(self, classb):
print('Class A init ...')
__classb = classb
def show(self):
__classb.show() # <=== I just want to do this!
b = ClassB()
a = ClassA(b)
a.show()
I expect the result should be:
Class B init ...
Class A init ...
Showing class b
But I meet the problem as this image shows:
How can I fix it?
By doing __classb = classb you are only defining a local __classb variable in the __init__ method.
If you want __classb to be an instance attribute you will need to use self:
self.__classb = classb
And then:
def show(self):
self.__classb.show()
You should create a attribute for a instance of class B in class A like that self.__classb.
Following code
class ClassB(object):
def __init__(self):
print('Class B init ...')
def show(self):
print('Showing class b')
class ClassA(object):
def __init__(self, classb):
print('Class A init ...')
self.__classb = classb
def show(self):
self.__classb.show() # <=== I just want to do this!
b = ClassB()
a = ClassA(b)
a.show()

Acessing a property from a parent object

In python, I'm creating an object inside a class:
class A:
def __init__(self):
self.one = 1
self.two = B()
Now I define class B, and I want to access self.one from inside B()
class B()
def __init__(self):
self.three = "hello"
# here I want to change self.one from class A to self.one + 1
# how to do it without doing self.two = B(self.one) on class A
# definition?
something = A()
is there a way to reference the parent object property, or do we have to pass the value when creating the object ?
A is not a parent object, parent object is the object from which you inherit. B has no knowledge about A, you have to modify your classes structure, for example by passing reference to parent in B's constructor (which you say you do not want to do, althouth it is not entirely clear what do you mean by "without doing self.two = B(self.one)", as this would pass copy of self.one, not a reference, but this is the way to do it)
class A:
def __init__(self):
self.one = 1
self.two = B(self)
class B()
def __init__(self, parent):
self.three = "hello"
self.parent = parent
print self.parent.one # or do whatever you want with it
If you really have to do this, you can use introspection, but this is ugly, hacky, bad way of achieving the result
import inspect
class A():
def __init__(self):
self.one = 1
self.two = B()
class B():
def __init__(self):
self.three = "hello"
self.parent = inspect.currentframe().f_back.f_locals['self']
print self.parent.one

Multiple inheritance problem in Python!

Why does c.print_a() output 'B'?
class A(object):
def __init__(self):
self.some_name = 'A'
def print_a(self):
print self.some_name
class B(object):
def __init__(self):
self.some_name = 'B'
def print_b(self):
print self.some_name
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
if __name__ == '__main__':
c = C()
c.print_a()
class A(object):
def __init__(self, some_name='A'):
self.some_name = some_name
def print_a(self):
print self.some_name
class B(object):
def __init__(self, some_name='B'):
self.some_name = some_name
def print_b(self):
print self.some_name
class C(A, B):
def __init__(self):
A.__init__(self, some_name='AAAAA')
B.__init__(self, some_name='BBBBB')
if __name__ == '__main__':
c = C()
c.print_a()
You only have a single object here; the some_name property is shared between methods from all inherited classes. You call A.__init__, which sets it to A, then B.__init__, which changes it to B.
Also note that you're calling base methods incorrectly; use super:
class A(object):
def __init__(self):
self.some_name = 'A'
super(A, self).__init__()
def print_a(self):
print self.some_name
class B(object):
def __init__(self):
self.some_name = 'B'
super(B, self).__init__()
def print_b(self):
print self.some_name
class C(A, B):
def __init__(self):
super(C, self).__init__()
if __name__ == '__main__':
c = C()
c.print_a()
There's only one self, and you're overwriting its some_name in B.__init__. Maybe you're used to C++, where there would be two separate fields, A.some_name and B.some_name. This concept doesn't apply to Python, where attributes are created dynamically on assignment.
Say you want C to set names for some objects of types A and B and later calling some print_a and print_b methods on objects of type C get these names back ?
You can get this type of behavior using C++ inheritance model, but python model is very different. Only one object with one set of fields. If you want the C++ behavior, the simplest way is probably to declare subobjects (and it looks like a common abuse of inheritance over composition).
Looks like you are trying to do something like below:
class Printable(object):
def __init__(self, name):
self.name = name
def myprint(self):
print self.name
class C(object):
def __init__(self):
self.a = Printable('A')
self.b = Printable('B')
def print_a(self):
self.a.myprint()
def print_b(self):
self.a.myprint()
if __name__ == '__main__':
c = C()
c.print_a()

Categories