web-dev-qa-db-ja.com

super()は、新しいスタイルのクラスに対して "TypeError:typeでならばならない、classobjではない"を発生させる

以下のsuper()の使用はTypeErrorを発生させます。

>>> from  HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...         
>>> TextParser()
(...)
TypeError: must be type, not classobj

StackOverflowについても同様の問題があります。 Python super()はTypeError を発生させます。ここでエラーはユーザークラスが新しいものではないという事実によって説明されます。スタイルクラスただし、上記のクラスはobjectから継承するため、新しいスタイルのクラスです。

>>> isinstance(HTMLParser(), object)
True

何が足りないの?どうやってsuper()を使えますか?

HTMLParser.__init__(self)の代わりにsuper(TextParser, self).__init__()を使うことはできますが、TypeErrorを理解したいのです。

PS:Joachimは、新しいスタイルのクラスのインスタンスであることがobjectであることと同等ではないと指摘しました。私は反対のことを何度も読んでいたので、私の混乱(objectインスタンステストに基づく新しいスタイルのクラスインスタンステストの例: https://stackoverflow.com/revisions/2655651/3 )。

330
Eric O Lebigot

さて、これは通常の「super()は古いスタイルのクラスでは使用できません」です。

ただし、重要な点は、正しいテストは「これは新しいスタイルのインスタンス(つまりオブジェクト)? "は

>>> class OldStyle: pass
>>> instance = OldStyle()
>>> issubclass(instance.__class__, object)
False

ではなく(質問のように):

>>> isinstance(instance, object)
True

classesの場合、正しい「is this a new-style class」テストは次のとおりです。

>>> issubclass(OldStyle, object)  # OldStyle is not a new-style class
False
>>> issubclass(int, object)  # int is a new-style class
True

重要なポイントは、古いスタイルのクラスでは、インスタンスのclassとそのtypeは区別されます。ここで、OldStyle().__class__OldStyleから継承されないobjectであり、type(OldStyle())instanceタイプであり、doesobjectから継承します。基本的に、古いスタイルのクラスはタイプinstanceのオブジェクトを作成します(一方、新しいスタイルのクラスはタイプがクラス自体であるオブジェクトを作成します)。これがおそらくインスタンスOldStyle()objectである理由です。そのtype()objectを継承します(そのクラスはnotobjectを継承しません。古いスタイルのクラスは、単にinstance)型の新しいオブジェクトを構築します。部分参照: https://stackoverflow.com/a/9699961/4297

PS:新しいスタイルのクラスと古いスタイルのクラスの違いは、次のことでも確認できます。

>>> type(OldStyle)  # OldStyle creates objects but is not itself a type
classobj
>>> isinstance(OldStyle, type)
False
>>> type(int)  # A new-style class is a type
type

(古いスタイルのクラスはnot型なので、インスタンスの型にすることはできません)。

244
Eric O Lebigot

super()は、新しいスタイルのクラスでのみ使用できます。つまり、ルートクラスは 'object'クラスから継承する必要があります。

たとえば、トップクラスは次のようにする必要があります。

class SomeClass(object):
    def __init__(self):
        ....

ではない

class SomeClass():
    def __init__(self):
        ....

したがって、解決策は、次のように、親のinitメソッドを直接呼び出すことです。

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []
195
Colin Su

class TextParser(HTMLParser, object):を使うこともできます。これはTextParser新しいスタイルのクラスにし、super()を使うことができます。

27

問題は、superが祖先としてobjectを必要とすることです。

>>> class oldstyle:
...     def __init__(self): self.os = True

>>> class myclass(oldstyle):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass()
TypeError: must be type, not classobj

よく調べてみると、

>>> type(myclass)
classobj

しかし:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

だからあなたの問題に対する解決策はHTMLParserからと同様にオブジェクトから継承することであろう。しかし、オブジェクトがクラスMROの最後に来るようにしてください。

>>> class myclass(oldstyle, object):
...     def __init__(self): super(myclass, self).__init__()

>>> myclass().os
True
21
user2070206

継承ツリー(バージョン2.6)を見ると、HTMLParserSGMLParserを継承しています。これはParserBaseを継承しています。これはdoes n'tobjectを継承しています。つまりHTMLParserは古いスタイルのクラスです。

isinstanceを使用したチェックについて、ipythonで簡単なテストを行いました。

 [1]:クラスA:
 ...:pass 
 ...:
 
 In [2]:isinstance(A 、オブジェクト)
 Out [2]:True 

クラスが古いスタイルのクラスであっても、objectのインスタンスのままです。

17

正しいやり方は 'object'から継承しない古いスタイルのクラスに従うことです

class A:
    def foo(self):
        return "Hi there"

class B(A):
    def foo(self, name):
        return A.foo(self) + name
5
Jacob Abraham

FWIWそして私はPythonの第一人者ではありませんが

>>> class TextParser(HTMLParser):
...    def handle_starttag(self, tag, attrs):
...        if tag == "b":
...            self.all_data.append("bold")
...        else:
...            self.all_data.append("other")
...     
...         
>>> p = TextParser()
>>> p.all_data = []
>>> p.feed(text)
>>> print p.all_data
(...)

解析結果を必要に応じて返してください。

0
qwerty_so