2010-02-11 15 views
10

Tôi đã tự hỏi điều gì sẽ xảy ra với các phương thức được khai báo trên một metaclass. Tôi hy vọng rằng nếu bạn khai báo một phương thức trên một metaclass, nó sẽ kết thúc là một classmethod, tuy nhiên, hành vi là khác nhau. Ví dụphương pháp metaclasses trên các cá thể lớp

>>> class A(object): 
...  @classmethod 
...  def foo(cls): 
...   print "foo" 
... 
>>> a=A() 
>>> a.foo() 
foo 
>>> A.foo() 
foo 

Tuy nhiên, nếu tôi cố gắng xác định một metaclass và cho nó một phương pháp foo, có vẻ như hoạt động tương tự đối với lớp học, không phải cho cá thể.

>>> class Meta(type): 
...  def foo(self): 
...   print "foo" 
... 
>>> class A(object): 
...  __metaclass__=Meta 
...  def __init__(self): 
...   print "hello" 
... 
>>> 
>>> a=A() 
hello 
>>> A.foo() 
foo 
>>> a.foo() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: 'A' object has no attribute 'foo' 

Điều gì đang diễn ra ở đây chính xác?

chỉnh sửa: chạm các câu hỏi

+0

Đây có phải là http://stackoverflow.com/questions/1770712/metaclass-not-being-called-in-subclasses cùng một vấn đề cơ bản không? –

+0

@Stefano: vậy, bạn không hài lòng với câu trả lời của tôi là bạn? :-) –

+0

@Olivier: tuyệt đối! rõ ràng và tuyến tính. Nhờ bạn và tất cả những người khác nữa. –

Trả lời

12

Bạn tăng điểm tốt.

Đây là một good reference để có được một sự hiểu biết tốt hơn về các mối quan hệ giữa các đối tượng, các lớp học và metaclasses:

Tôi cũng tìm this reference on descriptors là khá sáng tỏ về cơ chế nhìn lên trong python.

Nhưng tôi không thể nói tôi hiểu tại sao a.foo không thành công khi A.foo thành công. Có vẻ như khi bạn tra cứu một thuộc tính của một đối tượng, và python không tìm thấy nó ở đó, nó không chính xác tra cứu thuộc tính trong lớp, bởi vì nếu nó đã làm, nó sẽ tìm thấy A.foo.

EDIT:

Oh! Tôi nghĩ rằng tôi đã nhận nó. Đó là do cách thức hoạt động của thừa kế. Nếu bạn xem xét các giản đồ được cung cấp bởi các above link, nó trông như thế này:

alt text http://www.cafepy.com/article/python_types_and_objects/images/types_map.png

sơ đồ, nó nắm xuống:

type -- object 
    |  | 
Meta -- A -- a 

Đi trái nghĩa đi đến lớp của một định ví dụ. Đi lên có nghĩa là đi tới số cha mẹ.

Bây giờ cơ chế thừa kế làm cho cơ chế tra cứu thực hiện rẽ phải trong lược đồ ở trên. Nó đi a → A → object. Nó phải làm như vậy để thực hiện theo các quy tắc thừa kế!Để làm rõ, đường dẫn tìm kiếm là:

object 
^
    | 
    A <-- a 

Sau đó, rõ ràng, thuộc tính foo sẽ không được tìm thấy.

Khi bạn tra cứu cho thuộc tính foo trong A, tuy nhiên, nó được tìm thấy, bởi vì con đường tìm kiếm là:

type 
^
    |  
Meta <-- A 

Tất cả đều có ý nghĩa khi một người nghĩ về thừa kế làm việc như thế nào.

+0

+1 Tài liệu được liên kết thú vị, ta. – MattH

+0

ồ vâng, tài liệu đó. Tôi đã đọc nó từ lâu rồi. thời gian để đọc lại nó. Cảm ơn. –

+0

cảm ơn vì liên kết –

7

Quy tắc là như thế này: khi tìm kiếm một thuộc tính trên một đối tượng, lớp của đối tượng và lớp cha của nó được coi là tốt. Tuy nhiên, metaclass của lớp đối tượng là không được xem là. Khi bạn truy cập một thuộc tính của một lớp, lớp của lớp là metaclass, do đó, nó là được xem là. Dự phòng từ đối tượng đến lớp của nó không kích hoạt tra cứu thuộc tính "bình thường" trên lớp: ví dụ, các bộ mô tả được gọi khác nhau cho dù thuộc tính được truy cập trên một cá thể hay lớp của nó.

Phương thức là thuộc tính có thể gọi (và có phương thức __get__ giúp 'tự' được tự động chuyển.) Điều đó làm cho nó sao cho các phương thức trên metaclass giống như classmethods nếu bạn gọi chúng trên lớp, nhưng không có sẵn ví dụ.

+0

ok nhưng lớp của đối tượng A được tạo ra thông qua sự khởi tạo của metaclass. do đó, A (đối tượng lớp) có phương thức foo, và bất kỳ cá thể nào của A sẽ tra cứu lớp của nó để giải quyết các phương thức ... Chờ .. Tôi cố gắng đọc lại bạn. –

+0

@Stefano: A * không * có phương pháp; lớp của nó. –

+0

Lớp học không có thuộc tính. Metaclass có thuộc tính. Bạn có thể truy cập thuộc tính metaclass thông qua lớp, nhưng nó không giống với lớp có thuộc tính. Bởi vì metaclass và không phải lớp có thuộc tính, các cá thể của lớp không có quyền truy cập vào thuộc tính. –

0

Cách tôi hiểu nó là Meta là một lớp, và A là một thể hiện của nó. Vì vậy, khi bạn gọi A.foo(), nó sẽ kiểm tra đối tượng và lớp của nó. Vì vậy, khi cố gắng A.foo, nó đầu tiên xem xét thông qua các phương pháp A chính nó nắm giữ, và sau đó các phương pháp của lớp học của nó, Meta. Vì A không có phương thức nào, nó sử dụng một trong Meta, và vì thế thực sự thực hiện Meta.foo (A).

Tương tự, khi a.foo được thử, trước tiên nó sẽ xem qua a. Như một tổ chức không có phương pháp foo, nó sẽ xem xét thông qua A. Nhưng A cũng không có phương pháp foo, như foo được tổ chức trong Meta. Vì không phải một A cũng không giữ foo, nó sẽ làm tăng AttributeError.

Tôi đã thử điều này với một biến cũng như một hàm, đưa vào lớp Meta một thuộc tính txt = 'txt' và điều này cũng có thể truy cập bằng A, nhưng không phải là a. Vì vậy, tôi có khuynh hướng nghĩ rằng tôi đúng trong sự hiểu biết của tôi, nhưng tôi chỉ đoán thôi.