2016-07-28 13 views
5

Để bắt mắt của bạn:Có thể gọi metaclass có thể gọi được không?

Tôi nghĩ tài liệu có thể sai!

Theo tài liệu Python 2.7.12, 3.4.3. Tuỳ chỉnh lớp creation¶:

__metaclass__ biến này có thể là bất kỳ callable chấp nhận đối số cho tên, căn cứ, và dict. Khi tạo lớp học, số điện thoại có thể gọi được thay vì được xây dựng trong type().

Tính năng mới trong phiên bản 2.2.

Tuy nhiên, this article lập luận:

Q: Wow! Tôi có thể sử dụng bất kỳ đối tượng kiểu nào làm __metaclass__ không?

A: Không. Nó phải là lớp con của loại đối tượng cơ sở. ...

Vì vậy, tôi đã làm một thí nghiệm trên của riêng tôi:

class metacls(list): # <--- subclassing list, rather than type 
    def __new__(mcs, name, bases, dict): 
     dict['foo'] = 'metacls was here' 
     return type.__new__(mcs, name, bases, dict) 

class cls(object): 
    __metaclass__ = metacls 
    pass 

này mang lại cho tôi:

Traceback (most recent call last): 
    File "test.py", line 6, in <module> 
    class cls(object): 
    File "test.py", line 4, in __new__ 
    return type.__new__(mcs, name, bases, dict) 
TypeError: Error when calling the metaclass bases 
    type.__new__(metacls): metacls is not a subtype of type 

Vì vậy, là tài liệu thực sự xảy ra?

Trả lời

5

Không, bất kỳ cuộc gọi nào cũng có thể thực hiện được. Trong trường hợp của bạn, phương thức type.__new__() có một hạn chế mà bạn đang vi phạm; điều này không liên quan gì đến những gì bạn chỉ định cho __metaclass__.

Một chức năng là một callable:

def metaclass_function(name, bases, body): 
    return type(name, bases, body) 

Cái này chỉ trả về kết quả của type() (không type.__new__()), nhưng nó là chỉ là một callable. Giá trị trả lại được sử dụng làm lớp. Bạn thực sự có thể trả lại bất kỳ điều gì:

>>> class Foo(object): 
...  __metaclass__ = lambda *args: [] 
... 
>>> Foo 
[] 

Đây là danh sách được tạo ra có thể gọi được, do đó Foo bị ràng buộc vào danh sách. Không hữu ích lắm, nhưng __metaclass__ chỉ được gọi là để sản xuất một cái gì đó và thứ gì đó được sử dụng trực tiếp.

Trong ví dụ của bạn, đối số đầu tiên để type.__new__()không phải là một loại, và nó là mà gọi thất bại. mcs được gắn với list và không phải là (phân lớp) type. type.__new__() là miễn phí để thiết lập các hạn chế như vậy.

Bây giờ, vì một metaclass vẫn gắn với đối tượng lớp (type(ClassObj) trả về nó) và nó được sử dụng khi giải quyết tra cứu thuộc tính trên đối tượng lớp (trong đó thuộc tính không có sẵn trong lớp MRO), thường là làm nổi bật ý tưởng hay để biến nó thành lớp con của type, vì điều đó mang lại cho bạn việc triển khai đúng các thứ như __getattribute__. Đó là vì lý do đó mà type.__new__() làm cho một hạn chế về những gì có thể được thông qua như là đối số đầu tiên; đó là đối số đầu tiên mà type() gắn với đối tượng lớp được trả về.

+0

"bị ràng buộc" có nghĩa là "một phân lớp", phải không? –

+0

@sunqingyao: không, 'ràng buộc' có nghĩa là được gán cho. Phép gán 'Foo = []' được thực hiện. Câu lệnh 'class' về cơ bản được coi là một hàm; cơ thể được thực hiện như thể trong một hàm, và tất cả các tên địa phương được trích xuất thành một từ điển. Sau đó, đối tượng lớp được tạo ra bằng cách gọi 'bodydict.get ('__ metaclass__', type)' (do đó, hoặc là giá trị '__metaclass__' hoặc, nếu thiếu,' type') và chuyển tên lớp, căn cứ lớp và từ điển cơ thể. * Bất cứ điều gì được trả về * được gán cho tên của lớp. Ràng buộc là thuật ngữ kỹ thuật; những thứ khác cũng ràng buộc, như 'import' hoặc' for'. –

+0

Nhưng bằng cách nói "' mcs' bị ràng buộc với 'danh sách'", bạn có nghĩa là "' mcs' là một thể hiện của một phân lớp của 'danh sách'". –

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