Bạn đang suy đoán rất nhiều, trong khi Python tối giản và "Các trường hợp đặc biệt không đủ đặc biệt để phá vỡ các quy tắc". chỉ thị, làm cho nó dễ hiểu hơn thế.
Trong Python2, thuộc tính __metaclass__
trong nội dung lớp được sử dụng trong thời gian tạo lớp để gọi "lớp" mà lớp đó sẽ là. Thông thường nó là lớp có tên là type
.Để làm rõ, thời điểm đó là sau khi trình phân tích cú pháp đã phân tích cú pháp thân lớp, sau khi trình biên dịch biên dịch nó thành đối tượng mã, và sau khi nó thực sự chạy ở thời gian chạy chương trình, và chỉ khi __metaclass__
được cung cấp rõ ràng trong thân lớp đó.
Vì vậy, cách kiểm tra của let mà đi trong một trường hợp như thế:
class A(object):
__metaclass__ = MetaA
class B(A):
pass
A
có __metaclass__
trong cơ thể của nó - MetaA
được gọi thay vì type
để làm cho nó vào "đối tượng lớp". B
không có __metaclass__
trong cơ thể. Sau khi nó được tạo ra, nếu bạn chỉ cố gắng truy cập thuộc tính __metaclass__
, nó là một thuộc tính như anyother, sẽ được hiển thị vì Python sẽ lấy nó từ siêu lớp A
. Nếu bạn kiểm tra A.__dict__
bạn sẽ thấy __metaclass__
và nếu bạn kiểm tra B.__dict__
thì không.
Thuộc tính A.__metaclass__
này là không được sử dụng khi nào B được tạo. Nếu bạn thay đổi nó trong A
trước khi khai báo B
sẽ vẫn sử dụng cùng một metaclass là A
- bởi vì Python không sử dụng loại của lớp cha mẹ như metaclass trong sự vắng mặt của việc khai báo rõ ràng __metaclass__
.
Để minh họa:
In [1]: class M(type): pass
In [2]: class A(object): __metaclass__ = M
In [3]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(A.__class__, A.__metaclass__, A.__dict__.get("__metaclass__"), type(A))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: <class '__main__.M'>, type: <class '__main__.M'>
In [4]: class B(A): pass
In [5]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(B.__class__, B.__metaclass__, B.__dict__.get("__metaclass__"), type(B))
class: <class '__main__.M'>, metaclass_attr: <class '__main__.M'>, metaclass_in_dict: None, type: <class '__main__.M'>
In [6]: A.__metaclass__ = type
In [8]: class C(A): pass
In [9]: print "class: {}, metaclass_attr: {}, metaclass_in_dict: {}, type: {}".format(C.__class__, C.__metaclass__, C.__dict__.get("__metaclass__"), type(C))
class: <class '__main__.M'>, metaclass_attr: <type 'type'>, metaclass_in_dict: None, type: <class '__main__.M'>
Hơn nữa, nếu bạn cố gắng để chỉ cần tạo một lớp thông qua một cuộc gọi đến type
thay vì sử dụng một cơ thể với một tuyên bố class
, __metaclass__
cũng chỉ là một thuộc tính bình thường:
In [11]: D = type("D", (object,), {"__metaclass__": M})
In [12]: type(D)
type
Tổng hợp cho đến nay: Thuộc tính __metaclass__
trong Python 2 chỉ đặc biệt nếu được đặt trong cơ thể lớp một cách rõ ràng khai báo, như là một phần của việc thực hiện tuyên bố khối class
. Nó là một thuộc tính bình thường không có đặc tính đặc biệt sau đó.
Cả Python3 đều đã loại bỏ thuộc tính lạ "__metaclass__
này hiện không tốt" và được phép tùy chỉnh thêm cho thân lớp bằng cách thay đổi cú pháp để chỉ định metaclasses. (Nó giống như tuyên bố như thể nó là một "metaclass
tên tham số" trên class
tuyên bố chính nó)
Bây giờ, đến phần thứ hai của những gì nêu ra những nghi ngờ của bạn: nếu trong phương pháp __new__
của metaclass bạn gọi type
thay vì type.__new__
, không có cách nào Python có thể "biết" type
đang được gọi từ một metaclass có nguồn gốc. Khi bạn gọi type.__new__
, bạn chuyển làm tham số đầu tiên của thuộc tính cls
, chính số __new__
của metaclass của bạn đã được chuyển bởi thời gian chạy: đó là điều đánh dấu lớp kết quả là một thể hiện của một lớp con của type
.Đó là giống như thừa kế làm việc cho bất kỳ lớp khác trong Python - vì vậy "không có hành vi đặc biệt" ở đây:
Vì vậy, nhận ra sự khác biệt:
class M1(type):
def __new__(metacls, name, bases, attrs):
cls = type.__new__(metacls, name, bases, attrs)
# cls now is an instance of "M1"
...
return cls
class M2(type):
def __new__(metacls, name, bases, attrs):
cls = type(cls, name, bases, attrs)
# Type does not "know" it was called from within "M2"
# cls is an ordinary instance of "type"
...
return cls
Nó có thể được nhìn thấy trong cửa sổ tương tác:
In [13]: class M2(type):
....: def __new__(metacls, name, bases, attrs):
....: return type(name, bases, attrs)
....:
In [14]: class A(M2): pass
In [15]: type(A)
Out[15]: type
In [16]: class A(M2): __metaclass__ = M2
In [17]: A.__class__, A.__metaclass__
Out[17]: (type, __main__.M2)
(Lưu ý rằng metaclass __new__
phương pháp tham số đầu tiên là metaclass bản thân, do đó đặt tên đúng hơn metacls
hơn cls
như trong mã của bạn, và tôi rất nhiều mã "trong tự nhiên")
Tôi nghĩ rằng nó đã làm với loại .__ new__ và loại http://stackoverflow.com/questions/2608708/what-is-the-difference-between-type-and -loại-mới-trong-python –