Confused about calling a method on a class without instantiating the object

Hello all,

sorry for the funky title, I didn’t know how to sum up the question. So I’m learning how to use tkinter and in a few of the code examples I’m seeing the following pattern:

import tkinter
from tkinter import ttk

root = tkinter.Tk()
ttk.Button(root, text='Hello World').grid()
root.mainloop()

ttk.Button is a class and grid() is one if it’s methods; grid() renders a button symbolized by the ttk.Button class to the window symbolized by the tkinter.Tk object root

What I don’t understand is how the button symbolized by the class ttk.Button can be rendered to the window without actually creating an instance of the class and storing it in a variable. I’d imagined that in an example like the one above, an object representing the button widget has not actually been created.

Furthermore is it not likely that certain, necessary properties of such an object are established by it’s __init__ method, which won’t be run when calling the grid() method on the class directly?

Based on my current understanding, I would have thought the above example should look more like the following one, which is also valid, but the difference is that the following example is more intuitive to me:

import tkinter
from tkinter import ttk

root = tkinter.Tk()
button = ttk.Button(root, text='Hello World')
button.grid()

root.mainloop()

Any help is much appreciated!

1 Like

ttk.Button is a class and calling a class creates an instance of that class.

ttk.Button(root, text='Hello World') creates the button, which adds itself to the parent widget root when it’s created.

The grid() method isn’t being called on the class, it’s being called in the instance.

The only difference between the first and second examples is that you’re splitting the creation from the placement so that you can save the reference to the button.

1 Like

An instance is created. It just isn’t stored in a variable, but then it doesn’t need to be.

Calling ttk.Button(root, ...) creates a new instance, attaches it to the window, and returns a reference to the instance. We then call the grid method on that instance, not on the Button class.

We don’t need tkinter to demonstrate this pattern:

# Uses a temporary variable alist.
alist = list("Hello world")
print(alist.index("w"))

# Doesn't use a temp variable.
print(list("Greetings friend").index("f"))

In this case, the second list is created, the index method called, and then the list immediately destroyed. In the tkinter example, the button isn’t destroyed because the root window holds a reference to it.

1 Like

Thanks, this example gets at what I don’t understand quite well. I do understand how the second code snippet in your example could work as the list object itself isn’t required to exist beyond the point that the .index() method has returned a value. However, that’s not the case in the tkinter example I gave in the OP.

So you say that in the tkinter example the ttk.button object is created and a reference to it is stored in the tkinter.Tk() object (the root window); what exactly does this mean? Is the “reference” a pointer to the starting memory address at which the ttk.button object is stored?

Is the reference a pointer? It doesn’t matter. It’s an implementation detail.

At the Python level, a “reference” is just some way to refer to an object: a named variable, an entry in a list, a dict key or value, an attribute of some object, etc.; each of these is a “reference” to an object.

In this case, you can find the reference in root.children:

>>> import tkinter
>>> from tkinter import ttk
>>> root = tkinter.Tk()
>>> button = ttk.Button(root, text='Hello world')
>>> button
<tkinter.ttk.Button object .!button>
>>> root.children
{'!button': <tkinter.ttk.Button object .!button>}
>>> button is root.children['!button']
True
>>> del button
>>> root.children  # still has its own reference, so the button still exists
{'!button': <tkinter.ttk.Button object .!button>}
>>> root.children['!button'].grid()
>>> root.mainloop()  # shows the button

There’s more to references and how they work at the C level if you’re interested in a deeper dive, but it’s not really relevant or necessary for the question at hand and might just muddy things instead of clarifying.

2 Likes

The Python language does not specify how references work under the hood, but the simplest mechanism is to use a pointer to the object. Python interpreters written in C probably use pointers.

Python interpreters written for Dot Net (IronPython) or Java (Jython, GraalVM Python) will use some other, more complex, mechanism.

1 Like