0

I'm trying to understand some of the syntaxes of the tutorial of matplotlib from Sentdex's video. For example, on Line 10, what is "F(container, self)" and what is F in this context? Same thing for Line 11 with "self.Frames[F]", what is F here and why are there square brackets on the left-hand side? Finally, same thing for Line 15, why is there square brackets for [cont]?

class SeaofBTCapp(tk.Tk):                                                                   
    def __init__(self, *args, **kwargs):
    tk.Tk.__init__(self, *args, **kwargs)
    container = tk.Frame(self)

    container.pack(side="top", fill="both", expand = True)

    container.grid_rowconfigure(0, weight=1)
    container.grid_columnconfigure(0, weight=1)

    self.frames = {}

    for F in (StartPage, PageOne, PageTwo):

        frame = F(container, self)

        self.frames[F] = frame

        frame.grid(row=0, column=0, sticky="nsew")

    self.show_frame(StartPage)

def show_frame(self, cont):

    frame = self.frames[cont]
    frame.tkraise()
Ken White
  • 123,280
  • 14
  • 225
  • 444

1 Answers1

2

Functions, classes, and instances of a class (objects) are all first class in Python. F() just calls F, which will throw an exception if F is not callable (What is a "callable"?). The [F] is using F as a key in the dictionary self.frames ([] can be used in other ways as well: https://docs.python.org/3/reference/expressions.html#index-41) which will throw an exception unless F is hashable and supports an __eq__ method. Presumably all of StartPage, PageOne, and PageTwo are callable, hashable, and have an __eq__ method.

Perhaps making the code more generic will help to clarify things:

#!/usr/bin/env python3

class frame:
    def __init__(self, *args):
        print(f"frame initialized with {args}")
    def grid(self, **kwargs):
        print(f"frame.grid called with args: {kwargs}")


def StartPage(a=None, b=None):
    return frame(a, b)

class PageOne:
    def __init__(self, *args):
        print(f"PageOne initialized with {args}")
    def grid(self, **kwargs):
        print(f"PageOne.grid called with args: {kwargs}")

class C:
    def __call__(*args):
        return PageOne(args)

PageTwo = C()
container = None

frames = {}
for F in (StartPage, PageOne, PageTwo):
    frame = F(container, None)
    frames[F] = frame
    print( f"F is {F}")
    frame.grid(row=0, column=0, sticky="nsew")

The above code produces the output:

frame initialized with (None, None)
F is <function StartPage at 0x105c63f70>
frame.grid called with args: {'row': 0, 'column': 0, 'sticky': 'nsew'}
PageOne initialized with (None, None)
F is <class '__main__.PageOne'>
PageOne.grid called with args: {'row': 0, 'column': 0, 'sticky': 'nsew'}
PageOne initialized with ((<__main__.C object at 0x105e2a610>, None, None),)
F is <__main__.C object at 0x105e2a610>
PageOne.grid called with args: {'row': 0, 'column': 0, 'sticky': 'nsew'}

In this case, we iterate through the loop three times. The first time in the loop, the name F is bound to the function StartPage. Functions are callable; F(container, None) calls the function StartPage with those 2 arguments and binds frame to the object returned by that function call. That object has a grid method, and frame.grid() calls it. StartPage is hashable and supports an __eq__ method, so it can be used as a key in the frames dictionary. The 2nd time through the loop, the name F is cound to the class PageOne, and F(container, None) invokes the class's __init__ method to create an object which is an instance of that class. Again, that object is hashable and can be used as a key in the frames dictionary, and it contains a grid method. 3rd time through the loop, the name F is bound to the object PageTwo, which was previously initialized as an instance of class C.

In your case, all three objects in the iteration are probably instances of the same class, but I'm hoping the concrete example will help to clarify.

William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • Hi, thank you for your kind and considerate help in "holding my hand" on this. However, I still have some questions from your explanation: 1. How is F a function when I didn't declare it a function beforehand like "def F():", I still think that F here acts as a loop counter though? 2. Why did PageTwo suddenly have three parameters initialized, one being its address when looping through the third time? 3. What does an __eq__ method have to do with getting called as a function? Sorry for my questions as I'm still a newbie here in Python. – FragThemBoshKidz Aug 27 '21 at 06:18
  • `F` is simply a variable used as the loop index. Each time through the loop, it takes a different value. The first time through, the name `F` is bound to `StartPage`. I don't know what the type of `StartPage` is in your code. In my example, it is a function. Object methods receive the object as a first parameter, so `AnObject.method(b,c)` calls the functions with `AnObject` as its first argument, and `b` and `c` as its 2nd and third arguments. By convention, the first argument is usually named `self`. The `__eq__` method is not related to functions. – William Pursell Aug 27 '21 at 12:31
  • `__eq__` is a technical detail: any object that is used as a key in a dictionary must support an `__eq__` method. You can read more about that at https://docs.python.org/3/glossary.html#term-hashable and https://stackoverflow.com/questions/1608842/types-that-define-eq-are-unhashable and https://stackoverflow.com/questions/14535730/what-does-hashable-mean-in-python – William Pursell Aug 27 '21 at 12:36