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


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


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


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 = []

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.


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

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.


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

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.