Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

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.