Python class instance variables inheritance mechanics - python

If I run this code:
class Super:
def __init__(self, name):
self.name = name
class Sub(Super):
def publ(self):
print("works")
a = Sub()
as planned it succesfully fails with this message:
TypeError: __init__() takes exactly 2 arguments (1 given)
but it gives me bugging question:
Where the information is stored about Sub class requirement for the argument entry? I mean how SUB() knows that it needs "name" as the argument?
What is mechanism defining how it inherits "that knowledge" from Super().

The Python Tutorial has a section on inheritance.
if a requested attribute is not found in the class, the search proceeds to look in the base class. This rule is applied recursively if the base class itself is derived from some other class.
What happens when you call Sub() is that the interpreter looks in the class object for an attribute named __init__. If it finds one it will use it. If it doesn't find it (which is the case in your example), it will search through the class's base classes. In your example it finds the __init__ method inherited from class Super. The interpreter will proceed to call that method.
You can fix your example in either of the following ways:
a = Sub('John Doe')
or:
class Sub(Super):
def __init__(self):
super(Sub, self).__init__("My Name Is Sub")
def publ(self):
print("works")

Related

Calling a Parent class constructor with super() vs using Parent class name

Why is it so that when I try to call the Parent class constructor using super(), I don't need to pass 'self' as an argument:
super().__init__(x,y)
Yet when I call it using the Parent class itself (named Parent in this case), a 'self' argument needs to be passed.
Parent.__init__(self,x,y)
(x and y are Parent class attributes here)
Just want to understand the background logic here. Thanks!
This is because super() can only be called inside a class method definition (like in __init__), and it always refers to itself. Therefore, there is no need, it is redundant.
Interesting you mention it: self used to be required just a few years ago (maybe 5... I cannot remember).
Think of it like calling methods. If I have the following class:
class Conversation:
def __init__(self):
pass
def hi(self, name):
print(f'How are you doing, {name}?')
convo = Conversation()
convo.hi('Jason')
(output): 'How are you doing, Jason?'
I did not have to specify self when calling convo.hi, I only had to pass an argument to name. Why? Because self is always needed, and therefore redundant.
Same idea with super(). :)

Reason for calling super class constructor using superclass.__init()__ instead of superclass()

I am a beginner in Python and using Lutz's book to understand OOPS in Python. This question might be basic, but I'd appreciate any help. I researched SO and found answers on "how", but not "why."
As I understand from the book, if Sub inherits Super then one need not call superclass' (Super's) __init__() method.
Example:
class Super:
def __init__(self,name):
self.name=name
print("Name is:",name)
class Sub(Super):
pass
a = Sub("Harry")
a.name
Above code does assign attribute name to the object a. It also prints the name as expected.
However, if I modify the code as:
class Super:
def __init__(self,name):
print("Inside Super __init__")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
Super(name) #Call __init__ directly
a = Sub("Harry")
a.name
The above code doesn't work fine. By fine, I mean that although Super.__init__() does get called (as seen from the print statements), there is no attribute attached to a. When I run a.name, I get an error, AttributeError: 'Sub' object has no attribute 'name'
I researched this topic on SO, and found the fix on Chain-calling parent constructors in python and Why aren't superclass __init__ methods automatically invoked?
These two threads talk about how to fix it, but they don't provide a reason for why.
Question: Why do I need to call Super's __init__ using Super.__init__(self, name) OR super(Sub, self).__init__(name) instead of a direct call Super(name)?
In Super.__init__(self, name) and Super(name), we see that Super's __init__() gets called, (as seen from print statements), but only in Super.__init__(self, name) we see that the attribute gets attached to Sub class.
Wouldn't Super(name) automatically pass self (child) object to Super? Now, you might ask that how do I know that self is automatically passed? If I modify Super(name) to Super(self,name), I get an error message that TypeError: __init__() takes 2 positional arguments but 3 were given. As I understand from the book, self is automatically passed. So, effectively, we end up passing self twice.
I don't know why Super(name) doesn't attach name attribute to Sub even though Super.__init__() is run. I'd appreciate any help.
For reference, here's the working version of the code based on my research from SO:
class Super:
def __init__(self,name):
print("Inside __init__")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
#Super.__init__(self, name) #One way to fix this
super(Sub, self).__init__(name) #Another way to fix this
a = Sub("Harry")
a.name
PS: I am using Python-3.6.5 under Anaconda Distribution.
As I understand from the book, if Sub inherits Super then one need not call superclass' (Super's) __init__() method.
This is misleading. It's true that you aren't required to call the superclass's __init__ method—but if you don't, whatever it does in __init__ never happens. And for normal classes, all of that needs to be done. It is occasionally useful, usually when a class wasn't designed to be inherited from, like this:
class Rot13Reader:
def __init__(self, filename):
self.file = open(filename):
def close(self):
self.file.close()
def dostuff(self):
line = next(file)
return codecs.encode(line, 'rot13')
Imagine that you want all the behavior of this class, but with a string rather than a file. The only way to do that is to skip the open:
class LocalRot13Reader(Rot13Reader):
def __init__(self, s):
# don't call super().__init__, because we don't have a filename to open
# instead, set up self.file with something else
self.file = io.StringIO(s)
Here, we wanted to avoid the self.file assignment in the superclass. In your case—as with almost all classes you're ever going to write—you don't want to avoid the self.name assignment in the superclass. That's why, even though Python allows you to not call the superclass's __init__, you almost always call it.
Notice that there's nothing special about __init__ here. For example, we can override dostuff to call the base class's version and then do extra stuff:
def dostuff(self):
result = super().dostuff()
return result.upper()
… or we can override close and intentionally not call the base class:
def close(self):
# do nothing, including no super, because we borrowed our file
The only difference is that good reasons to avoid calling the base class tend to be much more common in normal methods than in __init__.
Question: Why do I need to call Super's __init__ using Super.__init__(self, name) OR super(Sub, self).__init__(name) instead of a direct call Super(name)?
Because these do very different things.
Super(name) constructs a new Super instance, calls __init__(name) on it, and returns it to you. And you then ignore that value.
In particular, Super.__init__ does get called one time either way—but the self it gets called with is that new Super instance, that you're just going to throw away, in the Super(name) case, while it's your own self in the super(Sub, self).__init__(name) case.
So, in the first case, it sets the name attribute on some other object that gets thrown away, and nobody ever sets it on your object, which is why self.name later raises an AttributeError.
It might help you understand this if you add something to both class's __init__ methods to show which instance is involved:
class Super:
def __init__(self,name):
print(f"Inside Super __init__ for {self}")
self.name=name
print("Name is:",name)
class Sub(Super):
def __init__(self,name):
print(f"Inside Sub __init__ for {self}")
# line you want to experiment with goes here.
If that last line is super().__init__(name), super(Sub, self).__init__name), or Super.__init__(self, name), you will see something like this:
Inside Sub __init__ for <__main__.Sub object at 0x10f7a9e80>
Inside Super __init__ for <__main__.Sub object at 0x10f7a9e80>
Notice that it's the same object, the Sub at address 0x10f7a9e80, in both cases.
… but if that last line is Super(name):
Inside Sub __init__ for <__main__.Sub object at 0x10f7a9ea0>
Inside Super __init__ for <__main__.Super object at 0x10f7a9ec0>
Now we have two different objects, at different addresses 0x10f7a9ea0 and 0x10f7a9ec0, and with different types.
If you're curious about what the magic all looks like under the covers, Super(name) does something like this (oversimplifying a bit and skipping over some steps1):
_newobj = Super.__new__(Super)
if isinstance(_newobj, Super):
Super.__init__(_newobj, name)
… while super(Sub, self).__init__(name) does something like this:
_basecls = magically_find_next_class_in_mro(Sub)
_basecls.__init__(self, name)
As a side note, if a book is telling you to use super(Sub, self).__init__(name) or Super.__init__(self, name), it's probably an obsolete book written for Python 2.
In Python 3, you just do this:
super().__init__(name): Calls the correct next superclass by method resolution order. You almost always want this.
super(Sub, self).__init__(name): Calls the correct next superclass—unless you make a mistake and get Sub wrong there. You only need this if you're writing dual-version code that has to run in 2.7 as well as 3.x.
Super.__init__(self, name): Calls Super, whether it's the correct next superclass or not. You only need this if the method resolution order is wrong and you have to work around it.2
If you want to understand more, it's all in the docs, but it can be a bit daunting:
__new__
__init__
super (also see Raymond Hettinger's blog post)
method invocation (also see the HOWTO)
The original introduction to super, __new__, and all the related features was very helpful to me in understanding all of this. I'm not sure if it'll be as helpful to someone who's not coming at this already understanding old-style Python classes, but it's pretty well written, and Guido (obviously) knows what he's talking about, so it might be worth reading.
1. The biggest cheat in this explanation is that super actually returns a proxy object that acts like _baseclass bound to self in the same way methods are bound, which can be used to bind methods, like __init__. This is useful/interesting knowledge if you know how methods work, but probably just extra confusion if you don't.
2. … or if you're working with old-style classes, which don't support super (or proper method-resolution order). This never comes up in Python 3, which doesn't have old-style classes. But, unfortunately, you will see it in lots of tkinter examples, because the best tutorial is still Effbot's, which was written for Python 2.3, when Tkinter was all old-style classes, and has never been updated.
Super(name) is not a "direct call" to the superclass __init__. After all, you called Super, not Super.__init__.
Super.__init__ takes an uninitialized Super instance and initializes it. Super creates and initializes a new, completely separate instance from the one you wanted to initialize (and then you immediately throw the new instance away). The instance you wanted to initialize is untouched.
Super(name) instantiates a new instance of super. Think of this example:
def __init__(self, name):
x1 = Super(name)
x2 = Super("some other name")
assert x1 is not self
assert x2 is not self
In order to explicitly call The Super's constructor on the current instance, you'd have to use the following syntax:
def __init__(self, name):
Super.__init__(self, name)
Now, maybe you don't want read further if you are a beginner.
If you do, you will see that there is a good reason to use super(Sub, self).__init__(name) (or super().__init__(name) in Python 3) instead of Super.__init__(self, name).
Super.__init__(self, name) works fine, as long as you are certain that Super is in fact your superclass. But in fact, you don't know ever that for sure.
You could have the following code:
class Super:
def __init__(self):
print('Super __init__')
class Sub(Super):
def __init__(self):
print('Sub __init__')
Super.__init__(self)
class Sub2(Super):
def __init__(self):
print('Sub2 __init__')
Super.__init__(self)
class SubSub(Sub, Sub2):
pass
You would now expect that SubSub() ends up calling all of the above constructors, but it does not:
>>> x = SubSub()
Sub __init__
Super __init__
>>>
To correct it, you'd have to do:
class Super:
def __init__(self):
print('Super __init__')
class Sub(Super):
def __init__(self):
print('Sub __init__')
super().__init__()
class Sub2(Super):
def __init__(self):
print('Sub2 __init__')
super().__init__()
class SubSub(Sub, Sub2):
pass
Now it works:
>>> x = SubSub()
Sub __init__
Sub2 __init__
Super __init__
>>>
The reason is that the super class of Sub is declared to be Super, in case of multiple inheritance in class SubSub, Python's MRO establishes the inheritance as: SubSub inherits from Sub, which inherits from Sub2, which inherits from Super, which inherits from object.
You can test that:
>>> SubSub.__mro__
(<class '__main__.SubSub'>, <class '__main__.Sub'>, <class '__main__.Sub2'>, <class '__main__.Super'>, <class 'object'>)
Now, the super() call in constructors of each of the classes finds the next class in the MRO so that the constructor of that class can be called.
See https://www.python.org/download/releases/2.3/mro/

Why can't I call a method from my Python class?

I am learning Python and currently working with classes. I am trying to make a basic game to help learn it and am having a weird issue with calling methods
from it. I have the main.py file which creates an instance from the class in the Character.py file.
This is the Character.py file:
class Character:
name=""
def __init__(Name):
name=Name
def getName():
return name
This is the main.py file:
from Character import *
player = Character("James")
print(player.getName())
I am not sure what the issue is. This is the error I get:
Traceback (most recent call last):
File "C:\Users\dstei\Documents\Python\It 102\Final Project\Main.py", line
12, in <module>
print(player.getName())
TypeError: getName() takes 0 positional arguments but 1 was given
It is saying I am giving 1 positional argument but I don't see where I gave any. What am I missing?
Since you have a class with instance methods, you need to include the first argument (self by convention) to refer to the current instance. Also, make sure to set the variable as an instance variable by using self, the current instance:
class Character:
def __init__(self, Name): #self is the current instance
self.name=Name #set the variable on the instance so that every instance of Character has a name
def getName(self):
return self.name #refer to the name with the instance
Python internally passes the new instance of a class as the first argument to all the class methods, like this in languages such as Java. The error comes from the fact that Python passes the instance as the first argument internally but your getter is not defined to take an argument.
With the above code, when you call the method upon an instance, the instance is internally passed as the first argument and Python doesn't complain as you specify that it takes an argument, self, and name is set correctly on the instance.
Note: By convention, Python does not use camelCase, but underscores, so your getter should by convention look like this:
def get_name(self):
#...
Also see chepner's answer which explains why getters and setters aren't usually needed. Just get and modify the instance variable by using dot notation:
print(player.name) #get
player.name = "Jeff" #set
As others have mentioned, even instance method must be declared with an extra argument, typically named self (although that is a conventional, not a required, name).
class Character:
def __init__(self, name):
self.name = name
def get_name(self):
return name
However, Python does not have any kind of enforced visibility (such as public or private), so such trivial getters and setters aren't usually written. Documentation about which attributes you are "allowed" to modify are considered sufficient protection.
class Character:
def __init__(self, name):
self.name = name
c = Character("Bob")
print(c.name) # instead of c.get_name()
c.name = "Charlie" # instead of c.set_name("Charlie")
You are forgetting to add the parameter self. self is an object reference to the object itself, therefore, they are same. Python methods are not called in the context of the object itself. self in Python may be used to deal with custom object models or
class Character:
def __init__(self,name):
self.name=name
def getName(self):
return self.name
To see why this parameter is needed, there are so good answers here:
What is the purpose of self?

How to use method of one class to another class in different files in python

I am quite new to python, so pardon me for basic question. I tried google for past few days but could not make it in my program.
Can anyone show me a good example how can I use method from One class to another in python and what is significance of __init__ while defining class.
I am using python2.7
Thanks in anticipation.
To use a method defined in one class inside of another class, you have several options:
Create an instance of B from within one of A's methods, then call B's method:
class A:
def methodInA():
b = B()
b.methodInB()
If appropriate, use the concept of inheritance (one of the defining concepts of object-oriented design) to create a subclass of the original class whose method(s) you wish to use:
class B(A):
...
__init__() is a class initializer. Whenever you instantiate an object you are invoking __init__() whether or not it is explicitly defined. It's main purpose is to initialize class data members:
class C:
def __init__(self, name):
self.name = name
def printName(self):
print self.name
c = C("George")
c.printName() # outputs George
With __init__() defined, in particular with the additional argument name in this example, you are able to differentiate between would-be generically constructed instances by allowing for different initial states from instance to instance.
There are 2 issues here:
First: Using method of class A in class B, both classes in different files
class A:
def methodOfA(self):
print "method Of A"
let the above class be in file a.py Now the class B is supposed to be in b.py. Both a.py and b.py are assumed to be on the same level or in the same location. Then b.py would look like:
import a
class B:
def methodOfB(self):
print "Method of B"
a.A().methodOfA()
You can also do this by inherting A in B
import a
class B(a.A):
def methodOfB(self):
print "Method of B"
self.methodOfA()
there are several other ways to use A in B. I will leave it to you to explore.
Now to your second question. The use of __init__ in a class. __init__ is not a constructor, as popularly believed and explained above. It is, as the name suggests, an initialization function. It is called only after the object has already been constructed and it is implicitly passed the object instance as the first argument, as signified by self in its argument list.
The actual constructor in python is called __new__, which does not need a object to call it. This is actually a specialized Static method, which receives the class instance as the first argument. __new__ is exposed for overwriting only if the class inherits form the object base class of python
Whatever other arguments are passed while creating an object of a class, first go to __new__ and then are passed with the object instance to the __init__, if it accepts them.
The init function is what is called a constructor function. When you create an instance of a class object = myClass(), init is the function that is automatically called. i.e.
That being said, to call a function from one class to another, you need to call an instance of the second class inside the first one, or vice versa. for eg.
class One():
def func(self):
#does sometthing here
class Two():
def __init__(self):
self.anotherClass = One()
#Now you can access the functions of the first class by using anotherClass followed by dot operator
self.anotherClass.func()
#When you call the main class. This is the time the __init__ function is automatically called
mainClass = Two()
Another way to access from another class is the use of oop concept called Inheritance.
class One():
def __init__(self):
print('Class One Called')
def func(self):
print('func1 Called')
class Two(One):
def __init__(self):
One.__init__(self,) #This basically creates One's instance
print('Main Called')
c= Two()
c.func()
The output for this is:
Class One Called
Main Called
func1 Called

How to call function that is in the other class python for pytest

I am trying to call the methods in CSVDatasource in my testing class by typing this code from ETL.CSVDatasource import CSVDatasource and to call the necessary methods but I have been receiving errors like TypeError: unbound method preprocess_col() must be called with CSVDatasource instance as first argument (got DataFrame instance instead)
http://imgur.com/8sfygtA -> Image of my coding path
Anyone can guide me on calling out the method in the other class so that I can call the method and do testing in my testing classs?
Thanks.
Generally, an instance of the class has to be created before calling the method of the class. For example,
class Person:
def __init__(self,name):
self.name=name
def who(self):
print 'I am {}'.format(self.name)
#staticmethod
def species():
print 'I am human.'
If we want to call the method who inside the class Person, we have to create an instance of class as follows:
if __name__=='__main__':
p1=Person('Qing Yong')
p1.who() #I am Qing Yong
However, if a method doesn't require self but you want to put it inside the class as this method may strongly related to you class in some senses. You may declare it as static method by using the decorator #staticmethod, like the method species
This static method can be called either through instance or through class directly as follows.
if __name__=='__main__':
p1.species() #I am human.
Person.species() #I am human.
Depending on the context of your code, you may choose either way to use the method inside your class.

Categories