2016-04-27 34 views
2

我已經成功至今做的事:

我已經做了一個elem類來表示HTML元素(divhtmlspan,body等)。混合型(),並使用超()自定義__init __().__的init()__

我能夠衍生物該類這樣使子類的每個元素:

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) 

而且它工作得很好。

但是我必須爲每個子類聲明寫這個,如果我想要做每一個HTML標記類型,那麼這是相當重複和冗餘的。

所以我試圖做一個make_elem()函數,通過將相應的標籤名稱作爲字符串參數來創建我的類。

因此,而不是以前的類定義,我只想有這樣的事情:

head = make_elem_class("head") 

如果我堅持

這個函數應該創建一個類。而這個類的__init__()方法應該從它繼承的類中調用__init__()方法。

我試圖讓這個make_elem_class()功能,它看起來像這樣:運行html = make_elem_class('html')

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("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()呼叫dictionnary刪除"__init__":init,它工作正常,但標籤不正確我ELEM設置。我也試圖直接通過{"tag":name}type(),但它也沒有工作。

+0

你能製作一個簡化的elem類嗎?我也不能重現你的確切異常,我得到'RuntimeError:super():__class__ cell not found'。你可能在某處使用'make_elem_class'作爲靜態或類方法嗎? –

+0

或者,您是否可能在某處設置了__class__ = elem? –

+0

@MartijnPieters當然!我編輯了我的帖子,'__init __()'方法完成。在我的代碼中沒有'__class__ = elem'。 – vmonteco

回答

3

這裏不能使用無參數形式的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()時明確指定類和self;再次,類將被從封閉拍攝:

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 
+0

您的解決方案完美無瑕!它可能被簡化了(我不是一個Python專家,可能沒有正確的詞彙表),但'super()'搜索我們在本地範圍傳遞給它的類名(默認情況下是'__class__',如果沒有參數並且在使用'class myclass():'語法?)時創建'__class__'? – vmonteco

+0

@vmonteco:看到已經鏈接[爲什麼是Python 3.x的超級()魔術?](https://stackoverflow.com/q/19608134); 'super()'查找一個*閉包單元*,當你a)在'class'塊中創建一個方法,並且'b'名稱爲'super'時,這個閉包單元由Python編譯器自動提供該方法。 –

+0

@vmonteco:通常只有當你使用父函數作用域中的名字(比如'make_elem_class'中的'name',爲了能夠在'init()'函數中使用這個名字時,創建)。 –

1

關於什麼的樣推斷標籤的類__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>" 
... 

少了幾分神奇的(有爭議的),有點更加明確。 :-)

+0

老實說,不確定那是不是那麼神奇。我們幸運的是,沒有任何HTML標籤碰巧是[保留的Python關鍵字](https://docs.python.org/3/reference/lexical_analysis.html#keywords)。使用類屬性可能是更好的想法'self.tag'然後將從'elem.tag'中讀取,您將在子類中重寫。在傳遞給'type()'的字典中用''tag':name'輕鬆設置。 –

+0

夠公平!對於這類事情來說,可能並非真的不那麼神奇,而是一種嵌入式DSL。 –

0

我認爲這是一個XY問題的一點點。在這方面,你已經問過如何在一個動態創建的類中使用super,但你真正想要的是爲你的子類設置各種類變量和默認值的一種不太冗長的方式。

由於您不希望相同標記類的所有實例共享相同的標記名稱,因此您不妨將其設置爲類變量而不是實例變量。例如。

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" 

您現在可以編寫很短的子類,並且仍然可以顯式聲明您的類。您會注意到我將一些默認值更改爲None。對於attr,這是因爲具有可變默認參數將無法滿足您的期望 - 它會表現得更像是共享類變量。相反,如果attr尚未指定,則將默認值設置爲None,然後爲每個實例創建一個新的attr。第二個(tag_type)是這樣的,如果tag_type被指定,那麼該實例將設置tag_type,但所有其他實例將依賴該類作爲默認值。

相關問題