2009-12-03 27 views
35

Tôi luôn thiết lập metaclasses một cái gì đó như thế này:Có lý do nào để chọn __new__ trên __init__ khi xác định metaclass không?

class SomeMetaClass(type): 
    def __new__(cls, name, bases, dict): 
     #do stuff here 

Nhưng tôi chỉ tình cờ gặp một metaclass mà được định nghĩa như thế này:

class SomeMetaClass(type): 
    def __init__(self, name, bases, dict): 
     #do stuff here 

Có lý do nào để thích một trong khác ?

Cập nhật: Lưu ý rằng tôi hỏi về cách sử dụng __new____init__ trong metaclass. Tôi đã hiểu sự khác biệt giữa chúng trong lớp khác. Nhưng trong một metaclass, tôi không thể sử dụng __new__ để thực hiện bộ nhớ đệm vì __new__ chỉ được gọi khi tạo lớp trong một metaclass.

Trả lời

40

Nếu bạn muốn thay đổi thuộc tính dict trước khi lớp được tạo hoặc thay đổi bộ đệm cơ sở, bạn phải sử dụng __new__. Vào thời điểm __init__ xem các đối số, đối tượng lớp đã tồn tại. Ngoài ra, bạn phải sử dụng __new__ nếu bạn muốn trả về một cái gì đó khác với lớp mới được tạo của loại được đề cập.

Mặt khác, vào lúc __init__ chạy, lớp học không tồn tại. Vì vậy, bạn có thể làm những việc như đưa ra một tham chiếu đến lớp vừa tạo ra cho một trong các đối tượng thành viên của nó.

Chỉnh sửa: thay đổi từ ngữ để làm cho nó rõ ràng hơn rằng theo "đối tượng", tôi có nghĩa là đối tượng lớp.

+0

Rõ ràng hơn nhiều. Và điều đó có ý nghĩa. Có lý do nào để sử dụng '__init__' không? –

+5

'__new__' có thể làm mọi thứ mà' __init__' có thể làm, nhưng không phải là cách khác. '__init__' có thể thuận tiện hơn khi sử dụng, giống như cách' __new__' và '__init__' liên quan đến việc khởi tạo lớp thông thường. Bạn phải nhớ trả về một cái gì đó từ '__new__' chẳng hạn, nhưng với' __init__' bạn thì không. –

+0

@JasonBaker Tôi nhận ra đây là một chủ đề cũ 5 năm và tôi không mong đợi để nhận được bất kỳ trả lời. Bạn sẽ không muốn sử dụng __init__ bất cứ khi nào bạn cần để tạo ra một instantiation của lớp trong câu hỏi. Giống như [mô hình trọng lượng] (http://en.wikipedia.org/wiki/Flyweight_pattern). –

1

Bạn có thể triển khai bộ nhớ đệm. Person("Jack") luôn trả về một đối tượng mới trong ví dụ thứ hai trong khi bạn có thể tra cứu một cá thể hiện có trong ví dụ đầu tiên với __new__ (hoặc không trả về bất kỳ thứ gì nếu bạn muốn).

3

Bạn có thể xem toàn bộ writeup trong the official docs, nhưng về cơ bản, __new__ được gọi trước đối tượng mới được tạo (đối với mục đích của việc tạo ra nó) và __init__ được gọi sau đối tượng mới được tạo (đối với mục đích khởi tạo nó).

Sử dụng các đối tượng giống như bộ nhớ đệm đối tượng . Nói chung, trừ khi bạn đang làm một cái gì đó khá kỳ quặc, __new__ là tiện ích hạn chế. Nếu bạn không cần phải gọi thủ đoạn như vậy, hãy gắn bó với __init__.

+0

Tôi hiểu sự khác biệt giữa '__new__' và' __init__' cho một lớp thông thường. Tôi đã hỏi tại sao tôi muốn sử dụng chúng cho một metaclass. –

+0

Ah. Đừng bận tâm. ^.^ –

+0

@JasonBaker Cơ chế này giống hệt nhau. Lớp học là đối tượng của metaclasses của họ, do đó, sáng tạo của họ hoạt động theo cùng một cách. – glglgl

1

Như đã nói, nếu bạn có ý định thay đổi thứ gì đó như lớp cơ sở hoặc thuộc tính, bạn sẽ phải làm điều đó trong __new__. Điều này cũng đúng với số name của lớp nhưng dường như có một sự khác biệt với nó. Khi bạn thay đổi name, nó không được truyền sang __init__, mặc dù, ví dụ: attr là.

Vì vậy, bạn sẽ có:

class Meta(type): 
    def __new__(cls, name, bases, attr): 
     name = "A_class_named_" + name 
     return type.__new__(cls, name, bases, attr) 

    def __init__(cls, name, bases, attr): 
     print "I am still called '" + name + "' in init" 
     return super(Meta, cls).__init__(name, bases, attr) 

class A(object): 
    __metaclass__ = Meta 

print "Now I'm", A.__name__ 

in

I am still called 'A' in init 
Now I'm A_class_named_A 

này rất quan trọng để biết, nếu __init__ gọi một metaclass siêu mà hiện một số phép thuật bổ sung. Trong trường hợp đó, người ta phải đổi tên lần nữa trước khi gọi số super.__init__.

+1

'__init__' được gọi với tham số' name' được đặt thành tên gốc, vì vậy với các đối số giống như '__new__'. Nhưng 'cls .__ name__' phải là cái mới vào lúc đó. – glglgl

+0

Tức là, với 'in 'tôi vẫn được gọi là" "+ name +"' trong init nhưng có '' + cls .__ name__ + "'" 'bạn nhận được' Tôi vẫn được gọi là' A 'trong init nhưng có' A_class_named_A ' '. – glglgl

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