Could you explain this code excerpt? I am learning and trying to get in touch with the logic of classes.
class NewWindow(tk.Toplevel):
def __init__(self):
super().__init__()
self.title("Another window")
Could you explain this code excerpt? I am learning and trying to get in touch with the logic of classes.
class NewWindow(tk.Toplevel):
def __init__(self):
super().__init__()
self.title("Another window")
The __init__
method initialises a new instance (object) of the class NewWindow
. The actual creation of the instance is done by a separate method __new__
, before __init__
is called. See the documentation for more details.
In the __init__
method the first statement, super().__init__()
, is a call to the __init__
method of the superclass, tk.TopLevel
- the NewWindow
instance would then acquire any attributes being defined in the superclass __init__
. And the last statement, self.title("Another Window")
, calls the title
method on the NewWindow
instance with the value "Another Window"
.
class NewWindow(tk.Toplevel): # Override the class "tk.Toplevel" with these functions.
def __init__(self): # Runs every time an instance of the class is run.
super().__init__() # This runs the parent class __init__ function in tk.Toplevel
self.title("Another window")
mytitle = "Stuff" # This variable is only available in this function __init__.
I’m newer to Python so constructive criticism is welcome.
So if super().__init__()
calls the initializer from the tk.Toplevel
superclass, it means the Toplevel widget is initialized, right?
So NewWindow
class sems to be about initializing Toplevel widget. So if we dont set tk.Toplevel
as a class parameter, we would not get parameters from the method which is called from superclass.
And what if I replace super().__init__()
by for example tk.Toplevel.__init__()
?
So self in def __init__(self):
is a a parameter variable, which calls the class itself to the __init__ method
? Which mean NewWindow class initialize itself. The title method is from where? Isnt it from tk.Toplevel
as we initalize Toplevel within the class before?
Did you try it? What happened?
Well, probably all variables of tk.Toplevel
are available from NewWindow class then, right?
TypeError: Toplevel.__init__() missing 1 required positional argument: 'self'
Umm, it would need an argument?
What does tk.TopLevel.__init__
look like? Without that it is unclear what actual initialisation if any that the superclass is doing? The fact you call self.title(...)
in NewWindow.__init__
implies title
is inherited from tk.TopLevel
or its superclasses, assuming you haven’t defined it anywhere else in the NewWindow
class.
Also, self
is a name for the NewWindow
instance.
No, self.title(...)
is a call to the title
method of self
, which represents the NewWindow
instance being initialised. The parentheses after title
indicate a function call, which in this case accepts a string.
I see you are right now. Is self.title()
a built-in function for that class tk.Toplevel?
I haven’t seen tk.Toplevel in my tutorials. Is this a visible window widget with a titlebar?
Probably, but I don’t know tkinter - you can check the Python docs
It doesn’t override the methods of tk.Toplevel
but inherits them, which then can be overridden in the class that inherited them.
Not quite, that would be the __call__
method, __init__
is only run when a new instance of a class is created.
No, self.title()
is a method inherited from tk.Toplevel
. Tho tkinter
comes with Python and there is no clear definition of what a built-in is, so you can consider it built-in I guess .
Well, yes, it’s just a window, see the definition of a top-level window by Microsoft:
A window that has no parent, or whose parent is the desktop window, is called a top-level window .
- About Windows - Win32 apps | Microsoft Learn
Hope this helped to improve your knowledge
Thank you, I really do appreciate being corrected.
What exactly do you mean by that? self
is the instance of a class that is normally automatically given by Python when calling a method from an instantiated object, and it doesn’t call functions or classes. Instantiated meaning so much as created. For example, if you have a blueprint for a building, you can’t just use the staircase or elevator, because well… it is just a blueprint. The same goes for classes they’re essentially just blueprints for something, and without creating (instantiating) an object from it you can’t use attributes/methods associated with it.
That could work but you would need to give tk.Toplevel.__init__()
self
as an argument, otherwise, it has no instance and thus can’t use functions or attributes in nothing[1].
I suggest you read this article: Inheritance and Composition: A Python OOP Guide – Real Python
That would also be a case where Python doesn’t automatically sets self
because this is just not the intended way of calling this method ↩︎
Assuming we are inheriting from tk.Toplevel
, and we want to make a modification to the constructor init()
from the inheriting super class, we can use either of the two ways:
1. super().__init__()
or
2. tk.Toplevel.__init__(self)
Note that for this particular example, you only require self
. In other classes, depending on their particular design, they may require additional arguments besides only self
. This applies to the __init__
method as well.
The following is a simple example of how the __init__
constructor from the superclass Reindeer
is being modified by the subclass Rudolph
:
class Reindeer:
def __init__(self, hoof, tail, nose):
self.hoof = hoof
self.tail = tail
self.nose = nose
def print_me(self):
print('Hoof: ', self.hoof)
print('Tail: ', self.tail)
print('Nose: ', self.nose)
class Rudolph(Reindeer):
def __init__(self, hoof): # Redefine constructor -
Reindeer.__init__(self, hoof, 'White', 'Red')
# Create class instances (objects)
generic_deer = Reindeer('Oval', 'Brown', 'Black')
Rudy = Rudolph('Round')
# Print
generic_deer.print_me()
print()
Rudy.print_me()
Since Rudolph the red nosed reindeer has a red nose and a white tail, we don’t have to include that every time that we create an object from it. We only have to state the type of hoofs that we wish it to have. In the superclass Reindeer
, all three arguments have to be provided. Thus, by using the following statements:
def __init__(self, hoof): # Redefine constructor - only need one argument at time of instantiation
Reindeer.__init__(self, hoof, 'White', 'Red') # Two arguments are pre-filled
We have modified the __init__
constructor in the subclass.
Hope this helps.
A key point that others have referred to but I want to emphasize a bit more, as it seems to be at the root of a lot of your confusion here and is crucial for understanding what’s going on here, is understanding the difference between a class and an instance of a class. Let’s suppose we have the following classes:
class Vehicle:
def __init__(self, speed):
self.speed = speed
class Motorcycle(Vehicle)
def __init__(self, speed, sidecar_attached):
super().__init__(speed)
self.sidecar_attached = sidecar_attached
def detach_sidecar(self):
if self.sidecar_attached:
import webbrowser; webbrowser.open("https://youtu.be/YC-MR84S1H8?t=58")
self.sidecar_attached = False
Motorcycle
is a class; it refers to a type (or class) of object. By contrast, if we then ran the following code:
my_motorcycle = Motorcycle(speed=120, sidecarattached=False)
your_motorcycle = Motorcycle(speed=50, sidecarattached=True)
we would have created two Motorcycle
instances, my_motorcycle
and your_motorcycle
, corresponding to two individual motorcycles. The class Motorcycle
has the consistent set of methods (things the object can do, such as accelerate, brake, honk, detach the sidecar, etc.), and attributes (data the object holds, such as a speed, tire pressure, sidecar status, etc.), per the class definition. However, the values of those (instance) attributes are unique to each instance of Motorcycle
.
In other words, a class is like a model of vehicle, while an instance is one particular vehicle. Every vehicle of the same model has the the same basic functions and attributes, but each individual vehicle has its own speed, tire pressure, mileage, etc (attribute values), and calling a (instance) method on a specific vehicle affects the attribute values (or “state”) of that instance—for example, calling your_motorcycle.detach_sidecar()
modifies that motorcycle’s value of sidecar_attached
(your_motorcycle.sidecar_attached
), but doesn’t affect others’.
Behind the scenes, when you call (instance) methods like your_motorcycle.detach_sidecar()
on a Motorcycle
instance, Python passes the your_motorcycle
object as their first argument (which by convention is named self
—there’s nothing actually magic about that name), before any you pass in yourself. That way, the method knows which particular motorcycle it is acting on. The same thing happens when you call super().__init__()
—through a bit of magic, super()
implicitly knows the current instance and passes it to any methods you call on the object it returns.
This is equivalent to calling the method on the class, and manually passing in the particular instance you want the method to act on, e.g. Motorcycle.detach_sidecar(your_motorcycle)
. (Of course, you’d normally never need to do this, but it hopefully serves to illustrate what’s actually happening here). In your case, since tk.Toplevel
is the superclass of NewWindow
, calling tk.Toplevel.__init__(self)
is equivalent to calling super().__init__()
inside an instance method of NewWindow
, just clunkier, less idiomatic and hard-coded.
As far as understanding self
is concerned, there isn’t anything actually special about the name self; it is merely the (universal) convention for the name of the first parameter to any instance method of a class, to which Python automatically passes the current instance of the class when calling a method on an instance.
Just to note, both these approaches have the effect of calling the method that is the immediate superclass constructor, not modifying it. (We might, however, call your specific example __init__
method as a whole “wrapping” the superclass one).
And for that, super().__init__()
is the canonical, idiomatic and straightforward way to do that specifically, and will work regardless of superclass. There’s no need to hardcode a particular superclass, call its __init__
class method and manually pass the current instance, aside from highly specialized advanced scenarios involving multiple inheritance from multiple non-mixin classes with __init__
methods defined where you want to call a specific constructor that has lower priority in the MRO. Needless to say, this is not something a beginner is going to need to worry about any time soon, and possibly ever. Thus:
should of course be written
super().__init__(hoof, 'White', 'Red')
And also: Tkinter 8.5 reference: a GUI for Python
So, does it mean that if I need to use certain methods from a class, I cannot just write CertainClass.certain_method()
? The class should be “loaded” to memory (?) via self
and then I can use its methods? The same should work for built-in methods. At least I can read it in /tkinter/init().py’s that every method of a widget (class) has the self parametr.
Thanks, I guess I am starting to understand.
Yes, you would need an instance for the method to work because the method might miss some information that is set/configured in an instance. CertainClass.__init__
might configure/set something on which CertainClass.certain_method
is dependant.
Well yes, you need an instance of CertainClass
to run its methods. It’s what I said with the blueprint, you can see what it is supposed to be and how it should work but you cannot interact with the attributes or methods the blueprint is describing, without creating an object from it.
Every method of a class is required to have the self
[1] parameter. But you don’t need to set it when you have an instance and call a method like this: instance.do_something()
because Python realizes the context and puts self
in for you
Just keep learning. OOP might be a bit confusing in the beginning but once you understand the concept, you will love it. I also recommend Real Python as a learning resource, I think they are pretty good.
Fun fact: You don’t have to call it self
you can just use any name you want. self
just is the standard name everyone uses for it, so you should stay with it too even tho you can change it ↩︎
No. If the class is CertainClass
then that class itself will be defined (and initialized as object) before any instances of the class can be initialized. And if certain_method
is a static method (defined with the @staticmethod decorator so the function doesn’t require a self
argument) you can call that method even if there are no instances of the class. (Class methods defined with @classmethod are similar.)
But if certain_method
is not a static method, so if it takes a self
argument, then it can only be called on an instantiated object of that class.