2013-07-10 29 views
7

này làm việc như mong đợi:Tại sao siêu hạng của một classmethod cần một đối số thứ hai?

>>> class Foo(object): 
... @classmethod 
... def hello(cls): 
...  print 'hello, foo' 
... 
>>> class Bar(Foo): 
... @classmethod 
... def hello(cls): 
...  print 'hello, bar' 
...  super(Bar, cls).hello() 
... 
>>> b = Bar() 
>>> b.hello() 
hello, bar 
hello, foo 

tôi cũng có thể gọi lớp cơ sở một cách rõ ràng:

>>> class Bar(Foo): 
... @classmethod 
... def hello(cls): 
...  print 'hello, bar' 
...  Foo.hello() 
... 
>>> b = Bar() 
>>> b.hello() 
hello, bar 
hello, foo 

tôi đã tự hỏi tại sao tôi không thể bỏ qua đối số đầu tiên super, như thế này:

>>> class Bar(Foo): 
... @classmethod 
... def hello(cls): 
...  print 'hello, bar' 
...  super(Bar).hello() 
... 
>>> b = Bar() 
>>> b.hello() 
hello, bar 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in hello 
AttributeError: 'super' object has no attribute 'hello' 

khi kết quả cuộc gọi super không có đối số thứ hai có vẻ là loại lớp bên trong một loại siêu :

>>> class Bar(Foo): 
... @classmethod 
... def hello(cls): 
...  print Foo, type(Foo) 
...  print super(Bar), type(super(Bar)) 
...  print cls, type(cls) 
... 
>>> b = Bar() 
>>> b.hello() 
<class '__main__.Foo'> <type 'type'> 
<super: <class 'Bar'>, NULL> <type 'super'> 
<class '__main__.Bar'> <type 'type'> 

Tôi đoán tôi đang tự hỏi về thiết kế ở đây. Tại sao tôi cần truyền đối tượng lớp vào cuộc gọi siêu để nhận tham chiếu đến loại lớp cơ sở Foo? Đối với một phương pháp bình thường, nó có ý nghĩa để vượt qua self cho hàm, vì nó cần phải ràng buộc kiểu lớp cơ sở với một thể hiện thực tế của lớp đó. Nhưng một classmethod không cần một instance cụ thể của class.

EDIT: Tôi gặp lỗi tương tự trong Python 3.2 như tôi đã làm ở trên trong 2.7 cho super(Bar).hello(). Tuy nhiên, tôi chỉ có thể làm super().hello() và hoạt động tốt.

+0

Trong python 3.x họ sửa siêu cuộc gọi khá một chút ... trong python2x họ chỉ không nghĩ rằng thông qua đó nhiều (là tôi đoán ...) anyway tôi nghĩ rằng điều này sẽ kết thúc đóng như câu hỏi "Tại sao" thường là ... –

+0

Bạn có thể thấy điều này hữu ích: http://stackoverflow.com/questions/11354786/super-confusing-python-multiple-inheritance-super?rq=1 – mdscruggs

+0

@JoranBeasley meh, I đã hỏi một số câu hỏi kiểu tại sao trước đó chưa được đóng lại. – jterrace

Trả lời

7

super() trả về một descriptor, và nhu cầu của hai hạng mục:

  • Một điểm khởi đầu mà từ đó để tìm kiếm hệ thống phân cấp lớp.
  • Đối số cho ràng buộc phương thức trả lại.

Đối với hai lập luận (và tiềm ẩn zero-luận *) trường hợp đối số thứ hai được sử dụng để liên kết với, nhưng nếu bạn không vượt qua trong một cuộc tranh luận thứ hai, super() không thể gọi giao thức mô tả để ràng buộc các hàm trả về, classmethods, các thuộc tính hoặc các bộ mô tả khác. classmethods vẫn là mô tả và bị ràng buộc; liên kết với một lớp và không phải là một cá thể, nhưng super()không biết cách trình mô tả sẽ sử dụng ngữ cảnh mà bạn ràng buộc.

super() không nên và không thể biết rằng bạn đang tìm kiếm phương pháp lớp thay vì phương pháp thông thường; các phương thức lớp chỉ khác với các phương thức thông thường vì phương thức .__get__() của chúng hoạt động khác nhau.

Tại sao các phương pháp lớp học bị ràng buộc? Bởi vì khi bạn phân lớp Foo nhưng làm không override .hello(), gọi Bar.hello() gọi các Foo.__dict__['hello'] chức năng, kết nối nó đến Bar và số đầu tiên của bạn để hello(cls) sẽ là lớp con, không Foo.

Không có đối số thứ hai, super() trả về đối tượng không liên kết có thể được ràng buộc theo cách thủ công sau này.Bạn có thể làm các ràng buộc mình bằng cách sử dụng phương pháp .__get__() cung cấp bởi super() dụ:

class Bar(Foo): 
    @classmethod 
    def hello(cls): 
     print 'hello, bar' 
     super(Bar).__get__(cls, None).hello() 

super().__get__() trên một thể hiện mà không có một bối cảnh hiệu quả trả về một super() dụ mới với bối cảnh được thiết lập. Trên một cá thể có ngữ cảnh .__get__() chỉ trả lại self; nó đã bị ràng buộc rồi.


* Trong Python 3, gọi super() không có đối số từ bên trong một phương pháp ràng buộc sẽ sử dụng khung gọi để khám phá, mặc nhiên, những gì các loại và đối tượng ràng buộc là, vì vậy bạn không còn phải rõ ràng vượt qua trong đối số kiểu và đối tượng trong trường hợp đó. Python 3 thực sự thêm một biến đóng mở ngụ ý __class__ cho các phương thức cho mục đích này. Xem PEP 3135Why is Python 3.x's super() magic?

Các vấn đề liên quan