Kabira  K
Kabira K

Reputation: 2006

Why is my variable name not visible inside a dynamically created class in Python?

I'm trying to create a function that dynamically generates a class with a specified set of mixins and attributes. Here's a simplified version of my code:

def post_type_factory(name, singular_name, mixins, **kwargs):

    # Dynamically create a class with the specified mixins
    class PostType(*mixins):
        name = name
        def __init__(self, *args, **kwargs):
            for amixin in mixins:
                amixin.__init__(*args, **kwargs)
    
    return PostType

However, when I run this code, I get an error: NameError: name 'name' is not defined. I want PostType to inherit from the classes specified in mixins and have a name attribute set to the name argument passed to post_type_factory.

Can anyone explain why the name variable isn't visible in the class definition and suggest how to fix this?

Upvotes: 0

Views: 42

Answers (1)

Hielke Walinga
Hielke Walinga

Reputation: 2845

The class body executes before the function is run. Thus name is not in the scope of the class body. See here for more details: https://docs.python.org/3/tutorial/classes.html#class-definition-syntax

A fix is thus:

def post_type_factory(name, singular_name, mixins, **kwargs):
    # Dynamically create a class with the specified mixins
    class PostType(*mixins):
    
        def __init__(self, *args, **kwargs):
            for amixin in mixins:
                amixin.__init__(*args, **kwargs)
            self.name = name

    return PostType(**kwargs)

A note on inheritance:

When calling the __init__ function you should not forget to provide self.

Thus:

def post_type_factory(name, singular_name, mixins, **kwargs):
    # Dynamically create a class with the specified mixins
    class PostType(*mixins):
    
        def __init__(self, *args, **kwargs):
            for amixin in mixins:
                amixin.__init__(self, *args, **kwargs)
            self.name = name

    return PostType(**kwargs)

What is more, using super(), you can automatically call all mixin constructors.

Thus:

def post_type_factory(name, singular_name, mixins, **kwargs):
    # Dynamically create a class with the specified mixins
    class PostType(*mixins):
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.name = name

    return PostType(**kwargs)

Upvotes: 1

Related Questions