Dynamic Classes
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.
00:14
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 type()
function.
00:31
Let’s review quickly. There’s my Dessert
… and a sundae
instance. The type of sundae
is the Dessert
class, and the type of Dessert
is type
.
00:50
Now let’s do the same thing without the class
keyword. Here, I’ve created an Appetizer
class using the type()
function.
01:04
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.
01:20
The 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.
01:30
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 Appetizer
is 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.
02:05
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 .description
.
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?
02:30
Type of salsa
is Dip
, and type of Dip
is type
. Because of inheritance, salsa
is an Appetizer
, and there’s the class .description
attribute. Let’s do another one.
02:59
Creating the Stuffed
class that inherits from Appetizer
. Like Dip
, it has a description, and I’ve added a .content_count
attribute.
03:24
total_things
is a lambda. So let’s stuff some mushrooms.
03:37
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.
03:55
Note that 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?
04:27
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 OnToast
object.
04:36
That makes sense, right? So let’s create OnToast
, inheriting from Appetizer
.
04:48
OnToast
has a .toppings
attribute.
04:57
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 OnToast
class.
05:11
If I were writing an actual method inside a class
block, I’d have used self
instead of obj
as the argument to to_string()
.
05:19
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.
05:27
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.
05:42
bruschetta
goes nicely on toast. And when I convert bruschetta
to a string using the str()
function, .__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.
06:14
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.
06:31
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.
06:46
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 subprocess.run()
.
07:14
The .__init__()
takes the actions
argument that is a list of actions, where actions themselves are lists. That’s because that’s what subprocess.run()
expects.
07:34
The do_it()
function is where subprocess
gets invoked. It loops through the actions and calls subprocess.run()
on each.
07:48
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.
08:10
I’ve instantiated an Action
object, calling the echo
Unix command twice, passing in some strings to echo
each time.
08:20
And calling .do_it()
does it. If you’re not a Unix person, echo
is a command-line tool kind of like print
. Whatever you hand it, it prints out to the terminal.
08:30
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.
09:02
The only difference this time is I’ve named .do_it()
under .__call__()
instead. Having done that, how I execute the action is different.
09:16
There’s my 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.