class One:
def __init__(self):
self.one = 1
class Two:
def __init__(self):
self.two = 2
class Three(One, Two):
def __init__(self):
self.three = 3
super().__init__()
obj = Three()
print(obj.one)
print(obj.two)
print(obj.three)
i am currently self learning OOP, i am having a hard time understanding why the object was able to print the attribute from Class One but not also the one from Class Two despite me using the super function, the error raised is AttributeError: 'Three' object has no attribute 'two'. How do we inherit from multiple classes?
super().__init__() in Three will only refer to the first class in Method Resolution Order. In order to call all the __init__, you'd need to do super() in all of them:
class One:
def __init__(self):
self.one = 1
super().__init__()
class Two:
def __init__(self):
self.two = 2
super().__init__()
Or, if you don't want to / can't modify parent class signatures, you refer to the classes directly instead of using super, so they will all be called regardless of MRO:
class Three(One, Two):
def __init__(self):
self.three = 3
One.__init__()
Two.__init__()
class One:
def __init__(self):
self.one = 1
super().__init__()
class Two:
def __init__(self):
self.two = 2
super().__init__()
class Three(One,Two):
def __init__(self):
self.three = 3
super().__init__()
obj = Three()
print(obj.one)
print(obj.two)
print(obj.three)
use super().__init__method in every class.
if you dont want to use it. you can call init methods of class one and class two in init method of class three.
Related
Suppose I have the following class inheriting from classes A and B:
class A:
def __init__(self):
self.x = 2
class B:
def __init__(self,u):
self.y = u + 2
class C(A,B):
def __init__(self):
#self.y should be 4 here
How do I initialize B only after initializing A? Using super(C,self).__init__() doesn't let me use attributes of A into B.
You don't HAVE to use super.
class A:
def __init__(self):
self.x = 2
class B:
def __init__(self,u):
self.y = u + 2
class C(A,B):
def __init__(self):
A.__init__(self)
B.__init__(self, self.x)
Now, that does mean some pretty tight coupling, in that C has to be way too aware of what A.__init__ does.
Just do this:
class A:
def __init__(self):
print("A was initialized")
self.x = 2
def getX(self):
return self.x
class B:
def __init__(self, u):
print("B was initialized")
self.u = u +2
class C(A,B):
def __init__(self, **kw):
A.__init__(self)
B.__init__(self, self.getX())
Alternatively, with super:
class A:
def __init__(self):
self.x = 2
class B:
def __init__(self,u):
self.y = u + 2
class C(A,B):
def __init__(self):
super().__init__()
super(A, self).__init__(self.x)
If you want to pass attributes in between classes use something like a return statement. Then in your B class use whatever the A class returned.
Let suppose we have defined two classes:
class A():
def __init__(self):
self.a = 0
class B():
def __init__(self):
self.b = 0
Now, we want to define a third class C that inherits from A and B:
class C(A, B):
def __init__(self):
A.__init__(self) # how to do this using super()
B.__init__(self) # how to do this using super()
You did not specify whether you are Python 2 or Python 3 and it matters as we shall see. But either way, if you will be using super() in a derived class to initialize the base classes, then the base classes must use super() also. So,
For Python 3:
class A():
def __init__(self):
super().__init__()
self.a = 0
class B():
def __init__(self):
super().__init__()
self.b = 0
class C(A, B):
def __init__(self):
super().__init__()
For Python 2 (where classes must be new-style classes) or Python 3
class A(object):
def __init__(self):
super(A, self).__init__()
self.a = 0
class B(object):
def __init__(self):
super(B, self).__init__()
self.b = 0
class C(A, B):
def __init__(self):
super(C, self).__init__()
I have these classes :
class Game():
def __init__(self):
self.player = Player("name")
class Turn():
def __init__(self):
pass
def end(self):
#how update xp player
class Player():
def __init__(self, name):
self.name = name
self.xp = 0
And my question is how when turn is terminated, how update the player
If these classes are in the same module you can define a global variable in the module that can be used in both classes. Otherwise you need to explicitly pass the variable on the instances of both classes.
Your Game class appears to have a reference to a player in its self.player attribute. If you want to update that player's xp attribute, you can do something like this:
def end(self):
self.player.xp += 5
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color):
self.color = color
fido = Dog(legs = 4, color = "brown")
This would spute out an error message. How would I do something like that where I add parameters to the subclass that doesn't pertain to the superclass.
Try this:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, legs, color):
super().__init__(legs)
self.color = color
fido = Dog(legs=4, color="brown")
That's not how inheritance works. When you inherit from another class, the super-class's parameters are not automatically added to the sub-class's parameter list. You must explicitly accept the desired parameters in your sub-class's constructor and pass them on to the super class's constructor:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")
Here's an example from a tutorial which explains inheritance and shows how to do this. You need to call the parent class's init function as in this similar example from this tutorial:
class Pet(object):
def __init__(self, name, species):
self.name = name
self.species = species
def getName(self):
return self.name
def getSpecies(self):
return self.species
def __str__(self):
return "%s is a %s" % (self.name, self.species)
class Dog(Pet):
def __init__(self, name, chases_cats):
Pet.__init__(self, name, "Dog")
self.chases_cats = chases_cats
def chasesCats(self):
return self.chases_cats
You still have to pass in the legs argument for Dog, and then use super:
class Wolf:
def __init__(self, legs):
self.legs = 4
class Dog(Wolf):
def __init__(self, color, legs):
super().__init__(legs)
self.color = color
fido = Dog(legs = 4, color = "brown")
So I have a class, Application, with 2 subclasses MyButton and MyLabel. Application also has
self.backgroundcolor = 'orange'
self.textcolor = 'black'
I want to use these two variables in my subclasses MyButton and MyLabel. So, I tried
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
but it doesn't work, saying that
TypeError: super(type, obj): obj must be an instance or subtype of type
But my Application class looks like
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
MORE STUFF HERE...
class MyButton(Button):
def __init__(self, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = super(Application, self).backgroundcolor
self['fg'] = super(Application, self).textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = super(Application, self).textcolor
Nested class cant access directly attributes of outer class. You need to do it indirectly, for example:
class Application(Frame):
global yearcal
def __init__(self, master=None):
Frame.__init__(self, master)
self.month = 5
self.year = 2014
self.color_clicked = 'lightskyblue'
now = datetime.datetime.now()
self.thisyear = now.year
self.thismonth = now.month
self.today = now.day
self.textcolor = 'purple'
self.bgcolor = 'gray'
self.today_color = 'palegreen1'
self.apt_color = 'light coral'
# create button and label and pass the application instance
# so that they can reference its attributes and methods
self.my_button = MyButton(self)
self.my_label = MyLabel(self)
class MyButton(Button):
def __init__(self, app_instance, *args, **kwargs):
Button.__init__(self, *args, **kwargs)
self['bg'] = app_instance.backgroundcolor
self['fg'] = app_instance.textcolor
self['relief'] = FLAT
class MyLabel(Label):
def __init__(self, app_instance, *args, **kwargs):
Label.__init__(self, *args, **kwargs)
self['fg'] = app_instance.textcolor
So I have a class, Application, with 2 subclasses MyButton and MyLabel.
MyButton and MyLabel do not appear to be subclasses of Application. You can only call super() to access Application from subclasses.
For example, to make MyLabel a subclass of Application
class MyLabel(Application):
def __init__(self, *args, **kwargs):
...
In the call to super() you have to pass your type and "yourself" (self):
so instead of doing:
super(Application, self)
you should do:
super(MyButton, self)
Hence the error, obj must be an instance or subtype of type:
self is not an instance nor subtype of Application
There is a confusion in this example between a nested class, which is a class defined within another class, and a subclass, which is a class defined by extending an existing class.
In Python, class inheritance (upon which the super method is based) is accomplished by defining a class with the super class provided as a parameter:
class Application(Frame):
... #Some class definition
class MyButton(Application):
... #MyButton is now a subclass of Application.
But this is not what you want, since you want to inherit the behaviour of the Tkinter Button and Label classes for your MyButton and MyLabel classes respectively (rather than inheriting the behaviour of the Application class.
You original attempt using nested classes doesn't appear as necessarily a bad idea, since it would neatly package all of the behaviour of your class into one place, but it has serious drawbacks which probably aren't what you want.
For a start, you cannot reference an instance of the Application class from the nested classes without injecting it in somehow, such as during initialisation. You can, however, access the properties if they are class properties, defined in the Application class namespace, just like you nested classes. This is probably confusing so here is an example:
class Application(object):
classvar = "Foo" #This is a class level variable
class NestedClass(object):
#This is now a nested class, accessed using Application.NestedClass
def __init__(self, app):
#This will fail, classvar doesn't exist in this scope
self.var = classvar
#This will work if app is an instance of Application
#Or if app is the class itself
self.var = app.classvar
#This will always work, since it references the Application
#class directly but it won't capture changes introduced
#by an instance of Application, which is what you'll probably do
self.var = Application.classvar
Class level behaviour becomes very confusing due to scoping, and nested classes are even more confusing for nothing that can't be gained from implementing every class at the module level.
The best way to inject this kind of requirement is to do it the very way Tkinter does it itself. Hand off the Application instance as the master of the widget instance it creates. This is shown in Marcin's answer.