Bagaimana cara mendokumentasikan atribut kelas dengan Python? [Tutup]

115

Saya sedang menulis kelas ringan yang atributnya dimaksudkan agar dapat diakses publik, dan hanya terkadang diganti dalam contoh tertentu. Tidak ada ketentuan dalam bahasa Python untuk membuat docstring untuk atribut kelas, atau atribut apa pun, dalam hal ini. Apa cara yang diharapkan dan didukung, haruskah ada, untuk mendokumentasikan atribut ini? Saat ini saya melakukan hal semacam ini:

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)

Ini akan menghasilkan docstring kelas yang berisi bagian docstring standar awal, serta baris yang ditambahkan untuk setiap atribut melalui tugas tambahan ke __doc__.

Meskipun gaya ini tampaknya tidak dilarang secara tegas dalam pedoman gaya docstring, gaya ini juga tidak disebutkan sebagai opsi. Keuntungannya di sini adalah menyediakan cara untuk mendokumentasikan atribut bersama definisinya, sambil tetap membuat docstring kelas yang rapi, dan menghindari keharusan menulis komentar yang mengulangi informasi dari docstring. Saya masih kesal karena saya harus benar-benar menulis atribut dua kali; Saya sedang mempertimbangkan untuk menggunakan representasi string dari nilai-nilai di docstring untuk setidaknya menghindari duplikasi nilai default.

Apakah ini pelanggaran keji dari konvensi komunitas ad hoc? Apakah itu oke Apakah ada cara yang lebih baik? Misalnya, dimungkinkan untuk membuat kamus yang berisi nilai dan docstrings untuk atribut dan kemudian menambahkan konten ke kelas __dict__dan docstring di akhir deklarasi kelas; ini akan mengurangi kebutuhan untuk mengetik nama dan nilai atribut dua kali. sunting : ide terakhir ini, saya pikir, sebenarnya tidak mungkin, setidaknya tidak tanpa secara dinamis membangun seluruh kelas dari data, yang tampaknya seperti ide yang sangat buruk kecuali ada alasan lain untuk melakukan itu.

Saya cukup baru mengenal python dan masih mengerjakan detail gaya pengkodean, jadi kritik yang tidak terkait juga diterima.

intuitif
sumber
Jika Anda mencari cara untuk mendokumentasikan atribut model Django, ini mungkin berguna: djangosnippets.org/snippets/2533
Michael Scheper
3
Duplikat dari Bagaimana mendokumentasikan bidang dan properti dengan Python? yang memiliki solusi berbeda.
bufh
1
Saya tidak mengerti mengapa ini didasarkan pada opini. Python secara khusus mendokumentasikan konvensi yang dapat diterima di PEP. Ada berbagai alat sumber Python yang mengekstrak dokumentasi yang diformat dengan benar. Faktanya Python sebenarnya memiliki yang attribute doc stringdisebutkan dalam PEP 257 yang tidak terkenal dan tampaknya sulit ditemukan yang mungkin menjawab pertanyaan OP, dan didukung oleh beberapa alat sumber. Ini bukan opini. Itu fakta, dan bagian dari bahasa, dan hampir persis seperti yang diinginkan OP.
NeilG

Jawaban:

83

Untuk menghindari kebingungan: istilah properti memiliki arti khusus dalam python. Yang Anda bicarakan adalah yang kami sebut atribut kelas . Karena mereka selalu ditindaklanjuti melalui kelas mereka, menurut saya masuk akal untuk mendokumentasikannya dalam string dokumen kelas. Sesuatu seperti ini:

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"

Saya pikir itu jauh lebih mudah dilihat daripada pendekatan dalam contoh Anda. Jika saya benar-benar ingin salinan nilai atribut muncul di string dokumen, saya akan meletakkannya di samping atau di bawah deskripsi setiap atribut.

Perlu diingat bahwa di Python, string dokumen adalah anggota sebenarnya dari objek yang mereka dokumentasikan, bukan hanya anotasi kode sumber. Karena variabel atribut kelas bukanlah objek itu sendiri tetapi referensi ke objek, mereka tidak memiliki cara untuk menyimpan string doc mereka sendiri. Saya kira Anda bisa membuat kasus untuk string doc pada referensi, mungkin untuk menjelaskan "apa yang harus diletakkan di sini" daripada "apa yang sebenarnya ada di sini", tapi saya merasa cukup mudah untuk melakukannya dalam string doc kelas yang berisi.

ʇsәɹoɈ
sumber
Saya kira dalam banyak kasus ini baik-baik saja, karena atribut — terima kasih untuk koreksi terminologi — cukup ringkas dinyatakan sehingga mereka hanya dapat dikelompokkan di awal deklarasi kelas tanpa membuatnya tidak praktis untuk bolak-balik ke {baca keduanya dokumentasi dan nilai default} atau {perbarui kedua contoh dokumentasi dan / atau nilai default}.
Intuisi
1
Perhatikan juga bahwa contoh saya akan menyebabkan dokumentasi untuk atribut muncul di docstring kelas. Saya sebenarnya lebih suka meletakkan dokumentasi di docstrings dari atributnya sendiri, tetapi ini tidak berfungsi untuk sebagian besar tipe bawaan.
Intuisi
Ya, ide awal saya adalah menyatakan mis flight_speed = 691; flight_speed.__doc__ = "blah blah". Saya pikir inilah yang Anda sebutkan dalam suntingan Anda . Sayangnya, ini tidak berfungsi untuk contoh jenis bawaan (kebanyakan?) (Seperti intdalam contoh itu). Ini berfungsi untuk contoh tipe yang ditentukan pengguna. =========== Sebenarnya ada PEP (maaf, lupakan nomornya) yang mengusulkan penambahan docstring untuk atribut kelas / modul, tapi ditolak karena mereka tidak bisa menemukan cara untuk menjelaskannya apakah docstring untuk atribut sebelumnya atau berikut.
Intuisi
2
jadi bagaimana jika mereka adalah atribut instance? masih mendokumentasikan di kelas docstring atau apa?
n611x007
1
@intuited Apakah PEP ini? legacy.python.org/dev/peps/pep-0224
taz
30

Anda mengutip PEP257: Konvensi Docstring, di bagian Apa itu docstring dinyatakan:

Literal string yang terjadi di tempat lain dalam kode Python juga dapat bertindak sebagai dokumentasi. Mereka tidak dikenali oleh kompilator bytecode Python dan tidak dapat diakses sebagai atribut objek runtime (yaitu tidak ditugaskan ke __doc__), tetapi dua jenis dokumen tambahan dapat diekstraksi oleh perangkat lunak:

Literal string yang muncul segera setelah tugas sederhana di tingkat atas modul, kelas, atau metode __init__ disebut "atribut docstrings".

Dan ini dijelaskan lebih detail di PEP 258: Dokumentasi atribut. Seperti yang dijelaskan di atas ʇsәɹoɈ. atribut bukan objek yang bisa memiliki __doc__ sehingga tidak akan muncul di help()atau pydoc. Docstring ini hanya dapat digunakan untuk dokumentasi yang dibuat.

Mereka digunakan di Sphinx dengan directive autoattribute

Sphinx dapat menggunakan komentar pada baris sebelum tugas atau komentar khusus setelah tugas atau docstring setelah definisi yang akan didokumentasikan secara otomatis.

marcz
sumber
1
Plugin jedi-vim juga mengenali atribut docstrings.
Long Vu
1
Saya tidak tahu kapan ini diperkenalkan, tetapi Sphinx 1.2.2 tampaknya menyertakan dokumen atribut dalam dokumentasi yang dihasilkan.
jochen
1
Terima kasih @jochen, saya memperbarui jawaban saya.
marcz
3
Harap dicatat bahwa PEP 258 ditolak. Pemberitahuan penolakan menyatakan: "Meskipun ini mungkin berfungsi sebagai dokumen desain yang menarik untuk docutils yang sekarang sudah independen, ini tidak lagi dijadwalkan untuk disertakan dalam pustaka standar."
Michał Łazowik
13

Anda dapat menyalahgunakan properti untuk efek ini. Properti berisi getter, setter, deleter, dan docstring . Secara naif, ini akan menjadi sangat bertele-tele:

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

Maka Anda akan memiliki docstring milik Cx:

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

Melakukan ini untuk banyak atribut memang merepotkan, tetapi Anda bisa membayangkan fungsi pembantu 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!

Kemudian, memanggil Pythons interaktif helpakan memberikan:

Help on class C in module __main__:

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

yang menurut saya seharusnya sesuai dengan keinginan Anda.

Sunting : Saya menyadari sekarang bahwa kita mungkin dapat menghindari perlunya memberikan argumen pertama mypropsama sekali, karena nama internal tidak masalah. Jika panggilan berikutnya mypropentah bagaimana dapat berkomunikasi satu sama lain, itu dapat secara otomatis memutuskan nama atribut internal yang panjang dan tidak mungkin. Saya yakin ada cara untuk menerapkan ini, tetapi saya tidak yakin apakah itu sepadan.

gerrit
sumber