使用 super().__init__() 混合 type() 和自定义 __init__()
Mix type(), and custom __init__() using super().__init__()
到目前为止我已经成功完成的事情:
我制作了一个 elem
class 来表示 html 个元素(div
、html
、span
、body
, 等等).
我可以像这样推导这个 class 来为每个元素制作子 classes:
class elem:
def __init__(self, content="", tag="div", attr={}, tag_type="double"):
"""Builds the element."""
self.tag = tag
self.attr = attr
self.content = content
self.tag_type = tag_type
class head(elem):
"""A head html element."""
def __init__(self, content=None, **kwargs):
super().__init__(tag="head", content=content, **kwargs)
而且效果很好。
但是我必须为每个 subclass 声明都写这个,如果我想对每个 HTML 标记类型都做,那是相当重复和多余的。
所以我试图制作一个 make_elem()
函数,通过将相应的标签名称作为字符串参数来使我的 class。
因此,我将简单地使用以下内容代替之前的 class 定义:
head = make_elem_class("head")
我被困在哪里
这个函数应该创建一个 class。 class 中的 __init__()
方法应该从它继承的 class 中调用 __init__()
方法。
我尝试制作这个 make_elem_class()
函数,它看起来像这样:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super().__init__(tag=name, content=None, **kwargs)
return type(name, (elem,), {"__init__" : init})
但是当 运行 html = make_elem_class('html')
时 html("html element")
我得到以下错误:
Traceback (most recent call last):
File "elements.py", line 118, in <module>
html("html element")
File "elements.py", line 20, in init
super().__init__(tag=name, content=None, **kwargs)
TypeError: object.__init__() takes no parameters
我猜想这与空 super()
调用有关,所以我尝试使用 super(elem, self)
代替。但显然效果并不好
我怎样才能做到这一点?
注意: 如果我在 type()
调用中从字典中删除 "__init__":init
,它工作正常,但标签设置不正确我的元素。我也试过直接将 {"tag":name}
传递给 type()
但它也没有用。
您不能在此处使用 super()
的无参数形式,因为此处没有 class
语句来提供该函数通常需要的上下文。
或者更确切地说,除非您自己提供该上下文,否则您不能;您需要在此处将名称 __class__
设置为闭包:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super().__init__(tag=name, content=content, **kwargs)
__class__ = type(name, (elem,), {"__init__" : init})
return __class__
super()
会自动从闭包中获取 __class__
值。请注意,我将 content
的值而不是 None
的值传递给 elem.__init__
方法;你不想失去那个价值。
如果那是 too magical,请在调用 super()
时明确命名 class 和 self
;同样,class 将从闭包中获取:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super(elemcls, self).__init__(tag=name, content=content, **kwargs)
elemcls = type(name, (elem,), {"__init__" : init})
return elemcls
有什么更直接的解决方案,例如推断 class __name__
的标签?
class elem:
def __init__(self, content="", tag=None, attr={}, tag_type="double"):
"""Builds the element."""
self.tag = tag or self.__class__.__name__
...
然后:
class div(elem): pass
class head(elem): "Optional docstring for <head>"
...
少一点魔法(有争议),多一点直白。 :-)
我认为这是一个 XY 问题。因为您已经询问了如何在动态创建的 class 中使用 super
,但您真正想要的是一种不那么冗长的方法来为您的子[设置各种 class 变量和默认值=26=]es。
由于您不希望同一标签 class 的所有实例都共享相同的标签名称,您最好将其设置为 class 变量而不是实例变量。例如
from abc import ABC, abstractmethod
class Elem(ABC):
tag_type = "double" # the default tag type
def __init__(self, content="", attr=None, tag_type=None):
"""Builds the element."""
self.attr = attr if attr is not None else {}
self.content = content
if tag_type is not None:
self.tag_type = tag_type
@property
@abstractmethod
def tag(self):
"""All base classes should identify the tag they represent"""
raise TypeError("undefined tag for {}".format(type(self)))
class Head(Elem):
tag = "head"
tag_type = "text"
class Div(Elem):
tag = "div"
h = Head()
d = Div()
h1 = Head(tag_type="int")
assert h.tag == "head"
assert d.tag == "div"
assert h1.tag == "head"
assert h.tag_type == "text"
assert d.tag_type == "double"
assert h1.tag_type == "int"
您现在可以编写非常短的子 classes,并且仍然显式声明您的 classes。您会注意到我将几个默认值更改为 None
。对于 attr
,这是因为具有可变的默认参数不会像您期望的那样工作——它的行为更像是一个共享的 class 变量。相反,将默认设置为 None
,如果未指定 attr
,则为每个实例创建一个新的 attr
。第二个 (tag_type
) 是这样的,如果指定 tag_type
则实例将设置 tag_type
,但所有其他实例将依赖 class 作为默认值.
到目前为止我已经成功完成的事情:
我制作了一个 elem
class 来表示 html 个元素(div
、html
、span
、body
, 等等).
我可以像这样推导这个 class 来为每个元素制作子 classes:
class elem:
def __init__(self, content="", tag="div", attr={}, tag_type="double"):
"""Builds the element."""
self.tag = tag
self.attr = attr
self.content = content
self.tag_type = tag_type
class head(elem):
"""A head html element."""
def __init__(self, content=None, **kwargs):
super().__init__(tag="head", content=content, **kwargs)
而且效果很好。
但是我必须为每个 subclass 声明都写这个,如果我想对每个 HTML 标记类型都做,那是相当重复和多余的。
所以我试图制作一个 make_elem()
函数,通过将相应的标签名称作为字符串参数来使我的 class。
因此,我将简单地使用以下内容代替之前的 class 定义:
head = make_elem_class("head")
我被困在哪里
这个函数应该创建一个 class。 class 中的 __init__()
方法应该从它继承的 class 中调用 __init__()
方法。
我尝试制作这个 make_elem_class()
函数,它看起来像这样:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super().__init__(tag=name, content=None, **kwargs)
return type(name, (elem,), {"__init__" : init})
但是当 运行 html = make_elem_class('html')
时 html("html element")
我得到以下错误:
Traceback (most recent call last):
File "elements.py", line 118, in <module>
html("html element")
File "elements.py", line 20, in init
super().__init__(tag=name, content=None, **kwargs)
TypeError: object.__init__() takes no parameters
我猜想这与空 super()
调用有关,所以我尝试使用 super(elem, self)
代替。但显然效果并不好
我怎样才能做到这一点?
注意: 如果我在 type()
调用中从字典中删除 "__init__":init
,它工作正常,但标签设置不正确我的元素。我也试过直接将 {"tag":name}
传递给 type()
但它也没有用。
您不能在此处使用 super()
的无参数形式,因为此处没有 class
语句来提供该函数通常需要的上下文。
或者更确切地说,除非您自己提供该上下文,否则您不能;您需要在此处将名称 __class__
设置为闭包:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super().__init__(tag=name, content=content, **kwargs)
__class__ = type(name, (elem,), {"__init__" : init})
return __class__
super()
会自动从闭包中获取 __class__
值。请注意,我将 content
的值而不是 None
的值传递给 elem.__init__
方法;你不想失去那个价值。
如果那是 too magical,请在调用 super()
时明确命名 class 和 self
;同样,class 将从闭包中获取:
def make_elem_class(name):
"""Dynamically creates the class with a type() call."""
def init(self, content=None, **kwargs):
super(elemcls, self).__init__(tag=name, content=content, **kwargs)
elemcls = type(name, (elem,), {"__init__" : init})
return elemcls
有什么更直接的解决方案,例如推断 class __name__
的标签?
class elem:
def __init__(self, content="", tag=None, attr={}, tag_type="double"):
"""Builds the element."""
self.tag = tag or self.__class__.__name__
...
然后:
class div(elem): pass
class head(elem): "Optional docstring for <head>"
...
少一点魔法(有争议),多一点直白。 :-)
我认为这是一个 XY 问题。因为您已经询问了如何在动态创建的 class 中使用 super
,但您真正想要的是一种不那么冗长的方法来为您的子[设置各种 class 变量和默认值=26=]es。
由于您不希望同一标签 class 的所有实例都共享相同的标签名称,您最好将其设置为 class 变量而不是实例变量。例如
from abc import ABC, abstractmethod
class Elem(ABC):
tag_type = "double" # the default tag type
def __init__(self, content="", attr=None, tag_type=None):
"""Builds the element."""
self.attr = attr if attr is not None else {}
self.content = content
if tag_type is not None:
self.tag_type = tag_type
@property
@abstractmethod
def tag(self):
"""All base classes should identify the tag they represent"""
raise TypeError("undefined tag for {}".format(type(self)))
class Head(Elem):
tag = "head"
tag_type = "text"
class Div(Elem):
tag = "div"
h = Head()
d = Div()
h1 = Head(tag_type="int")
assert h.tag == "head"
assert d.tag == "div"
assert h1.tag == "head"
assert h.tag_type == "text"
assert d.tag_type == "double"
assert h1.tag_type == "int"
您现在可以编写非常短的子 classes,并且仍然显式声明您的 classes。您会注意到我将几个默认值更改为 None
。对于 attr
,这是因为具有可变的默认参数不会像您期望的那样工作——它的行为更像是一个共享的 class 变量。相反,将默认设置为 None
,如果未指定 attr
,则为每个实例创建一个新的 attr
。第二个 (tag_type
) 是这样的,如果指定 tag_type
则实例将设置 tag_type
,但所有其他实例将依赖 class 作为默认值.