0

Contiuing from this post: Dynamically creating a class from file, type(name, bases, dict) can be used to dynamically create a class with name name, with base classes bases, and attributes dict.

I have classes such as:

class City:
    def __init__(self):
        self.name = 0
class Building:
   def __init__(self):
       self.number = 100

I want to create a new class from a string (for ex. "School"), which inherits from the above classes. I have done:

School = type("School", (City, Building), {"school_name": "abc"}
s = School()

hasattr(s, "school_name") gives True. However, hasattr(s, "name") gives False.

How can I make sure that:

  1. My dynamically generated class School inherits the attributes of its base class(es)?
  2. How can I add additional attributes to the class School after already creating the class School = type("School", (Building, Home), {"school_name": "abc"}? (Suppose I want to add two new attributes (address, pincode) to the class later on in my script - can I do that?)
14
  • You provide an example of City and Building but your new type uses Building and Home... where's that come from? Commented Jul 4, 2022 at 23:01
  • 1
    Both City.__init__ and Building.__init__ should be calling super().__init__. Neither actually defines any attributes, though. Even City().name would raise an exception. Commented Jul 4, 2022 at 23:01
  • 1
    Put another way, this has little to do with calling type explicitly versus using class School(Building, Home): ... instead. (Your call to type defines a class attribute named school_name when School is defined.) Commented Jul 4, 2022 at 23:02
  • 1
    You rarely want to defined classes like this, anyway. Commented Jul 4, 2022 at 23:03
  • You can add attributes by defining them in the dict dictionary argument. i.e. each key being a function name and the cooresponding value a function def. Commented Jul 4, 2022 at 23:04

2 Answers 2

1

City’s init method gets override by Building’s init method try

hasattr(s, ‘number’)

and it should return True.

Define your class as

class City:
    name = 0
 
class Building:
    number = 100

This way attributes can be inherited.

For the second question, not sure about what you are asking for, but try

School.address = ‘foo’
School.pincode = ‘bar’
Sign up to request clarification or add additional context in comments.

2 Comments

class attributes != instance attributes
@cards Thanks for pointing out this. I put all instance attributes to class attributes, which may solve OP’s problem, but be aware it could lead to undesired behavior, i.e. instance initiated before adding/changing class attributes may or may not have that new attributes.
1

When you create a class dynamically you can also pass methods, "constructor" __init__ included.

For a class with single parent:

# methods & attrs
d = {"school_name": 'abc',
     '__init__': lambda self, *args, **kwargs:
        super(type(self), self).__init__(*args, **kwargs)}

# dynamic class
School = type("School", (City,), d)

# instance
s = School()

print(hasattr(s, 'name'))
#True
print(s.school_name)
#abc

For a class with two parents:

d = {"school_name": 'abc',
     '__init__': lambda self, *args, **kwargs:
        (City.__init__(self, *args, **kwargs),
        Building.__init__(self, *args, **kwargs))[0]
    }

School = type("School", (City, Building), d)
s = School()

print(hasattr(s, 'name'))
#True
print(hasattr(s, 'number'))
#True

The __init__ is called explicitly for each parent. For more complex stuffs I recommend to write a classical function instead of lambdas.

For a class with two parents, base class-free version:

d = {"school_name": 'abc',
     '__init__': lambda self, *args, **kwargs:
     [cls.__init__(self, *args, **kwargs) for cls in type(self).__bases__][0]
    }

NOTE if dealing with multiple inheritance where parents classes have different signatures (so the parameters of __init__) it could be a big mess if don't pay attention to *args & **kwargs!

3 Comments

this would really be a lot clearner if you stopped trying to use a lambda expression to define __init__, which is just clumsy. lambda expressions are best for pure functions that are meant to return a value, __init__ is usually exclusively for side-effects.
also, this is just not how you should be doing the cooperative multiple inheritance, super(type(self), self) is a red flag
@juanpa.arrivillaga [1] about the lambda: yes, I also mentioned it in my answer... also because functions can be quite complicated. The ___init__ lambda returns a value, None. [2] could you tell more about the "red flag"? I am curiuous... The doc says that "The two argument form" for calls outside methods as in these cases

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.