How to write better way to python inheritance? - python

I have written this,
class Sp():
def __init__(self):
self.price = 1
class A(Sp):
def __init__(self):
super(A, self).__init__()
self.test = True
class B(A):
pass
class C(A):
pass
class D(A):
"""In this class I don't want to inherit Sp class, but need A class"""
def __init__(self):
super(D, self).__init__()
self.me = 'ok'
self.list_ = [Sp()]
Sp is the Parent class for A. And I'm using A class in B,C and D, But D don't need Sp inheritance instead it needs Sp instance object inside D(Please look into D class). I want to stop Sp inheritance in D, is there any good way to write this ?

You can't inherit from A without inheriting from Sp if A itself inherits from Sp. You could try to work around it though, by making A inherit from two classes, one of which implements the non-Sp behaviors (say, call it Abits), and Sp (class A(Abits, Sp):). Then have B and C inherit A, while D inherits solely from Abits.
If A doesn't need to be created independently, you could just make A not inherit from Sp at all, and have B and C inherit from both A and Sp (class B(A, Sp):), while D only inherits from A, which saves the need for a separate Abits.
Lastly, you might consider composition. Have D not inherit from anything, just contain an instance of A. Then use the __getattr__ special method to get attributes from A when they're not defined on D:
class D(object): # Explicitly inheriting from object not needed on Py3
def __init__(self, ...):
self.a = A(...)
def __getattr__(self, name):
# Only called when attribute "name" doesn't exist on instance of D
return getattr(self.a, name)
You might also need to use __setattr__ if you need to mutate the A instance. This is trickier (because __setattr__ is called unconditionally, not just when an attribute doesn't exist), but there are plenty of examples of using it available if you search.

Related

how to know from which class instance a function is called to access the instance attributes

I want to access an attribute of the class instance that called a function :
for example:
class A:
def a(self):
return B.q
class B:
q=0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())
the output will be 0 but I want it to print the q attribute of the instance c of the class B which has the value 6
Pass the instance as a parameter.
class A:
def a(self, b):
return b.q
class B:
q=0
def b(self):
M=A()
return M.a(self)
c=B()
c.q = 6
print(c.b())
This appears to be very bad program design. What are you trying to accomplish with this?
You have a class attribute and an instance attribute -- in that class -- of the same name, q. This makes your code difficult to follow and to maintain.
You have method B.b instantiate an instance of class A. You immediately call A.a, which has been assigned the questionable task of returning an instance attribute from and object of class B.
Clean up your design.
Use init appropriately for each class.
Design your class methods to work appropriately with the characteristics of instances of that class. Your question strongly suggests that your design is not yet clean in your mind, nor in code.
define an init method so that you can work with the instance attributes instead of the class variable
class A:
def a(self):
return B.q
class B:
def __init__(self):
self.q = 0
def b(self):
M=A()
return M.a()
c=B()
c.q = 6
print(c.b())

python dynamic multiple inheritance __init__

I am trying to write a plugin environment where I need to do multiple inheritances on an unknown number of classes. Therefore, I have opted to use the type class creation:
class A(object):
def __init__(self,s):
self.a="a"
def testA(self,x):
print(x)
class B(object):
def __init__(self,s):
self.b="b"
def testA(self,x):
print(x)
C = type('C', (A,B), {})
x= C("test")
print x.b
When I run the above code, I get the error:
AttributeError: 'C' object has no attribute 'b'
This is because only the init for class A is being run when the instance for class C is initialized. My question is how can I get the class C to have both the init for class A as well as the init for class B to run when an instance of class C is initialized. I do realize that if I had class C like the following it would work:
class C(A,B):
def __init__(self,s):
A.__init__(self,s)
B.__init__(self,s)
However, given that I need to have a dynamic list of classes inherited this will not work.
It seems you're using python 2 so I'm using this old python 2 super() syntax where you have to specify the class and the instance, although it would work in python 3 as well. In python 3 you could also use the shorter super() form without parameters.
For multiple inheritance to work is important that the grandparent class __init__ signature matches the signature of all siblings for that method. To do that, define a common parent class (MyParent in this example) whose __init__ has the same parameter list as all the childs. It will take care of calling the object's __init__ that doesn't take any parameter, for us.
from __future__ import print_function
class MyParent(object):
def __init__(self, s):
super(MyParent, self).__init__()
class A(MyParent):
def __init__(self, s):
self.a = "a"
super(A, self).__init__(s)
def testA(self, x):
print(x)
class B(MyParent):
def __init__(self, s):
self.b = "b"
super(B, self).__init__(s)
def testA(self,x):
print(x)
C = type('C', (A, B), {})
x = C("test")
print(x.b)
You can define as many children to MyParent as you want, and then all __init__ methods will be called, provided you used super() correctly.

How to return a subclass from __new__ parameter

I have a class parent and two subclasses child1(parent) and child2(parent) sort of like the following near code.
(edited to more properly show that the parent class is doing something)
class parent(object):
name = None
def __init__(self,e):
# process the common attributes
name = e.attrib['name']
def __new__(cls,e):
if e.attrib['type'] == 'c1':
return child1(e)
elif e.attrib['type'] == 'c2':
return child2(e)
else:
raise
class child1(parent):
extra1 = None
def __init__(self,e):
super(e)
# set attributes from e that are specific to type c1
class child2(parent):
extra2 = None
def __init__(self,e):
super(e)
# set attributes from e that are specific to type c2
The goal is to be able to get the "right" class based on the value of the parameter. So if I can say obj = parent(element) and obj will be either child1 or child2 depending on what the value of element.attrib['type'] is.
The problem is that inside parent.__new__, you're calling child1(e), while calls child1.__new__, which finds the implementation in parent.__new__ and calls it with the same e, which calls child1(e), which… so you get infinite recursion.
There are better ways to design this, but if you just want to fix your design, there are three options:
If you define __new__ in all of your subclasses, it won't fall through the parent.__new__. You can do this in a single step by interposing an intermediate class between parent and childN, so you only need intermediate.__new__. Or use a mixin that they all inherit, or …
Get rid of the inheritance. Is there really any reason child1 is-a parent here?
You seem to be looking for what in Smalltalk/ObjC terms is called a "class cluster", and you don't need the "visible face" of the cluster to be the base class in Python any more than you do in those languages.
For example:
class base(object):
pass
class parent(base):
def __new__(cls, e):
# same as before
class child1(base):
# etc.
In Python, you can even make parent an ABC, and register each childN with it so you can use isinstance and friends with it.
Finally, you can just catch the recursion by only handling __new__ on parent, not its subclasses:
def __new__(cls, e):
if cls is not parent:
return super(parent, cls).__new__(cls)
This is a lot easier if you don't have parent be a class at all, but just a normal function.
Using the base class is a pattern from languages where that's the only real way to do this. It's not necessary or helpful in Python.

Python extending with - using super() Python 3 vs Python 2

Originally I wanted to ask this question, but then I found it was already thought of before...
Googling around I found this example of extending configparser. The following works with Python 3:
$ python3
Python 3.2.3rc2 (default, Mar 21 2012, 06:59:51)
[GCC 4.6.3] on linux2
>>> from configparser import SafeConfigParser
>>> class AmritaConfigParser(SafeConfigParser):
... def __init__(self):
... super().__init__()
...
>>> cfg = AmritaConfigParser()
But not with Python 2:
>>> class AmritaConfigParser(SafeConfigParser):
... def __init__(self):
... super(SafeConfigParser).init()
...
>>> cfg = AmritaConfigParser()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: must be type, not classob
Then I read a little bit on Python New Class vs. Old Class styles (e.g. here.
And now I am wondering, I can do:
class MyConfigParser(ConfigParser.ConfigParser):
def Write(self, fp):
"""override the module's original write funcition"""
....
def MyWrite(self, fp):
"""Define new function and inherit all others"""
But, shouldn't I call init? Is this in Python 2 the equivalent:
class AmritaConfigParser(ConfigParser.SafeConfigParser):
#def __init__(self):
# super().__init__() # Python3 syntax, or rather, new style class syntax ...
#
# is this the equivalent of the above ?
def __init__(self):
ConfigParser.SafeConfigParser.__init__(self)
super() (without arguments) was introduced in Python 3 (along with __class__):
super() -> same as super(__class__, self)
so that would be the Python 2 equivalent for new-style classes:
super(CurrentClass, self)
for old-style classes you can always use:
class Classname(OldStyleParent):
def __init__(self, *args, **kwargs):
OldStyleParent.__init__(self, *args, **kwargs)
In a single inheritance case (when you subclass one class only), your new class inherits methods of the base class. This includes __init__. So if you don't define it in your class, you will get the one from the base.
Things start being complicated if you introduce multiple inheritance (subclassing more than one class at a time). This is because if more than one base class has __init__, your class will inherit the first one only.
In such cases, you should really use super if you can, I'll explain why. But not always you can. The problem is that all your base classes must also use it (and their base classes as well -- the whole tree).
If that is the case, then this will also work correctly (in Python 3 but you could rework it into Python 2 -- it also has super):
class A:
def __init__(self):
print('A')
super().__init__()
class B:
def __init__(self):
print('B')
super().__init__()
class C(A, B):
pass
C()
#prints:
#A
#B
Notice how both base classes use super even though they don't have their own base classes.
What super does is: it calls the method from the next class in MRO (method resolution order). The MRO for C is: (C, A, B, object). You can print C.__mro__ to see it.
So, C inherits __init__ from A and super in A.__init__ calls B.__init__ (B follows A in MRO).
So by doing nothing in C, you end up calling both, which is what you want.
Now if you were not using super, you would end up inheriting A.__init__ (as before) but this time there's nothing that would call B.__init__ for you.
class A:
def __init__(self):
print('A')
class B:
def __init__(self):
print('B')
class C(A, B):
pass
C()
#prints:
#A
To fix that you have to define C.__init__:
class C(A, B):
def __init__(self):
A.__init__(self)
B.__init__(self)
The problem with that is that in more complicated MI trees, __init__ methods of some classes may end up being called more than once whereas super/MRO guarantee that they're called just once.
In short, they are equivalent.
Let's have a history view:
(1) at first, the function looks like this.
class MySubClass(MySuperClass):
def __init__(self):
MySuperClass.__init__(self)
(2) to make code more abstract (and more portable). A common method to get Super-Class is invented like:
super(<class>, <instance>)
And init function can be:
class MySubClassBetter(MySuperClass):
def __init__(self):
super(MySubClassBetter, self).__init__()
However requiring an explicit passing of both the class and instance break the DRY (Don't Repeat Yourself) rule a bit.
(3) in V3. It is more smart,
super()
is enough in most case. You can refer to http://www.python.org/dev/peps/pep-3135/
Just to have a simple and complete example for Python 3, which most people seem to be using now.
class MySuper(object):
def __init__(self,a):
self.a = a
class MySub(MySuper):
def __init__(self,a,b):
self.b = b
super().__init__(a)
my_sub = MySub(42,'chickenman')
print(my_sub.a)
print(my_sub.b)
gives
42
chickenman
Another python3 implementation that involves the use of Abstract classes with super(). You should remember that
super().__init__(name, 10)
has the same effect as
Person.__init__(self, name, 10)
Remember there's a hidden 'self' in super(), So the same object passes on to the superclass init method and the attributes are added to the object that called it.
Hence super()gets translated to Person and then if you include the hidden self, you get the above code frag.
from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
name = ""
age = 0
def __init__(self, personName, personAge):
self.name = personName
self.age = personAge
#abstractmethod
def showName(self):
pass
#abstractmethod
def showAge(self):
pass
class Man(Person):
def __init__(self, name, height):
self.height = height
# Person.__init__(self, name, 10)
super().__init__(name, 10) # same as Person.__init__(self, name, 10)
# basically used to call the superclass init . This is used incase you want to call subclass init
# and then also call superclass's init.
# Since there's a hidden self in the super's parameters, when it's is called,
# the superclasses attributes are a part of the same object that was sent out in the super() method
def showIdentity(self):
return self.name, self.age, self.height
def showName(self):
pass
def showAge(self):
pass
a = Man("piyush", "179")
print(a.showIdentity())

Inheriting properties of a separate class in Python

I am instantiating a class inside another one:
class A(F):
def __init__(self):
return
b = B()
Class B also inherits class F:
class B(F):
def __init__(self):
return
There are properties of F which have been defined in class A, which I need to access inside class B. (a MySQL connection and a logging handler.)
I would like B to have the properties which have been set to F, when they were instantiated initially in A, so I can use the logging/mysql handlers inside B without re-instantiating them.
How can I go about this? Sorry if the question is unclear.
Put the stuff you want to share in F and both A and B will be able to share it. Eg
class F(object):
def useful(self):
pass
class A(F):
def something(self):
self.useful()
class B(F):
def something_else(self):
self.useful()

Categories