2010-07-09 38 views
13

Tôi đang gọi một hàm tạo trong ClassA và muốn có đối tượng kết quả là một lớp khác (ClassB) nếu một điều kiện nhất định được đáp ứng. Tôi đã thử thay thế các đối số đầu tiên để __init __() ('tự' trong ví dụ dưới đây) trong vòng __init __() nhưng nó dường như không làm những gì tôi muốn.Làm thế nào để thay thế một thể hiện trong __init __() bằng một đối tượng khác?

trong chính:

import ClassA 

my_obj = ClassA.ClassA(500) 
# unfortunately, my_obj is a ClassA, but I want a ClassB! 

trong ClassA/__ init__.py:

import ClassB 

class ClassA: 
    def __init__(self,theirnumber): 
     if(theirnumber > 10): 
      # all big numbers should be ClassB objects: 
      self = ClassB.ClassB(theirnumber) 
      return 
     else: 
      # numbers under 10 are ok in ClassA. 
      return 

trong ClassB/__ init__.py:

class ClassB: 
    pass 

Trả lời

27

Bạn cần __new__() cho điều đó. (Và bạn cũng cần phải làm cho nó một lớp kiểu mới, giả sử bạn đang sử dụng Python 2, bởi subclassing object.)

class ClassA(object): 
    def __new__(cls,theirnumber): 
     if theirnumber > 10: 
      # all big numbers should be ClassB objects: 
      return ClassB.ClassB(theirnumber) 
     else: 
      # numbers under 10 are ok in ClassA. 
      return super(ClassA, cls).__new__(cls, theirnumber) 

__new__() chạy như một phần của quá trình lớp instantiation trước __init__(). Về cơ bản, __new__() là những gì thực sự tạo ra cá thể mới, và __init__() sau đó được gọi để khởi tạo các thuộc tính của nó. Đó là lý do tại sao bạn có thể sử dụng __new__() nhưng không phải __init__() để thay đổi loại đối tượng đã tạo: sau khi __init__() bắt đầu, đối tượng đã được tạo và đã quá muộn để thay đổi loại đối tượng. (Ừm ... không thực sự, nhưng nó được đưa vào ma thuật đen rất phức tạp của Python.) Xem the documentation.

Trong trường hợp này, tuy nhiên, tôi muốn nói một chức năng nhà máy là thích hợp hơn, một cái gì đó giống như

def thingy(theirnumber): 
    if theirnumber > 10: 
     return ClassB.ClassB(theirnumber) 
    else: 
     return ClassA.ClassA(theirnumber) 

Bằng cách này, lưu ý rằng nếu bạn làm những gì tôi đã làm với __new__() trên, nếu một ClassB là trả lại, phương pháp __init__() của trường hợp ClassB sẽ không phải là được gọi! Python chỉ gọi __init__() nếu đối tượng được trả về từ __new__() là một thể hiện của lớp mà trong đó phương thức __new__() được chứa (ở đây ClassA). Đó là một lập luận khác có lợi cho phương pháp tiếp cận chức năng của nhà máy.

+0

Ok, bây giờ nếu tôi muốn hoán đổi đối tượng bằng đối tượng mới bên trong phương thức đối tượng, thay vì trong hàm tạo? Tôi muốn một vật thể biến thành vật thể khác trong vòng đời của nó. – sneak

+0

@sneak, bạn không thể: phương thức được 'tự' làm đối số và vì vậy bất cứ điều gì họ có thể rebind để nói tên mã nó sẽ không có bất kỳ tác dụng bên ngoài phương pháp. (Tất nhiên bạn có thể đổi tên ** các tên ** đủ điều kiện như 'self .__ class__' - tên và tên gọi đủ điều kiện hoàn toàn khác nhau về mọi khía cạnh bạn có thể nghĩ đến). –

+1

@ Alex, thực tế là bạn có thể gán cho 'self .__ class__' là một tai nạn thực hiện trong CPython; thông số python chính thức nói rằng điều này * không nên * hoạt động. Thậm chí nếu nó không phải là một tai nạn, đó là một điều khá khủng khiếp để làm. Trong ngắn hạn: xin vui lòng không, và đặc biệt là xin vui lòng không đề nghị nó cho người khác. – habnabit

11

Đừng cố gắng làm hỏng mục đích của các nhà xây dựng: sử dụng chức năng của nhà máy. Gọi một hàm tạo cho một lớp và được trả về một thể hiện của một lớp khác nhau là một cách chắc chắn để gây nhầm lẫn.

+5

Tôi nghĩ rằng tôi có thể quản lý để không bị bối rối, thx. – sneak

13

Tôi khuyên bạn nên sử dụng mẫu nhà máy cho việc này. Ví dụ:

def get_my_inst(the_number): 
    if the_number > 10: 
     return ClassB(the_number) 
    else: 
     return ClassA(the_number) 

class_b_inst = get_my_inst(500) 
class_a_inst = get_my_inst(5) 
+1

Đây là cách "đúng" để thực hiện, IMHO. – ThatAintWorking

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