2010-06-16 25 views
72

Tôi đang viết một lớp hạng nhẹ có các thuộc tính được dự định có thể truy cập công khai và đôi khi chỉ được ghi đè trong các cảnh báo cụ thể. Không có điều khoản nào trong ngôn ngữ Python để tạo tài liệu cho các thuộc tính lớp hoặc bất kỳ loại thuộc tính nào, cho vấn đề đó. Cách được chấp nhận là gì, nên có một, để ghi lại các thuộc tính này? Hiện nay tôi đang làm việc này:Làm cách nào để ghi lại các thuộc tính lớp trong Python?

class Albatross(object): 
    """A bird with a flight speed exceeding that of an unladen swallow. 

    Attributes: 
    """ 

    flight_speed = 691 
    __doc__ += """ 
     flight_speed (691) 
      The maximum speed that such a bird can attain. 
    """ 

    nesting_grounds = "Raymond Luxury-Yacht" 
    __doc__ += """ 
     nesting_grounds ("Raymond Luxury-Yacht") 
      The locale where these birds congregate to reproduce. 
    """ 

    def __init__(self, **keyargs): 
     """Initialize the Albatross from the keyword arguments.""" 
     self.__dict__.update(keyargs) 

này sẽ dẫn đến docstring của lớp chứa phần tiêu chuẩn docstring ban đầu, cũng như các dòng thêm cho mỗi thuộc tính thông qua nhiệm vụ tăng cường để __doc__.

Mặc dù kiểu này dường như không bị cấm rõ ràng trong số docstring style guidelines, cũng không được đề cập dưới dạng tùy chọn. Lợi thế ở đây là nó cung cấp một cách để ghi lại các thuộc tính cùng với các định nghĩa của chúng, trong khi vẫn tạo ra một lớp docstring có thể trình bày, và tránh phải viết các chú thích để nhắc lại thông tin từ chuỗi tài liệu. Tôi vẫn cảm thấy khó chịu khi tôi phải viết các thuộc tính hai lần; Tôi đang xem xét việc sử dụng các biểu diễn chuỗi của các giá trị trong docstring để ít nhất tránh trùng lặp các giá trị mặc định.

Đây có phải là sự vi phạm nghiêm trọng các quy ước cộng đồng đặc biệt không? Như thế có ổn không? Có cách nào tốt hơn? Ví dụ, có thể tạo một từ điển có chứa các giá trị và tài liệu cho các thuộc tính và sau đó thêm các nội dung vào lớp __dict__ và docstring vào cuối khai báo lớp; điều này sẽ làm giảm bớt nhu cầu nhập tên và giá trị thuộc tính hai lần. chỉnh sửa: ý tưởng cuối cùng này là, tôi nghĩ, không thực sự có thể, ít nhất là không tự động xây dựng toàn bộ lớp từ dữ liệu, điều này có vẻ như là một ý tưởng tồi nếu không có lý do khác để làm điều đó.

Tôi khá mới mẻ với python và vẫn đang nghiên cứu chi tiết về kiểu mã hóa, vì vậy những lời phê bình không liên quan cũng được hoan nghênh.

+0

Nếu bạn đang tìm cách ghi lại thuộc tính mô hình Django, điều này có thể hữu ích: https://djangosnippets.org/snippets/2533/ –

+0

Sao chép [Làm cách nào để tài liệu các trường và thuộc tính bằng Python?] (Http : //stackoverflow.com/questions/6060813/how-to-document-fields-and-properties-in-python) giữ một giải pháp khác. – bufh

Trả lời

50

Để tránh nhầm lẫn: cụm từ thuộc tínhspecific meaning trong python. Những gì bạn đang nói đến là những gì chúng tôi gọi là class attributes. Vì chúng luôn luôn được thực hiện thông qua lớp học của chúng, tôi thấy rằng nó có ý nghĩa để ghi lại chúng trong chuỗi tài liệu của lớp. Một cái gì đó như thế này:

class Albatross(object): 
    """A bird with a flight speed exceeding that of an unladen swallow. 

    Attributes: 
     flight_speed  The maximum speed that such a bird can attain. 
     nesting_grounds The locale where these birds congregate to reproduce. 
    """ 
    flight_speed = 691 
    nesting_grounds = "Throatwarbler Man Grove" 

Tôi nghĩ điều đó dễ dàng hơn nhiều so với cách tiếp cận trong ví dụ của bạn. Nếu tôi thực sự muốn một bản sao của các giá trị thuộc tính xuất hiện trong chuỗi tài liệu, tôi sẽ đặt chúng bên cạnh hoặc bên dưới mô tả của từng thuộc tính.

Edit:

Hãy ghi nhớ rằng trong Python, chuỗi doc là thành viên thực tế của các đối tượng họ tài liệu, chứ không phải chỉ đơn thuần là chú thích mã nguồn. Vì các biến thuộc tính lớp không phải là các đối tượng mà là các tham chiếu đến các đối tượng nên chúng không có cách nào giữ các chuỗi tài liệu của riêng chúng. Tôi đoán bạn có thể làm cho một trường hợp cho chuỗi tài liệu tham khảo, có lẽ để mô tả "những gì nên đi ở đây" thay vì "những gì thực sự ở đây", nhưng tôi tìm thấy nó dễ dàng, đủ để làm điều đó trong chuỗi chứa lớp doc.

+0

Tôi đoán trong hầu hết các trường hợp, điều này là tốt, vì các thuộc tính —điều chỉnh cho điều chỉnh thuật ngữ— được khai báo gọn gàng rằng chúng chỉ có thể được nhóm vào phần đầu của khai báo lớp mà không làm cho việc chuyển đổi trở lại và {read cả tài liệu và giá trị mặc định} hoặc {cập nhật cả hai phiên bản của tài liệu và/hoặc giá trị mặc định}. – intuited

+1

Cũng lưu ý rằng ví dụ của tôi sẽ khiến tài liệu cho các thuộc tính xuất hiện trong chuỗi tài liệu của lớp học. Tôi thực sự muốn đưa các tài liệu trong docstrings của các thuộc tính mình, nhưng điều này không làm việc cho hầu hết các loại nội trang. – intuited

+0

Vâng, ý tưởng ban đầu của tôi là chỉ tuyên bố, ví dụ: 'flight_speed = 691; flight_speed .__ doc__ = "blah blah" '. Tôi nghĩ rằng đây là những gì bạn đang đề cập trong ** chỉnh sửa của bạn **. Thật không may, điều này không làm việc cho instantiations (nhất?) Các loại nội trang (như 'int' trong ví dụ đó).Nó làm việc cho instantiations của người dùng định nghĩa các loại. =========== Thực ra có một PEP (xin lỗi, quên số) đề xuất thêm tài liệu cho thuộc tính lớp/mô-đun, nhưng nó đã bị từ chối bởi vì họ không thể tìm ra cách để làm rõ liệu các tài liệu có dành cho các thuộc tính trước hoặc sau không. – intuited

18

Bạn trích dẫn PEP257: ước docstring, trong phần What is a docstring nó được tuyên bố:

literals chuỗi xảy ra ở những nơi khác trong mã Python cũng có thể đóng vai trò như tài liệu hướng dẫn. Chúng không được trình biên dịch bytecode của Python nhận dạng và không thể truy cập được dưới dạng thuộc tính đối tượng thời gian chạy (tức làkhông được gán cho __doc__), nhưng hai loại tài liệu bổ sung có thể được trích xuất bằng các công cụ phần mềm:

Các chuỗi ký tự xảy ra ngay sau khi gán đơn giản ở cấp cao nhất của mô-đun, lớp hoặc phương thức __init__ được gọi là "docstrings attribute" .

Và điều này được giải thích chi tiết hơn trong PEP 258: Thuộc tính docstrings. Như đã giải thích ở trên ʇsәɹoɈ. một thuộc tính không phải là đối tượng có thể sở hữu __doc__ để chúng không xuất hiện trong help() hoặc pydoc. Những tài liệu này chỉ có thể được sử dụng cho tài liệu được tạo.

Nhưng hiện tại rất ít công cụ sử dụng chúng.

Cũ hơn Epydoc do use them và Sphinx đã giới thiệu nó trong v0.6 và mở rộng nó trong v1.1. Nhân sư có thể sử dụng docstring trên một dòng trước khi phân công hoặc trong một nhận xét đặc biệt sau một bài tập.

Xem directive autoattribute in the Sphinx Manual và các ví dụ về tài liệu thuộc tính tại đó.

+0

Plugin jedi-vim cũng công nhận tài liệu thuộc tính. –

+1

Tôi không biết khi nào điều này được giới thiệu, nhưng Sphinx 1.2.2 dường như bao gồm các thuộc tính docstrings trong tài liệu được tạo ra. – jochen

+1

Cảm ơn bạn @ jochen, tôi cập nhật câu trả lời của mình. – marcz

7

Bạn có thể lạm dụng thuộc tính với hiệu ứng này. Thuộc tính chứa bộ thu thập dữ liệu, trình đặt, dấu phân cách, và một chuỗi tài liệu. Ngây thơ, điều này sẽ nhận được rất chi tiết:

class C: 
    def __init__(self): 
     self._x = None 

    @property 
    def x(self): 
     """Docstring goes here.""" 
     return self._x 

    @x.setter 
    def x(self, value): 
     self._x = value 

    @x.deleter 
    def x(self): 
     del self._x 

Sau đó, bạn sẽ có một docstring thuộc Cx:

In [24]: print(C.x.__doc__) 
Docstring goes here. 

Để thực hiện điều này trong nhiều thuộc tính là cồng kềnh, nhưng bạn có thể hình dung một hàm helper myprop:

def myprop(x, doc): 
    def getx(self): 
     return getattr(self, '_' + x) 

    def setx(self, val): 
     setattr(self, '_' + x, val) 

    def delx(self): 
     delattr(self, '_' + x) 

    return property(getx, setx, delx, doc) 

class C: 
    a = myprop("a", "Hi, I'm A!") 
    b = myprop("b", "Hi, I'm B!") 

In [44]: c = C() 

In [46]: c.b = 42 

In [47]: c.b 
Out[47]: 42 

In [49]: print(C.b.__doc__) 
Hi, I'm B! 

Sau đó, gọi Trăn tương tác help sẽ cung cấp cho:

Help on class C in module __main__: 

class C 
| Data descriptors defined here: 
| 
| a 
|  Hi, I'm A! 
| 
| b 
|  Hi, I'm B! 

which I think should be pretty much what you're after. 

Chỉnh sửa: Bây giờ tôi nhận ra rằng có lẽ chúng ta có thể tránh phải vượt qua đối số đầu tiên là myprop vì tên nội bộ không quan trọng. Nếu các cuộc gọi tiếp theo của myprop bằng cách nào đó có thể giao tiếp với nhau, nó có thể tự động quyết định tên thuộc tính nội bộ dài và không chắc chắn. Tôi chắc chắn có nhiều cách để thực hiện điều này, nhưng tôi không chắc liệu chúng có xứng đáng không.

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