-1

I want to dynamically create subclasses from a superclass using strings as the subclasses names. For example, say I have this class representing an HTML tag.

class Tag:
   def __init__(self, content, **attr):
       pass
   def __repr__(self):
       return f"<{self.__class__.__name__.lower()}>{content}</{<self.__class__.__name__.lower()>}>"

And I also have this list:

class_names = ["Div", "A", "Body", "Html", "Nav"] # etc. Basically contains all html tags.

I want to somehow create subclasses (maybe using a for loop) of the Tag class, i.e. I want to have new classes namely Div, A, Body, Html, Nav, etc. that are all subclasses of the Tag class. Is there a quick way to do this? Also is it bad practice to not explicitly declare a class?

Edit: I want to create subclasses, not objects.

Edit 2: As Goyo stated that my purpose is not clear, I want to basically implement all html tag classes in one go. Many people have already implemented HTML generators, but as for this example, they had to define all subclasses of the Tag class and just write pass in the subclass, like this:

...

class Address(HtmlTag):
    """Defines contact information for the author/owner of a document"""
    pass


class Applet(HtmlTag):
    """Not supported in HTML5. Use <embed> or <object> instead."""
    pass


class Area(SelfClosingHtmlTag):
    """Defines an area inside an image-map"""
    pass
...

Which is what I want to avoid, since this looks like redundant duplicated code and a lot of copy-pasting to me.

Edit 3: My apologies for mentioning other people's code as a "bad" example. That's not my intention in any way. I just want to learn how to write shorter and more concise code.

Mike Pham
  • 437
  • 6
  • 17
  • 1
    Possible duplicate of [How to create multiple class objects with a loop in python?](https://stackoverflow.com/questions/21598872/how-to-create-multiple-class-objects-with-a-loop-in-python) – Matteo Peluso Jul 01 '19 at 15:29
  • 3
    If the desire is to just save lines of code/typing time by dynamically creating the classes, I'd recommend against it, especially if your subclasses are all essentially the same thing aside from names. What's your need for doing this on the fly? –  Jul 01 '19 at 15:37
  • Possible duplicate of [Class factory in Python](https://stackoverflow.com/questions/456672/class-factory-in-python) – AGN Gazer Jul 02 '19 at 02:47
  • @AGNGazer I don't see how that answers my question. – Mike Pham Jul 02 '19 at 03:11

1 Answers1

2

Look at the docs for type:

class type(name, bases, dict)
[...]
With three arguments, return a new type object. This is essentially a dynamic form of the class statement. The name string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and is copied to a standard dictionary to become the __dict__ attribute.

So something like this should create the classes you want:

type(class_name, (Tag,), {})

If you want to dinamicaly create aliases for those clases, that has been asked and answered many times here. It is almost always a bad idea but since you insist, here you are:

class Tag:
   def __init__(self, content, **attr):
       self.content = content

   def __repr__(self):
       return f"<{self.__class__.__name__.lower()}>{self.content}</{self.__class__.__name__.lower()}>"

class_names = ["Div", "A", "Body", "Html", "Nav"]

for class_name in class_names:
    globals()[class_name] = type(class_name, (Tag,), {})

div = Div('some content')
print(div)

There are a couple of issues with this code though:

It is harder to read an understand

For a casual reader it would not be obvious that the classes Div, A, etc. exist or what they do. At least less obvious than if they were statically defined.

Aiming for doing several things "in one go" and writing "shorter and more concise code" will often hurt readability and maintainability. Remember that explicit is better than implicit, sparse is better than dense and readability counts.

Using an ad-hoc dictionary would probably be an improvement but it wouldn't fix the problem completely.

tag_classes = {class_name: type(class_name, (Tag,), {})
               for class_name in class_names}
div = tag_clases['Div']('some content')
print(div)

You are making a distinction without a difference

You are creating a bunch of classes that behave the same. What is the point? By creating them dinamically you even lose the potential benefits of static type checking. The only difference between an instance of Div and a instance of A is some data that you are codifying in the class name. By storing that data in a instance attribute instead you can spare yourself the need of all those classes and the code becomes much simpler.

class Tag:
    def __init__(self, tag_name, content, **attr):
        self.tag_name = tag_name
        self.content = content

    def __repr__(self):
       return f"<{self.tag_name.lower()}>{self.content}</{self.tag_name.lower()}>" 

div = Tag('Div', 'some_content')
print(div)
Stop harming Monica
  • 12,141
  • 1
  • 36
  • 56
  • This does not really answer my question because although the `__name__` attribute is set, I cannot instantiate the class with that name, e.g. I cannot just do this `Div("Some text", class_="my_class)`. I've added an edit to my question explaining why I want this. – Mike Pham Jul 02 '19 at 02:27
  • @MikePham I edited the answer to add that, but I think you are trying to make it too complex. – Stop harming Monica Jul 02 '19 at 20:49