2013-02-22 35 views
11

Khi nào và làm thế nào các phương pháp tĩnh giả sử được sử dụng trong python? Chúng ta đã thiết lập sử dụng một phương thức lớp làm phương thức factory để tạo một thể hiện của một đối tượng cần tránh khi có thể. Nói cách khác, cách tốt nhất là không nên sử dụng các phương thức lớp như một phương thức khởi tạo thay thế (xem Factory method for python object - best practice).Sử dụng các phương pháp tĩnh trong python - thực hành tốt nhất

Cho phép nói rằng tôi có một lớp được sử dụng để đại diện cho một số dữ liệu thực thể trong cơ sở dữ liệu. Hãy tưởng tượng dữ liệu là một đối tượng dict có chứa tên trường và giá trị trường và một trong các trường là số ID làm cho dữ liệu duy nhất.

class Entity(object): 
    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 

Ở đây phương pháp __init__ tôi mất dữ liệu đối tượng dict đối tượng. Cho phép nói rằng tôi chỉ có một số ID và tôi muốn tạo ra một ví dụ Entity. Trước tiên, tôi sẽ cần phải tìm phần còn lại của dữ liệu, sau đó tạo một thể hiện của đối tượng Entity của tôi. Từ câu hỏi trước của tôi, chúng tôi đã thiết lập rằng việc sử dụng phương pháp lớp học làm phương pháp nhà máy có lẽ nên tránh khi có thể.

class Entity(object): 

    @classmethod 
    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return cls(data, db_connection) 

    def __init__(self, data, db_connection): 
     self._data = data 
     self._db_connection 


# Create entity 
entity = Entity.from_id(id_number, db_connection) 

Trên đây là ví dụ về những việc không nên làm hoặc ít nhất là không nên làm gì nếu có giải pháp thay thế. Bây giờ tôi tự hỏi nếu chỉnh sửa phương pháp lớp của tôi để nó là nhiều hơn một phương pháp tiện ích và ít hơn của một phương pháp nhà máy là một giải pháp hợp lệ. Nói cách khác, ví dụ sau có tuân thủ phương pháp hay nhất để sử dụng các phương pháp tĩnh hay không.

class Entity(object): 

    @staticmethod 
    def data_from_id(id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     data = db_connection.find(filters) 
     return data 


# Create entity 
data = Entity.data_from_id(id_number, db_connection) 
entity = Entity(data) 

Hoặc sử dụng chức năng độc lập có ý nghĩa hơn để tìm dữ liệu thực thể từ số ID.

def find_data_from_id(id_number, db_connection): 
    filters = [['id', 'is', id_number]] 
    data = db_connection.find(filters) 
    return data 


# Create entity. 
data = find_data_from_id(id_number, db_connection) 
entity = Entity(data, db_connection) 

Lưu ý: Tôi không muốn thay đổi phương pháp __init__ của mình. Trước đây mọi người đã đề xuất làm cho phương pháp __init__ của tôi trông giống như thế này __init__(self, data=None, id_number=None) nhưng có thể có 101 cách khác nhau để tìm dữ liệu thực thể vì vậy tôi muốn giữ logic đó tách biệt ở một mức độ nào đó. Có lý?

+0

Tên của câu hỏi này và câu giới thiệu làm cho âm thanh như bạn đang đào vì lý do sử dụng các tính năng ngôn ngữ, thay vì cố gắng tìm ra đúng cách để viết mã bạn muốn. Tôi không nói đó là những gì bạn đang làm (cơ thể thực sự của câu hỏi của bạn chắc chắn là một câu hỏi thực sự, hợp lệ). Chỉ cần bạn có thể nghĩ một chút về cách nói của bạn. Bằng cách này, bạn chỉ có thể nhận được câu trả lời từ những người thích tranh luận về các tính năng ngôn ngữ, chứ không phải là nhóm người viết nhiều Python tốt hơn nhiều. – abarnert

Trả lời

6

Ví dụ đầu tiên của bạn có ý nghĩa nhất đối với tôi: Entity.from_id khá ngắn gọn và rõ ràng.

Nó tránh sử dụng data trong hai ví dụ tiếp theo, không mô tả những gì đang được trả lại; các data được sử dụng để xây dựng một Entity. Nếu bạn muốn cụ thể về số data đang được sử dụng để xây dựng Entity, thì bạn có thể đặt tên cho phương thức của mình là Entity.with_data_for_id hoặc hàm tương đương entity_with_data_for_id.

Sử dụng động từ như find cũng có thể khá khó hiểu, vì nó không đưa ra bất kỳ dấu hiệu nào về giá trị trả về - chức năng được phép làm khi tìm thấy dữ liệu là gì? (Vâng, tôi nhận ra str có một phương pháp find; nó sẽ không được đặt tên tốt hơn index_of Nhưng sau đó cũng là index ...?) Nó làm tôi nhớ đến cổ điển:

find x

tôi luôn cố gắng để suy nghĩ những gì một tên sẽ chỉ ra cho ai đó với (a) không có kiến ​​thức về hệ thống, và (b) kiến ​​thức về các phần khác của hệ thống - không phải để nói rằng tôi luôn thành công!

+1

Điểm hay về "tìm". Nó giống như cuộc phiêu lưu văn bản Scott Adams cũ, ở đâu, bởi vì câu đố chính là tìm ra những danh từ để cung cấp cho động từ "sử dụng" tất cả các mục đích. (Trong một trò chơi Infocom, bạn sẽ "Lấy sợi dây thừng từ ống chỉ, buộc nó dây vào móc, sau đó trượt xuống sợi dây thừng"; trong một trò chơi SA, bạn muốn "SỬ DỤNG SPOOL. SỬ DỤNG HOOK. SỬ DỤNG ROPE.") – abarnert

10

Câu trả lời cho câu hỏi liên quan đặc biệt nói điều nà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, v.v.

Vì vậy, tôi không biết làm thế nào bạn có ý tưởng rằng việc sử dụng một classmethod vốn dĩ tệ. Tôi thực sự thích ý tưởng của việc sử dụng một classmethod trong tình hình cụ thể của bạn, vì nó làm cho sau mã và sử dụng api dễ dàng.

Việc thay thế sẽ được sử dụng đối số constructor mặc định như vậy:

class Entity(object): 
    def __init__(self, id, db_connection, data=None): 
     self.id = id 
     self.db_connection = db_connection 
     if data is None: 
      self.data = self.from_id(id, db_connection) 
     else: 
      self.data = data 

    def from_id(cls, id_number, db_connection): 
     filters = [['id', 'is', id_number]] 
     return db_connection.find(filters) 

Tôi thích phiên bản classmethod mà bạn đã viết ban đầu tuy nhiên. Đặc biệt là từ data là khá mơ hồ.

16

Phương pháp tĩnh và giả sử được sử dụng trong python ở đâu và như thế nào?

Câu trả lời glib là: Không thường xuyên.

Câu trả lời thậm chí không phải là câu trả lời vô dụng là: Khi họ làm cho mã của bạn dễ đọc hơn.


Đầu tiên, chúng ta hãy đi đường vòng để the docs:

phương pháp tĩnh trong Python tương tự như tìm thấy trong Java hay C++. Ngoài ra, hãy xem classmethod() cho một biến thể hữu ích cho việc tạo các hàm tạo lớp thay thế.

Vì vậy, khi bạn cần một phương pháp tĩnh trong C++, bạn cần một phương thức tĩnh bằng Python, phải không?

Ồ, không.

Trong Java, không có hàm, chỉ là các phương thức, vì vậy bạn kết thúc việc tạo các lớp giả chỉ là các bó của các phương thức tĩnh. Cách để làm điều tương tự trong Python là chỉ cần sử dụng các chức năng miễn phí.

Điều đó khá rõ ràng. Tuy nhiên, nó là một phong cách Java tốt để xem xét một lớp thích hợp để nêm một hàm vào, vì vậy bạn có thể tránh viết các lớp giả, trong khi làm điều tương tự là kiểu Python xấu — một lần nữa, sử dụng các hàm miễn phí — và ít rõ ràng hơn nhiều.

C++ không có giới hạn giống như Java, nhưng nhiều kiểu C++ thì khá giống nhau. (Mặt khác, nếu bạn là một lập trình viên "Modern C++", người đã "nội bộ hóa" các chức năng miễn phí là một phần của giao diện "thành ngữ", bản năng của bạn cho "phương pháp tĩnh hữu ích" có lẽ là khá tốt cho Python.)


Nhưng nếu bạn đang đến lúc này từ nguyên tắc đầu tiên, chứ không phải từ một ngôn ngữ khác, có một cách đơn giản hơn để nhìn vào điều:

một @staticmethod là về cơ bản chỉ là một chức năng toàn cầu.Nếu bạn có hàm foo_module.bar() thì sẽ dễ đọc hơn vì một số lý do nếu nó được viết là foo_module.BazClass.bar(), hãy đặt số @staticmethod. Nếu không, đừng. Đó thực sự là tất cả để có nó. Vấn đề duy nhất là xây dựng bản năng của bạn cho những gì dễ đọc hơn đối với một lập trình viên lập trình Python.

Và tất nhiên sử dụng @classmethod khi bạn cần quyền truy cập vào lớp học, nhưng không phải là cá thể — nhà xây dựng thay thế là trường hợp mô hình cho điều đó, như tài liệu ngụ ý. Mặc dù bạn thường xuyên có thể mô phỏng một @classmethod với một @staticmethod chỉ bằng cách tham chiếu rõ ràng lớp (đặc biệt là khi bạn không có nhiều phân lớp), bạn không nên.


Cuối cùng, nhận được cho câu hỏi cụ thể của bạn:

Nếu lý do duy nhất cho khách hàng bao giờ cần phải tìm kiếm dữ liệu bằng ID là xây dựng một Entity, mà âm thanh như một chi tiết thực hiện bạn không nên phơi bày và nó cũng làm cho mã khách hàng trở nên phức tạp hơn. Chỉ cần sử dụng một hàm tạo. Nếu bạn không muốn sửa đổi __init__ của mình (và bạn có lý do chính đáng mà bạn có thể không muốn), hãy sử dụng @classmethod làm phương thức khởi tạo thay thế: Entity.from_id(id_number, db_connection). Mặt khác, nếu tra cứu đó là thứ hữu dụng cho khách hàng trong các trường hợp khác không liên quan gì đến việc xây dựng Entity, có vẻ như điều này không liên quan gì đến lớp Entity (hoặc ít nhất là không quá bất cứ điều gì khác trong cùng một mô-đun). Vì vậy, chỉ cần làm cho nó một chức năng miễn phí.

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