2009-11-20 8 views
9

Đây là phiên python.Metaclass không được gọi trong các lớp con

>>> class Z(type): 
    def __new__(cls, name, bases, attrs): 
     print cls 
     print name 
     return type(name, bases, attrs) 
...  
>>> class Y(object): 
    __metaclass__ = Z 
...  
<class '__main__.Z'> 
Y 
>>> class X(Y): 
...  pass 
... 
>>> class W(Y): 
...  __metaclass__ = Z 
...  
<class '__main__.Z'> 
W 
>>> 

Sau khi tôi xác định lớp XI mong đợi Z._new__ được gọi cho nó, và để in các dòng hai, đó không xảy ra, (như metaclass được thừa kế?)

Trả lời

12

Vấn đề là rằng đối số cls (là đối tượng metaclass) không được truyền khi bạn gọi type, do đó đối tượng lớp Y được tạo và trả về không có tham chiếu đến metaclass Z.

Nếu bạn thay thế dòng cuối cùng trong __new__ với

return super(Z, cls).__new__(cls, name, bases, attrs) 

sau đó nó hoạt động. Lưu ý rằng mặc dù cls được sử dụng trong super chúng tôi vẫn phải cung cấp cls làm đối số, vì super ở đây trả về một phương thức không liên kết (xem here để biết thêm).

Là một thay thế cho việc sử dụng siêu người ta có thể sử dụng:

return type.__new__(cls, name, bases, attrs) 

Điều quan trọng là chúng ta cung cấp cho cls (object metaclass của chúng tôi Z) đến classmethod __new__. Mẫu ngắn hơn type(name, bases, attrs) điền vào số type chính nó cho đối số cls, tất nhiên là sai. Lỗi này tương tự như gọi một phương thức thể hiện với đối số sai số self.

Tôi thích sử dụng super vì đây là kiểu tốt hơn.

+2

Ah, ok, công việc đó. Nhưng không nên 'trả về super (Z, cls) .__ new__' tương đương với' type .__ class __. New' whis tương đương với 'type .__ new__' nên giống như tạo một class mới thông qua' type'? – agiliq

+2

Nó thực sự là không giống nhau, bây giờ tôi giải quyết điều này trong câu trả lời của tôi. Super không gọi phương thức 'type .__ new__', nhưng sau đó chúng ta có thể sử dụng đối số' cls' đúng, điều này là không thể nếu chúng ta gọi trực tiếp 'type'. – nikow