2013-04-06 8 views
5

liên quan: Python object conversionPython: có trường hợp sử dụng nào để thay đổi lớp của cá thể không?

Gần đây tôi đã học được rằng Python cho phép bạn thay đổi lớp của một ví dụ như vậy:

class Robe: 
    pass 

class Dress: 
    pass 

r = Robe() 
r.__class__ = Dress 

Tôi đang cố gắng tìm hiểu xem có một trường hợp 'transmuting' một đối tượng như điều này có thể hữu ích. Tôi đã rối tung xung quanh với điều này trong IDLE, và một điều tôi đã nhận thấy là gán một lớp khác nhau không gọi phương thức __init__ của lớp mới, mặc dù điều này có thể được thực hiện một cách rõ ràng nếu cần.

Hầu như mọi trường hợp sử dụng mà tôi có thể nghĩ là sẽ được phục vụ tốt hơn theo sáng tác, nhưng tôi là một newb mã hóa nên tôi biết điều gì. ;)

Trả lời

6

Ít khi có lý do chính đáng để làm điều này cho các lớp không liên quan, như RobeDress trong ví dụ của bạn. Nếu không có một chút công việc, thật khó để đảm bảo rằng đối tượng bạn nhận được cuối cùng là ở trạng thái sane.

Tuy nhiên, nó có thể hữu ích khi kế thừa từ một lớp cơ sở, nếu bạn muốn sử dụng một hàm nhà máy không chuẩn hoặc hàm tạo để xây dựng đối tượng cơ sở. Dưới đây là một ví dụ:

class Base(object): 
    pass 

def base_factory(): 
    return Base() # in real code, this would probably be something opaque 

def Derived(Base): 
    def __new__(cls): 
     self = base_factory()  # get an instance of Base 
     self.__class__ = Derived # and turn it into an instance of Derived 
     return self 

Trong ví dụ này, phương pháp __new__ lớp Derived của muốn xây dựng đối tượng của nó bằng cách sử dụng phương pháp base_factory mà trả về một thể hiện của lớp Base. Thông thường, loại nhà máy này nằm trong thư viện ở đâu đó và bạn không thể biết chắc chắn cách thức tạo đối tượng (bạn không thể chỉ gọi số Base() hoặc mình để có cùng kết quả).

__class__ thuộc tính của dụ được viết lại để các kết quả của gọi Derived.__new__ sẽ là một thể hiện của lớp Derived, đảm bảo rằng nó sẽ có phương pháp Derived.__init__ gọi vào nó (nếu một phương pháp như vậy tồn tại).

+0

Câu trả lời này có vẻ rất kỹ lưỡng, nhưng tôi mới lập trình và do đó đấu tranh để hiểu nó. Bỏ qua khía cạnh '__new__'/constructor, tôi có thể thấy rằng nếu bạn có một lớp được định nghĩa trong thư viện ở đâu đó, và bạn đã tạo lớp của riêng bạn thừa kế nó, thì bạn có thể tạo một thể hiện của lớp sau bằng cách tạo một thể hiện của trước đây và sau đó 'chuyển đổi'. Tuy nhiên, tại sao không đơn giản là khởi tạo lớp thừa hưởng? Tôi thừa nhận tôi không hiểu các khái niệm về các hàm tạo hoặc hàm của phương thức '__new__'. – henrebotha

+1

@henrebotha: Tôi khuyên bạn nên đọc về [mô hình dữ liệu Python] (http://docs.python.org/3/reference/datamodel.html#basic-customization). Phương thức '__new__' của lớp được gọi để xây dựng các thể hiện của nó. Gọi 'Derived()' sẽ kích hoạt một cuộc gọi đến 'Derived .__ new__' nếu nó tồn tại. Thông thường '__new__' trả về một đối tượng mới được tạo bởi' object '__ new__' (thông qua 'super' calls), nhưng nó không phải làm điều đó. Trong ví dụ trên, chúng tôi đã nhận được cá thể từ một chức năng của nhà máy, sau đó tùy chỉnh nó. Xem thêm [câu hỏi trước này] (http://stackoverflow.com/questions/15330755/construct-class-from-superclass-instance). – Blckknght

2

Tôi nhớ sử dụng kỹ thuật này từ lâu để "nâng cấp" các đối tượng hiện có sau khi nhận ra loại dữ liệu nào họ nắm giữ. Nó là một phần của ứng dụng XMPP thử nghiệm. XMPP sử dụng nhiều thông điệp XML ngắn (“stanzas”) để liên lạc.

Khi ứng dụng nhận được một đoạn văn bản, nó được phân tích thành một cây DOM. Sau đó, các ứng dụng cần thiết để nhận ra loại stanza đó là (một sự hiện diện stanza, tin nhắn, truy vấn tự động, vv). Ví dụ, nếu nó được nhận dạng như một đoạn tin nhắn, đối tượng DOM đã được “nâng cấp” lên một lớp con cung cấp các phương thức như “get_author”, “get_body”, v.v.

Tôi có thể tạo một lớp mới đại diện cho một thông điệp được phân tích cú pháp, tạo đối tượng mới của lớp đó và sao chép dữ liệu có liên quan từ đối tượng XML DOM ban đầu. Tuy nhiên, có hai lợi ích của việc thay đổi lớp của đối tượng tại chỗ. Thứ nhất, XMPP là một tiêu chuẩn rất mở rộng, và nó rất hữu ích để vẫn có thể truy cập dễ dàng vào đối tượng DOM ban đầu trong trường hợp một số phần khác của mã tìm thấy một cái gì đó hữu ích ở đó, hoặc trong khi gỡ lỗi. Thứ hai, việc viết mã cho tôi biết rằng việc tạo một đối tượng mới và sao chép dữ liệu rõ ràng chậm hơn nhiều so với việc chỉ sử dụng lại đối tượng sẽ bị phá hủy nhanh chóng — sự khác biệt là đủ để quan trọng trong XMPP, sử dụng nhiều thông điệp ngắn.

Tôi không nghĩ rằng bất kỳ lý do nào trong số này giải thích việc sử dụng kỹ thuật này trong mã sản xuất, trừ khi có thể bạn thực sự cần tốc độ (không lớn) trong CPython. Nó chỉ là một hack mà tôi thấy hữu ích để làm cho mã một chút ngắn hơn và nhanh hơn trong các ứng dụng thử nghiệm.Cũng lưu ý rằng kỹ thuật này sẽ dễ dàng phá vỡ các công cụ JIT trong việc triển khai không CPython, làm cho mã chậm hơn nhiều!

+0

Điểm thú vị ở đó về trình biên dịch JIT. – henrebotha