2013-02-21 30 views
22

Đây là câu hỏi về cách thực hành tốt nhất để tạo một thể hiện của một lớp hoặc kiểu từ các dạng khác nhau của cùng một dữ liệu bằng cách sử dụng python. Tốt hơn là sử dụng một phương thức lớp hay tốt hơn là sử dụng một hàm riêng biệt hoàn toàn? Cho phép nói rằng tôi có một lớp được sử dụng để mô tả kích thước của một tài liệu. (Lưu ý: Đây chỉ đơn giản là một ví dụ tôi muốn biết cách tốt nhất để tạo ra một thể hiện của lớp không phải là cách tốt nhất để mô tả kích thước của một tài liệu..)Phương pháp nhà máy cho đối tượng python - thực hành tốt nhất

class Size(object): 
    """ 
    Utility object used to describe the size of a document. 
    """ 

    BYTE = 8 
    KILO = 1024 

    def __init__(self, bits): 
     self._bits = bits 

    @property 
    def bits(self): 
     return float(self._bits) 

    @property 
    def bytes(self): 
     return self.bits/self.BYTE 

    @property 
    def kilobits(self): 
     return self.bits/self.KILO 

    @property 
    def kilobytes(self): 
     return self.bytes/self.KILO 

    @property 
    def megabits(self): 
     return self.kilobits/self.KILO 

    @property 
    def megabytes(self): 
     return self.kilobytes/self.KILO 

phương pháp __init__ tôi có một giá trị kích thước đại diện bằng bit (bit và chỉ bit và tôi muốn giữ nó theo cách đó) nhưng cho phép nói rằng tôi có một giá trị kích thước bằng byte và tôi muốn tạo một thể hiện của lớp của tôi. Tốt hơn là sử dụng một phương thức lớp hay tốt hơn là sử dụng một hàm riêng biệt hoàn toàn?

class Size(object): 
    """ 
    Utility object used to describe the size of a document. 
    """ 

    BYTE = 8 
    KILO = 1024 

    @classmethod 
    def from_bytes(cls, bytes): 
     bits = bytes * cls.BYTE 
     return cls(bits) 

HOẶC

def create_instance_from_bytes(bytes): 
    bits = bytes * Size.BYTE 
    return Size(bits) 

này không có vẻ như một vấn đề và có lẽ cả hai ví dụ có giá trị nhưng tôi nghĩ về nó mỗi khi tôi cần phải thực hiện một cái gì đó như thế này. Trong một thời gian dài, tôi đã ưa thích cách tiếp cận phương pháp lớp vì tôi thích các lợi ích của tổ chức khi kết hợp lớp và phương thức factory với nhau. Ngoài ra, bằng cách sử dụng một phương thức lớp bảo toàn khả năng tạo ra các cá thể của bất kỳ lớp con nào, do đó nó được định hướng nhiều đối tượng hơn. Mặt khác, một người bạn đã từng nói "Khi nghi ngờ, hãy làm những gì thư viện chuẩn làm" và tôi chưa tìm thấy một ví dụ về điều này trong thư viện chuẩn.

Mọi phản hồi được đánh giá cao.

Cheers

+0

Tôi muốn lật một đồng xu. Hầu hết các thư viện python dường như thích các API thủ tục hơn, nhưng đó là vì chúng có nghĩa là được tiêu thụ theo một cách khác với mã có thể tái sử dụng bên trong mã nguồn của bạn. – millimoose

+4

PS, không gọi biến 'byte'; đó là loại nội trang (trong phiên bản 2.6 trở lên). – abarnert

+3

Và tất nhiên tôi chỉ nhận ra rằng tôi đã phạm sai lầm tương tự trong câu trả lời của tôi: 'Kích thước (byte = 20)'. Đừng làm như tôi làm, làm như tôi nói. :) – abarnert

Trả lời

22

Thứ nhất, phần lớn thời gian bạn nghĩ rằng bạn cần một cái gì đó như thế này, bạn không; đó là một dấu hiệu cho thấy bạn đang cố gắng đối xử với Python như Java, và giải pháp là lùi lại và hỏi tại sao bạn cần một nhà máy.

Thông thường, điều đơn giản nhất là chỉ cần có một hàm tạo với các đối số mặc định/tùy chọn/từ khóa. Ngay cả trường hợp bạn không bao giờ viết theo cách đó trong Java - ngay cả trường hợp các nhà xây dựng quá tải sẽ cảm thấy sai trong C++ hoặc ObjC — có thể trông hoàn toàn tự nhiên trong Python. Ví dụ: size = Size(bytes=20) hoặc size = Size(20, Size.BYTES) có vẻ hợp lý. Đối với vấn đề đó, một lớp học Bytes(20) kế thừa từ Size và hoàn toàn không thêm gì ngoài việc quá tải __init__ có vẻ hợp lý. Và đây là tầm thường để xác định:

def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None): 

Hoặc:

BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object() 
def __init__(self, count, unit=Size.BITS): 

Nhưng, đôi khi bạn làm chức năng cần nhà máy. Vì vậy, bạn làm gì sau đó? Vâng, có hai loại thứ thường được gộp lại với nhau thành "nhà máy".

Một @classmethod là cách thành ngữ để làm một "nhà xây dựng thay thế" -Có là những ví dụ trên tất cả các stdlib- itertools.chain.from_iterable, datetime.datetime.fromordinal vv

Một chức năng là cách thành ngữ để làm một "Tôi không quan tâm những gì lớp thực tế là "nhà máy. Hãy xem, ví dụ: chức năng được xây dựng trong open. Bạn có biết những gì nó trả về trong 3.3? Bạn có quan tâm không? Không. Đó là lý do tại sao nó là một chức năng, không phải là io.TextIOWrapper.open hoặc bất cứ điều gì.

Ví dụ cụ thể của bạn có vẻ giống như một trường hợp sử dụng hoàn toàn hợp pháp và phù hợp với thùng "constructor thay thế" (nếu nó không vừa với thùng "constructor with extra argument" bin).

+2

Trong khi tôi đồng ý với điều này - nó chỉ công bằng để chỉ ra rằng một lựa chọn thiết kế khác thay vì một @classmethod - là làm cho '__init __ (kilobytes = 345)' ... –

+0

@JonClements: Điểm tốt - mặc dù tôi nghĩ rằng thực sự phù hợp với câu đầu tiên, chắc chắn không rõ ràng bằng văn bản, vì vậy tôi sẽ chỉnh sửa nó. – abarnert

+0

Yup - những gì tôi đã suy nghĩ cho trường hợp sử dụng này: http://dpaste.com/958194/ (ngoại trừ việc nó phải là n * base * cough *) –

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