super () memunculkan “TypeError: harus mengetik, bukan classobj” untuk kelas gaya baru

335

Penggunaan berikut ini super()memunculkan TypeError: mengapa?

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

Ada pertanyaan serupa di StackOverflow: Python super () memunculkan TypeError , di mana kesalahannya dijelaskan oleh fakta bahwa kelas pengguna bukan kelas gaya baru. Namun, kelas di atas adalah kelas gaya baru, karena mewarisi dari object:

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

Apa yang saya lewatkan? Bagaimana saya bisa menggunakan super(), di sini?

Menggunakan HTMLParser.__init__(self)bukannya super(TextParser, self).__init__()akan bekerja, tapi saya ingin memahami TypeError.

PS: Joachim menunjukkan bahwa menjadi contoh kelas gaya baru tidak setara dengan menjadi object. Saya membaca kebalikannya berkali-kali, karenanya kebingungan saya (contoh uji contoh kelas gaya baru berdasarkan objectuji contoh: https://stackoverflow.com/revisions/2655651/3 ).

Eric O Lebigot
sumber
3
Terima kasih atas pertanyaan dan jawaban Anda. Saya bertanya-tanya mengapa 2,7 super.__doc__tidak menyebutkan apa pun tentang gaya lama vs baru!
Kelvin
Terima kasih. :) Dokumen biasanya mengandung informasi yang lebih sedikit daripada versi HTML lengkap dari dokumentasi. Fakta yang super()hanya berfungsi untuk kelas gaya baru (dan objek) disebutkan dalam dokumen HTML ( docs.python.org/library/functions.html#super ).
Eric O Lebigot
1
kemungkinan duplikat python super () memunculkan TypeError! Mengapa?
pengguna
Ini bukan duplikat (lihat pertanyaan yang diperbarui, dan jawaban yang diterima).
Eric O Lebigot

Jawaban:

246

Baiklah, itu biasa " super()tidak dapat digunakan dengan kelas gaya lama".

Namun, poin penting adalah bahwa tes yang benar untuk "apakah ini contoh gaya baru (yaitu objek)?" adalah

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

dan tidak (seperti dalam pertanyaan):

>>> isinstance(instance, object)
True

Untuk kelas , tes "apakah ini kelas gaya baru" yang benar adalah:

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

The titik penting adalah bahwa dengan kelas gaya lama, yang kelas dari sebuah contoh dan yang jenis yang berbeda. Berikut, OldStyle().__class__adalah OldStyle, yang tidak mewarisi dari object, sedangkan type(OldStyle())adalah instancejenis, yang tidak mewarisi dari object. Pada dasarnya, kelas gaya lama hanya menciptakan objek bertipe instance(sedangkan kelas gaya baru menciptakan objek yang tipenya adalah kelas itu sendiri). Ini mungkin mengapa turunannya OldStyle()adalah object: ia type()mewarisi dari object(fakta bahwa kelasnya tidak diwarisi dari objecttidak masuk hitungan: kelas gaya lama hanya membangun objek tipe baru instance). Referensi parsial:https://stackoverflow.com/a/9699961/42973 .

PS: Perbedaan antara kelas gaya baru dan kelas gaya lama juga dapat dilihat dengan:

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

(kelas gaya lama bukan tipe, jadi mereka tidak bisa menjadi tipe instance mereka).

Eric O Lebigot
sumber
11
Dan ini adalah salah satu alasan kita sekarang memiliki Python 3.
Steven Rumbalski
2
BTW: (Oldstyle().__class__ is Oldstyle)adalahTrue
Tino
2
@Tino: memang, tetapi intinya OldStyle().__class__adalah untuk menunjukkan bagaimana menguji apakah suatu objek ( OldStyle()) berasal dari kelas gaya lama. Dengan hanya kelas gaya baru dalam pikiran, orang mungkin tergoda untuk melakukan tes dengan isinstance(OldStyle(), object)gantinya.
Eric O Lebigot
27
Sungguh tidak masuk akal berapa banyak pustaka standar python masih dalam 2.7.x tidak mewarisi object, sehingga mengacaukan Anda dengan proxy.
Nick Bastin
2
@NickBastin - ini bukan kebetulan. Ini semua untuk mendorong semua orang ke Python 3. Di mana "semuanya sudah baik-baik saja." Tapi - emptor peringatan - itu hanya umpan dan beralih.
Tomasz Gandor
204

super () dapat digunakan hanya di kelas-kelas gaya baru, yang berarti kelas root perlu mewarisi dari kelas 'objek'.

Sebagai contoh, kelas atas harus seperti ini:

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

tidak

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

Jadi, solusinya adalah memanggil metode init orang tua secara langsung, seperti ini:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.all_data = []
Colin Su
sumber
8
Bagi saya, saya harus melakukan ini: HTMLParser .__ init __ (mandiri) Saya ingin tahu apakah contoh terakhir Anda berhasil?
chaimp
1
@ EOL Apa maksudnya? jeffp baru saja menunjukkan bahwa kode yang diberikan dalam jawaban ini salah karena kurangnya selfparameter yang HTMLParser.__init__()dipanggil.
Piotr Dobrogost
1
@PiotrDobrogost: Maaf, komentar saya tentang jawaban LittleQ, bukan tentang poin (baik) jeffp.
Eric O Lebigot
1
@ jeffp maaf, ini salah ketik, saya cukup mengetikkannya di SO tetapi belum mengujinya, salah saya. terima kasih sudah mengoreksi
Colin Su
1
Suara positif untuk perbaikan yang berfungsi dengan kode yang ada, seperti logging.Formatter di python2.6
David Reynolds
28

Anda juga bisa menggunakan class TextParser(HTMLParser, object):. Hal ini membuat TextParsersebuah gaya baru kelas, dan super()dapat digunakan.

Valentin Lorentz
sumber
Suara positif dari saya, seperti menambahkan warisan dari objek adalah ide bagus. (Yang mengatakan, jawaban ini tidak membahas masalah memahami TypeError dari pertanyaan.)
Eric O Lebigot
23

Masalahnya adalah yang supermembutuhkan objectsebagai leluhur:

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

Pada pemeriksaan lebih dekat seseorang menemukan:

>>> type(myclass)
classobj

Tapi:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

Jadi solusi untuk masalah Anda adalah dengan mewarisi dari objek maupun dari HTMLParser. Tapi pastikan objek menjadi yang terakhir di kelas MRO:

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

>>> myclass().os
True
pengguna2070206
sumber
Poin yang valid, tetapi sudah ada di jawaban sebelumnya. Juga, alih-alih memeriksa type(myclass), yang penting adalah apakah myclassmewarisi dari objek (yaitu apakah isinstance(myclass, object)itu benar — itu salah).
Eric O Lebigot
17

Jika Anda melihat pohon warisan (dalam versi 2.6), HTMLParsermewarisi dari SGMLParsermana warisan dari ParserBasemana tidak mewarisi dariobject . Yaitu HTMLParser adalah kelas gaya lama.

Tentang pemeriksaan Anda dengan isinstance, saya melakukan tes cepat di ipython:

Dalam [1]: kelas A:
   ...: lulus
   ...: 

Dalam [2]: isinstance (A, object)
Keluar [2]: Benar

Bahkan jika suatu kelas adalah kelas gaya lama, itu masih merupakan contoh object.

Beberapa programmer Bung
sumber
2
Saya percaya bahwa tes yang benar seharusnya isinstance(A(), object), bukan isinstance(A, object), bukan? Dengan yang terakhir, Anda menguji apakah kelas A adalah object, sedangkan pertanyaannya adalah apakah contoh dari Aseorang object, kan?
Eric O Lebigot
5
PS: tes terbaik tampaknya issubclass(HTMLParser, object), yang mengembalikan False.
Eric O Lebigot
5

cara yang benar untuk dilakukan adalah mengikuti kelas-kelas gaya lama yang tidak mewarisi dari 'objek'

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

class B(A):
    def foo(self, name):
        return A.foo(self) + name
Yakub Abraham
sumber
1
Dalam pertanyaan, metode ini sudah disebutkan: masalahnya adalah untuk memahami mengapa pesan kesalahan dibuat, bukan untuk membuatnya pergi (khususnya dengan cara ini).
Eric O Lebigot
Selain itu, solusi ini tidak akan berfungsi jika kita ingin panggilan instance kelas Ayang menyimpan keadaan.
tashuhka
0

FWIW dan meskipun saya bukan guru Python saya berhasil dengan ini

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

Baru saja saya mendapatkan hasil parse kembali sesuai kebutuhan.

qwerty_so
sumber