super () tăng lên Loại TypeError: phải là loại chứ không phải classobj 'cho lớp kiểu mới


335

Việc sử dụng sau đây super()làm tăng TypeError: tại sao?

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

Có một câu hỏi tương tự trên StackOverflow: Python super () làm tăng TypeError , trong đó lỗi được giải thích bởi thực tế là lớp người dùng không phải là một lớp kiểu mới. Tuy nhiên, lớp ở trên là một lớp kiểu mới, vì nó kế thừa từ object:

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

Tôi đang thiếu gì? Làm thế nào tôi có thể sử dụng super(), ở đây?

Sử dụng HTMLParser.__init__(self)thay vì super(TextParser, self).__init__()sẽ hoạt động, nhưng tôi muốn hiểu TypeError.

PS: Joachim đã chỉ ra rằng việc là một thể hiện của lớp kiểu mới không tương đương với việc là một object. Tôi đã đọc ngược lại nhiều lần, do đó tôi nhầm lẫn (ví dụ về kiểm tra cá thể lớp kiểu mới dựa trên objectkiểm tra cá thể: https://stackoverflow.com/revutions/2655651/3 ).


3
Cảm ơn câu hỏi và câu trả lời của bạn. Tôi tự hỏi tại sao 2.7 super.__doc__không đề cập bất cứ điều gì về phong cách cũ và mới!
Kelvin

Cảm ơn. :) Tài liệu thường chứa ít thông tin hơn phiên bản HTML đầy đủ của tài liệu. Thực tế super()chỉ hoạt động cho các lớp (và đối tượng) kiểu mới được đề cập trong tài liệu HTML ( docs.python.org/l Library / fiances.html # supup ).
Eric O Lebigot


Đây không phải là một bản sao (xem câu hỏi cập nhật và câu trả lời được chấp nhận).
Eric O Lebigot

Câu trả lời:


246

Được rồi, đó là "thông thường" super()không thể được sử dụng với một lớp kiểu cũ ".

Tuy nhiên, điểm quan trọng là kiểm tra đúng cho "là này một phong cách mới dụ (tức là đối tượng)?" Là

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

và không (như trong câu hỏi):

>>> isinstance(instance, object)
True

Đối với các lớp , bài kiểm tra chính xác "đây có phải là một lớp kiểu mới" không:

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

Điểm cốt yếu là với các lớp kiểu cũ, lớp của một thể hiện và kiểu của nó là khác biệt. Dưới đây, OldStyle().__class__OldStyle, mà không kế thừa từ object, trong khi type(OldStyle())instanceloại, mà không kế thừa từ object. Về cơ bản, một lớp kiểu cũ chỉ tạo các đối tượng kiểu instance(trong khi đó lớp kiểu mới tạo các đối tượng có kiểu là chính lớp đó). Đây có lẽ là lý do tại sao cá thể OldStyle()là một object: type()kế thừa của nó object(thực tế là lớp của nó không được thừa kế từ objectkhông được tính: các lớp kiểu cũ chỉ đơn thuần xây dựng các đối tượng kiểu mới instance). Tham khảo một phần:https://stackoverflow.com/a/9699961/42973 .

PS: Cũng có thể thấy sự khác biệt giữa lớp kiểu mới và lớp kiểu cũ:

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

(các lớp kiểu cũ không phải là kiểu, vì vậy chúng không thể là kiểu của thể hiện của chúng).


11
Và đây là một trong những lý do chúng ta có Python 3.
Steven Rumbalski 15/03/2016

2
BTW: (Oldstyle().__class__ is Oldstyle)True
Tino

2
@Tino: thực sự, nhưng quan điểm OldStyle().__class__là chỉ ra cách kiểm tra xem một đối tượng ( OldStyle()) có đến từ một lớp kiểu cũ hay không. Chỉ có các lớp học kiểu mới trong tâm trí, người ta có thể bị cám dỗ để làm bài kiểm tra isinstance(OldStyle(), object)thay thế.
Eric O Lebigot

27
Thật vô lý khi thư viện tiêu chuẩn python vẫn còn trong 2.7.x không được thừa kế từ objectđó, do đó làm hỏng bạn bằng proxy.
Nick Bastin

2
@NickBastin - đây không phải là sự trùng hợp. Đó là tất cả để đẩy tất cả mọi người vào Python 3. Trong đó "mọi thứ đều ổn cả." Nhưng - emptor caveat - nó chỉ mồi và chuyển đổi.
Tomasz Gandor

204

super () chỉ có thể được sử dụng trong các lớp kiểu mới, có nghĩa là lớp gốc cần kế thừa từ lớp 'đối tượng'.

Ví dụ, lớp hàng đầu cần phải như thế này:

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

không phải

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

Vì vậy, giải pháp là gọi trực tiếp phương thức init của cha mẹ , như cách này:

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

8
Đối với tôi, tôi đã phải làm điều này: HTMLParser .__ init __ (tự) Tôi tò mò liệu ví dụ cuối cùng của bạn có hiệu quả không?
chaimp

1
@EOL nghĩa là gì? jeffp chỉ chỉ ra rằng mã được đưa ra trong câu trả lời này là sai do thiếu selftham số trong HTMLParser.__init__()cuộc gọi.
Piotr Dobrogost

1
@PiotrDobrogost: Xin lỗi, nhận xét của tôi là về câu trả lời của LittleQ, không phải về điểm (tốt) của jeffp.
Eric O Lebigot

1
@jeffp xin lỗi, đó là một lỗi đánh máy, tôi chỉ gõ nó trên SO nhưng không kiểm tra được, lỗi của tôi. cảm ơn vì đã sửa lỗi
Colin Su

1
Upvote cho một bản sửa lỗi hoạt động với mã hiện có, chẳng hạn như đăng nhập.Formatter trong python2.6
David Reynold

28

Bạn cũng có thể sử dụng class TextParser(HTMLParser, object):. Điều này làm cho TextParsermột lớp phong cách mới , và super()có thể được sử dụng.


Một upvote từ tôi, vì thêm sự kế thừa từ đối tượng là một ý tưởng tốt. (Điều đó nói rằng, câu trả lời này không giải quyết vấn đề hiểu TypeError của câu hỏi.)
Eric O Lebigot

23

Vấn đề là supercần objectmột tổ tiên:

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

Khi xem xét kỹ hơn người ta thấy:

>>> type(myclass)
classobj

Nhưng:

>>> class newstyle(object): pass

>>> type(newstyle)
type    

Vì vậy, giải pháp cho vấn đề của bạn sẽ là kế thừa từ đối tượng cũng như từ HTMLParser. Nhưng hãy chắc chắn rằng đối tượng xuất hiện cuối cùng trong các lớp MRO:

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

>>> myclass().os
True

Điểm hợp lệ, nhưng chúng đã có trong câu trả lời trước. Ngoài ra, thay vì kiểm tra type(myclass), điều quan trọng là liệu myclasskế thừa từ đối tượng (tức là liệu isinstance(myclass, object)có đúng hay không, nó là sai).
Eric O Lebigot

17

Nếu bạn nhìn vào cây thừa kế (trong phiên bản 2.6), HTMLParserkế thừa từ SGMLParserđó thừa kế từ ParserBaseđó không kế thừa từ đó object. Tức là HTMLParser là một lớp kiểu cũ.

Về việc kiểm tra của bạn với isinstance, tôi đã làm một bài kiểm tra nhanh trong ipython:

Trong [1]: lớp A:
   ...: vượt qua
   ... 

Trong [2]: isinstance (A, object)
Hết [2]: Đúng

Ngay cả khi một lớp là lớp kiểu cũ, nó vẫn là một ví dụ object.


2
Tôi tin rằng các bài kiểm tra chính xác nên isinstance(A(), object), không isinstance(A, object), không? Với trường hợp sau, bạn đang thử nghiệm liệu lớp A là một object, trong khi câu hỏi là liệu trường hợp của Alà một object, phải không?
Eric O Lebigot

5
PS: thử nghiệm tốt nhất dường như là issubclass(HTMLParser, object), trả về Sai.
Eric O Lebigot

5

cách làm đúng sẽ như sau trong các lớp kiểu cũ không kế thừa từ 'đối tượng'

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

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

1
Trong câu hỏi, phương pháp này đã được đề cập: vấn đề là để hiểu tại sao một thông báo lỗi được tạo ra, không làm cho nó biến mất (đặc biệt là theo cách này).
Eric O Lebigot

Ngoài ra, giải pháp này sẽ không hoạt động trong trường hợp chúng tôi muốn gọi một thể hiện của lớp Alưu trữ trạng thái.
tashuhka

0

FWIW và mặc dù tôi không phải là bậc thầy về Python, tôi đã có được điều này

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

Chỉ cần cho tôi kết quả phân tích trở lại khi cần thiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.