00:00 In the previous lesson, I said the word type rather a lot, hopefully resulting in you learning the class hierarchy in Python. In this lesson, I’ll show you how to put that to use creating classes on the fly.
Say it with me: everything in Python is an object, including the classes. The mechanism Python uses to instantiate class objects is available to you as a programmer. And as you might have guessed, this mechanism is the
Let’s review quickly. There’s my
Dessert … and a
sundae instance. The type of
sundae is the
Dessert class, and the type of
Now let’s do the same thing without the
class keyword. Here, I’ve created an
Appetizer class using the
This variant on the
type() function takes three arguments. The first is a string for the internal name of the class, the second an inheritance tuple, which is empty in this case, and the third an attributes dictionary, also empty in this case.
Appetizer returned by the
type() function is a class. When I wrote the
Dessert snippet up top, Python was essentially doing this call to
type() for me.
What do you do with a class? Well, instantiate it, of course. Here’s my
shrimp instance, whose type is
Appetizer. And for completeness’ sake, the type of
type, which I got by using the
type()—yeah, I know, I promise to stop, sorry about that. Let’s do that again, but with something a little more complicated.
This creates the
Dip class that inherits from
Appetizer, hence why
Appetizer is in the second argument’s tuple.
Dip classes have an attribute called
02:16 Note that this is a class, not an instance, so the attribute here is a class attribute. It’ll be shared by all object instances. Who doesn’t love a good salsa?
Dip, and type of
type. Because of inheritance,
salsa is an
Appetizer, and there’s the class
.description attribute. Let’s do another one.
Stuffed class that inherits from
Dip, it has a description, and I’ve added a
total_things is a lambda. So let’s stuff some mushrooms.
mushrooms are appetizers. There’s
1 thing stuffed in the mushroom … and one thing inside, plus the mushroom itself, gives me
2 things in total.
total_things is a lambda, so I need to use parenthesis to execute it. I’m going to convert
mushrooms to a string, and that’s the usual default stringification of an object. To get something else, the object needs a
.__str__() method. Want to guess what I’m going to show you next?
Stay with me for a second. This function might seem a little strange on its own, but imagine it as a
.__str__() method of the
That makes sense, right? So let’s create
OnToast, inheriting from
OnToast has a
And similar to the lambda, I can add a function as an attribute. Naming the attribute
.__str__ and pointing it to a reference of the
to_string() function turns
to_string() into a method on the
If I were writing an actual method inside a
class block, I’d have used
self instead of
obj as the argument to
I didn’t here, as I didn’t want to confuse you by using that convention outside of a
class block. Remember the name
self is just a convention.
You can call the first argument to a method anything you want. Other programmers may hunt you down and burn your house to the ground, but you can do it. And there’s
OnToast, burnt or otherwise.
bruschetta goes nicely on toast. And when I convert
bruschetta to a string using the
.__str__() gets called, and the
to_string() function, which is now a method, gets invoked. Funky, huh?
05:58 The mechanics behind classes really are just syntactical sugar making classes clearer to write, but underneath, you’ve got some very function-calling, object-attribute-applying, everything’s-an-object kind of stuff going on.
Why is invoke spelled with a k, but invocation is spell with a c? English is a weird language. Anyhow, invoking something in Python is done with parenthesis, and underneath this results in a
.__call__() method on an object executing.
Cause, like, you’ve heard everything is an object, right? In practice, writing a
.__call__() method allows you to treat object instances as callable. This can be handy in its own right, but understanding it is needed before taking the next steps.
So let’s dive into what seems like a tangent. Back in my handy REPL, going to import the
subprocess module, and now I’m going to define a class that executes multiple calls to
.__init__() takes the
actions argument that is a list of actions, where actions themselves are lists. That’s because that’s what
do_it() function is where
subprocess gets invoked. It loops through the actions and calls
subprocess.run() on each.
If you haven’t used
subprocess.run() before, it takes a list. The first item in the list is the command to
run(), while the rest of the items in the list are the arguments to the command.
I’ve instantiated an
Action object, calling the
echo Unix command twice, passing in some strings to
echo each time.
.do_it() does it. If you’re not a Unix person,
echo is a command-line tool kind of like
The response here isn’t actually the REPL but the
subprocess execution spitting something back to the REPL. But you said something about
.__call__(), I hear you say.
08:41 Let’s try it again in a different way.
The only difference this time is I’ve named
.__call__() instead. Having done that, how I execute the action is different.
hellos again, and instead of invoking
.do_it(), I call
hello() like it is a function. You can do this with your own classes and make objects that look like their functions. And since functions are also objects in Python—yeah, I’ll save that one for another course.
09:38 Next up, what you came here for: it’s been a bit of a twisty road, but you’ve got enough background now that you can build a metaclass.
Become a Member to join the conversation.