How can I reuse another classes' method without inheritance in Python 2? - python

Two of my classes need to have the same method, but they are not related by inheritance.
The following works in Python 3:
class A(object):
def __init__(self):
self.x = 'A'
def printmyx(self):
print(self.x)
class B(object):
def __init__(self):
self.x = 'B'
printmyx = A.printmyx
a = A()
b = B()
a.printmyx()
b.printmyx()
and prints
A
B
However, in Python 2 I'm getting
Traceback (most recent call last):
File "py2test.py", line 18, in <module>
b.printmyx()
TypeError: unbound method printmyx() must be called with A instance as first argument (got nothing instead)
I think the problem is that in Python 3 printmyx is just a regular function while in Python 2 it's an unbound method.
How to make the code work in Python 2?
edit
In my real code, A and B inherit from different parent classes. They need to share one helper method but have no other relation to each other.

Bear in mind that Python does support multiple inheritance, so it's very possible to define a mixin class and have both A and B inherit from it without disturbing the main inheritance hierarchy. I understand you're saying the classes have little in common - but they do both have a variable called x and a method to print it - and to me at least, that's enough in common to consider using inheritance.
But that said, another way to do this is using a class decorator to add the common method:
def add_printmyx(original_class):
def printmyx(self):
print (self.x)
original_class.printmyx = printmyx
return original_class
#add_printmyx
class B(object):
def __init__(self):
self.x = 'B'
b = B()
b.printmyx()
The class decorator takes the original class and adds (or replaces) a printmyx method that prints the contents of x.

Apparently, in Python 2 the original function an unbound method was created from is stored in the im_func attribute.1
To make the code work in Python 2 like it does in Python 3, use
printmyx = A.printmyx.im_func
in B's body.
1 Described in the The standard type hierarchy
section of the Python 2 Data Model documentation.

Why is inheritance not allowed? This is the perfect use case for inheritance.
class Common(object):
def printmyx(self):
print(self.x)
class A(Common):
def __init__(self):
self.x = 'A'
class B(Common):
def __init__(self):
self.x = 'B'
a = A()
b = B()
a.printmyx()
b.printmyx()

Related

Calling a variable from a classmethod function to a normal method

I would like to call a variable from a classmethod to a different method inside the same class:
class A():
#classmethod
def b(cls):
cls.g = 5
def c(self):
if self.g < 1:
print("TestA")
else:
print("TestB")
When doing:
x = A()
x.c()
I get:
AttributeError: 'A' object has no attribute 'g'
I've read and searched for a similar case but haven't found one. Most deal with calling variables from the init method and that doesn't apply here.
If you don't run .b() beforehand, your .g doesn't exist,...at all.
Add an __init__ function to your class and declare .g there to make sure it exists at least.
You did not define g as a class attribute of the class A. This could be done this way:
class A():
g = 7
but then in your code you are treating g as instance (self.g) and class variable (cls.g) at the same time. While this works (self.g will refer to cls.g) it may be confusing.

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 pass arguments to python function whose first parameter is self?

Take the following simplified example.
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
print(param+self.variable_A)
print(A.functionA(3))
In the above example, I get the following error
Traceback (most recent call last):
File "python", line 8, in <module>
TypeError: functionA() missing 1 required positional argument: 'param'
But, if I remove the self, in the function declaration, I am not able to access the variables variable_A and variable_B in the class, and I get the following error
Traceback (most recent call last):
File "python", line 8, in <module>
File "python", line 6, in functionA
NameError: name 'self' is not defined
So, how do I access the class variables and not get the param error here?
I am using Python 3 FYI.
You must first create an instance of the class A
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
return (param+self.variable_A)
a = A()
print(a.functionA(3))
You can use staticmethod decorator if you don't want to use an instance.
Static methods are a special case of methods. Sometimes, you'll write code that belongs to a class, but that doesn't use the object itself at all.
class A(object):
variable_A = 1
variable_B = 2
#staticmethod
def functionA(param):
return (param+A.variable_A)
print(A.functionA(3))
Another option is to use classmethod decorator.
Class methods are methods that are not bound to an object, but to a class!
class A(object):
variable_A = 1
variable_B = 2
#classmethod
def functionA(cls,param):
return (param+cls.variable_A)
print(A.functionA(3))
functionA in your snippet above is an instance method. You do not pass "self" directly to it. Instead, you need to create an instance in order to use it. The "self" argument of the function is, in fact, the instance it's called on. E.g.:
a = A()
a.functionA(3)
P.S.
Note that your functionA calls print but doesn't return anything, meaning it implicitly returns None. You should either have it return a value and print it from the caller, or, as I have done above, call it and let it print on its own.
Create an object of A first.
a = A()
a.functionA(3)
When a function object (what the def statement creates) is an attribute of a class AND is looked up (using the obj.attrname scheme) on the class or an instance of the class, it gets turned into a method object. This method object is itself a callable. If the lookup happens on an instance, this instance will be "magically" inserted as the first argument to the function. If not, you will have to provide it by yourself (just like you would for any other argument).
You can read more about this (and how the "magic" happens here: https://wiki.python.org/moin/FromFunctionToMethod
In your case, you lookup the function on the class, so it expects two arguments (self and param), but you only pass param, hence the error.
You defined variable_A and variable_B as class attributes (attributes that will be shared between all instances of the class). If that's really the intention, and you want a method you can call on the class itself and that will be able to access class attributes, you can make functionA a classmethod (it works the same as an "instance" method except it's the class that is 'magically' inserted as first argument):
class A(object):
variable_A = 1
variable_B = 2
#classmethod
def functionA(cls, param):
return param + cls.variable_A
Then you can call functionA either directly on the class itself:
print(A.functionA(42))
or on an instance if you already have one at hand:
a = A()
# ...
print(a.functionA(42))
Now if you really wanted variable_A and variable_B to be per-instance attributes (each instance of A has it's own distinct variables), you need to 1/ create those attributes on the instance itself in the initialier method and 2/ call functionA on some A instance, ie:
class A(object):
def __init__(self, variable_A=1, variable_B=2):
self.variable_A = variableA
self.variable_B = variableB
def functionA(self, param):
return param + self.variable_A
a1 = A() # using default values
print(a1.functionA(42))
a2 = A(5) # custom value for variable_A
print(a2.functionA(42))
class A(object):
variable_A = 1
variable_B = 2
def functionA(self, param):
print(param+self.variable_A)
A().functionA(3)
A() is calling the class to create an instance
4
[Program finished]
You can use return in function and then print at last.
Posting this answer as per OP template , accepted answers and other answers are recommended way to do it.

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())

nested classes in Python

Dealing with classes (nested etc) does not look easy in Python, surprisingly! The following problem appeared to me recently and took several hours (try, search ...) without success. I read most of SO related links but none of them has pointed the issue presented here!
#------------------------------------
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
class C:
class A:
def __init__(self):
self.a = 'a'
print self.a
class B(A):
def __init__(self):
self.b = 'b'
A.a = 'a_b'
print self.b, A.a
#------------------------------------
#------------------------------------
>>> c1 = A()
a
>>> c1.a
'a'
>>> c2 = B()
b
>>> c2.a, c2.b
('a_b', 'b')
>>> c3 = C()
>>> c4 = c3.A()
a
>>> c4.a
'a'
>>> c5 = c3.B()
b a_b
>>> c5.b
'b'
>>> c5.a
Traceback (most recent call last):
File "", line 1, in
AttributeError: B instance has no attribute 'a'
Where is the problem in the code?
AND
In both cases it seems that when B(A) is initialized A() is not initialized. What is the solution for this issue? Note that the term A.__init__() being called inside B()'s __init__() does not work!
Updates:
class Geometry:
class Curve:
def __init__(self,c=1):
self.c = c #curvature parameter
print 'Curvature %g'%self.c
pass #some codes
class Line(Curve):
def __init__(self):
Geometry.Curve.__init__(self,0) #the key point
pass #some codes
g = Geometry()
C = g.Curve(0.5)
L = g.Line()
which results in:
Curvature 0.5
Curvature 0
what I was looking for.
The code executed in a method runs in the local scope of that method. If you access an object that is not in this scope, Python will look it up in the global/module scope, NOT in the class scope or the scope of any enclosing class!
This means that:
A.a = 'a_b'
inside C.B.__init__ will set the class attribute of the global A class, not C.A as you probably intended. For that you would have to do this:
C.A.a = 'a_b'
Also, Python will not call parent methods if you override them in subclasses. You have to do it yourself.
The scoping rules mean that if you wanted to call the __init__ method of the parent class inside C.B.__init__, it has to look like this:
C.A.__init__(self)
and NOT like this:
A.__init__(self)
which is probably what you've tried.
Nested classes seems so unpythonic, even if considered as factories. But to answer your question: There simply is no c5.a (instance of C.B). In the init-method of C.B you add to the CLASS C.A an attribute a, but not to C.B! The class A does already have an attribute a, if instantiated! But the object of class B (and even the class) doesn't!
You must also keep in mind, that __init__ is not an constructor like in C++ or Java! The "real constructor" in python would be __new__. __init__ just initializes the instance of a class!
class A:
c = 'class-attribute'
def __init__(self):
self.i = 'instance-attribute'
So in this example c is a class-attribute, where i is an attribute of the instance.
Even more curios, is your attempt to add an attribute to the baseclass at the moment of the instantiation of the child-class. You are not getting a "late" inheritance-attribute that way.
You simply add to the class A an additional attribute, which surprises me to even work. I guess you are using python 3.x?
The reason for this behaviour? Well, i guess it has to do with pythons neat feature that in python definitions are executed(AFAIK).
The same reason why:
def method(lst = []):
is almost ever a bad idea. the deafult-parameter gets bound at the moment of the definition and you won't generate a new list-object every-time you call the method, but reusing the same list-object.

Categories