Динамическое создание подклассов с именами из списка строк в Python - PullRequest
0 голосов
/ 01 июля 2019

Я хочу динамически создавать подклассы из суперкласса, используя строки в качестве имен подклассов.Например, скажем, у меня есть этот класс, представляющий тег HTML.

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

И у меня также есть этот список:

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

Я хочу как-то создать подклассы (возможно, используя цикл for) класса Tag, т.е. я хочу иметь новые классы, а именно Div, A, Body, Html, Nav, etc., которые являются подклассами класса Tag.Есть ли быстрый способ сделать это?Также плохая практика - не объявлять класс явно?

Редактировать: я хочу создавать подклассы, не объекты.

Редактировать 2: Как Гойо заявил, что моя цельне ясно, я хочу в основном реализовать все классы тегов HTML за один раз.Многие люди уже внедрили генераторы HTML, но что касается этого примера , им пришлось определить все подклассы класса Tag и просто написать pass в подклассе, например:

...

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
...

Вот чего я хочу избежать, так как это выглядит как избыточный дублированный код и много копий для меня.

Редактировать 3: Мои извинения за упоминание кода других людей как "плохо" пример.Это не мое намерение в любом случае.Я просто хочу научиться писать более короткий и краткий код.

1 Ответ

1 голос
/ 01 июля 2019

Посмотрите документы для type:

класс тип ( имя , основания , dict )
[...]
С тремя аргументами вернуть объект нового типа. По сути, это динамическая форма выражения класса. Строка имени является именем класса и становится атрибутом __name__; кортеж базовых элементов классифицирует базовые классы и становится атрибутом __bases__; а словарь dict является пространством имен, содержащим определения для тела класса, и копируется в стандартный словарь, чтобы стать атрибутом __dict__.

Так что-то вроде этого должно создать классы, которые вы хотите:

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

Если вы хотите динамически создавать псевдонимы для этих предложений, об этом много раз спрашивали и отвечали здесь. Это почти всегда плохая идея но так как вы настаиваете, вот вам:

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)

Существует несколько проблем с этим кодом:

Труднее понять, понять

Для обычного читателя не было бы очевидно, что классы Div, A и т. Д. Существуют или что они делают. По крайней мере, менее очевидно, чем если бы они были определены статически.

Стремление к выполнению нескольких действий «за один раз» и написание «более короткого и краткого кода» часто ухудшает удобочитаемость и ремонтопригодность. Запомните , что явное лучше, чем неявное, разреженное лучше, чем плотное и считываемое.

Использование специального словаря, вероятно, было бы улучшением, но не решило бы проблему полностью.

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

Вы делаете различие без разницы

Вы создаете группу классов, которые ведут себя одинаково. Какой смысл? Создавая их динамически, вы даже теряете потенциальные преимущества статической проверки типов. Единственное различие между экземпляром Div и экземпляром A заключается в некоторых данных, которые вы кодифицируете в имени класса. Храня эти данные в атрибуте экземпляра, вы избавите себя от необходимости использовать все эти классы, и код станет намного проще.

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)
...