Dynamically Creating Classes in Python

by James Johnson

Dynamically typed programming languages have a major advantage over statically typed languages in their flexibility. Code can be dynamically imported, variables’ types can change, and classes can be created dynamically at runtime.

TL;DR - Dynamically Creating a Class

To dynamically create a class, the type(name, bases, attributes) type method must be used:

def constructor(self, arg1):
    self.constructor_arg = arg1

def some_func(self, arg1):
    print(arg1)

@classmethod
def some_class_method(cls, arg1):
    print("{} - arg1: {}".format(cls.__name__, arg1))

NewClass = type("NewClass", (object,), {
    "string_val": "this is val1",
    "int_val": 10,
    "__init__": constructor,
    "func_val": some_func,
    "class_func": some_class_method
})

instance = NewClass("constructor arg")
print(instance.constructor_arg)
print(instance.string_val)
print(instance.int_val)
instance.func_val("test")
NewClass.class_func("test")

Running the code above yields the output:

constructor arg
this is val1
10
test
NewClass - arg1: test

Notice how classmethods, constructors, methods, and class variables can all be defined through the attributes argument to the type function.

If you want to set the metaclass in the example above, you would instantiate a new instance of the metaclass instead of using the type function. Notice that a metaclass’ __init__ and __new__ signatures are the same as that of the type function (name, bases, attributes): NewClass = MetaClass("name", (base1,base2,), {}). Also see Eli Bendersky’s metaclass blog post for more details.

The type Function

Classes can be dynamically created using the type function. The type function normally is used to return the type of the supplied object. For example:

print(type(5)) # <type 'int'>
print(type("hello")) # <type 'str'>

The type function can also be used to create classes. According to the offical type documentation, the type function has two signatures:

  • type(object) - returns the type of the object
  • type(name, bases, dict) - return a new type object

The second signature of the type function is what can be used to dynamically create classes.

Fun Dynamic Class Creation Example

As a simple example, let’s write some code that creates classes dynamically to simulate gene and attribute inheritance among related individuals. Classnames will essentially be last names, and each Person instance will have a member variable for the individual’s first name:

class Person(object):
    def __init__(self, firstname):
        self.firstname = firstname

Let’s also say that a Person can spawn a new individual that shares all of the same attributes and functions of itself (inheritance). When spawning a new individual (creating a new class dynamically and instantiating the class), we can also add new attributes to the spawned Person:

class Person(object):
    # ...
    @classmethod
    def spawn(cls, firstname, lastname, **attributes):
        new_class = type(lastname, (cls,), attributes)
        globals()[lastname] = new_class
        return new_class(firstname)

Notice that we’re adding the newly created class to the globals() dict. This will allow us to access the class (named after the spawned person’s last name) globally, like this:

susy = Person.spawn("Susy", "Awesome")
susys_sister = Awesome("Sister")

Below is the complete and fleshed-out example:

class Person(object):
    @classmethod
    def spawn(cls, firstname, lastname, **attributes):
        new_class = type(lastname, (cls,), attributes)
        globals()[lastname] = new_class
        return new_class(firstname)

    def __init__(self, firstname):
        self.firstname = firstname

    def say_hi(self, to_person):
        print("Hi {}".format(to_person.wholename()))

    def wholename(self):
        return "{} {}".format(
            self.firstname.capitalize(),
            self.__class__.__name__
        )

def punch(self, person):
    print("{} punched {}! ({} damage)".format(
        self.wholename(),
        person.wholename(),
        self.punch_damage
    ))
frank = Person.spawn("Frank", "Puncherson",
    punch_damage=10,
    punch=punch
)
# a normal person
harold = Person.spawn("Harold", "Hill")
frank.punch(harold)

franks_bro = Puncherson("Ralph")
franks_bro.punch(harold)

def kick(self, person):
    print("{} kicked {}! ({} damage)".format(
        self.wholename(),
        person.wholename(),
        self.kick_damage
    ))
susy = Puncherson.spawn("Susy", "KickPuncherson",
    kick_damage=20,
    kick=kick
)
susy.punch(frank)
susy.kick(frank)
susy.punch(franks_bro)
susy.kick(franks_bro)

The example above yields the output:

Frank Puncherson punched Harold Hill! (10 damage)
Ralph Puncherson punched Harold Hill! (10 damage)
Susy KickPuncherson punched Frank Puncherson! (10 damage)
Susy KickPuncherson kicked Frank Puncherson! (20 damage)
Susy KickPuncherson punched Ralph Puncherson! (10 damage)
Susy KickPuncherson kicked Ralph Puncherson! (20 damage)

I’ve only ever had the legitimate need to dynamically create classes a few times before (see pfp’s interp.py). It’s always good to know how the underpinnings of something work so you can use them when that one odd situation presents itself.