Apakah boleh memiliki beberapa kelas dalam file yang sama dengan Python?

18

Saya baru datang ke dunia Python setelah bertahun-tahun Java dan PHP. Sementara bahasanya sendiri cukup mudah, saya berjuang dengan beberapa masalah 'kecil' yang tidak dapat saya lewati - dan saya tidak dapat menemukan jawaban di berbagai dokumen dan tutorial yang saya baca sejauh ini. .

Untuk praktisi Python yang berpengalaman, pertanyaan ini mungkin tampak konyol, tetapi saya benar-benar ingin jawaban untuk itu sehingga saya bisa melangkah lebih jauh dengan bahasanya:

Dalam Java dan PHP ( meskipun tidak sepenuhnya diperlukan ), Anda diharapkan untuk menulis masing class- masing pada file sendiri, dengan nama file adalah classsebagai praktik terbaik.

Tetapi dengan Python, atau setidaknya dalam tutorial yang telah saya periksa, boleh saja memiliki beberapa kelas dalam file yang sama.

Apakah aturan ini berlaku dalam produksi, kode yang siap digunakan atau itu dilakukan hanya untuk singkatnya dalam kode hanya edukatif?

Olivier Malki
sumber

Jawaban:

13

Apakah boleh memiliki beberapa kelas dalam file yang sama dengan Python?

Iya. Baik dari perspektif filosofis maupun praktis.

Dalam Python, modul adalah namespace yang ada sekali di memori.

Katakanlah kita memiliki struktur direktori hipotetis berikut, dengan satu kelas ditentukan per file:

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

Semua kelas ini tersedia dalam collectionsmodul dan (pada kenyataannya, total ada 25) didefinisikan dalam modul perpustakaan standar di_collections_abc.py

Ada beberapa masalah di sini yang saya percaya membuat _collections_abc.pylebih unggul daripada struktur direktori hipotetis alternatif.

  • File-file ini disortir berdasarkan abjad. Anda dapat mengurutkannya dengan cara lain, tetapi saya tidak mengetahui fitur yang mengurutkan file berdasarkan dependensi semantik. Sumber modul _collections_abc diatur oleh ketergantungan.
  • Dalam kasus-kasus non-patologis, baik modul dan definisi kelas adalah lajang, yang terjadi sekali dalam memori. Akan ada pemetaan bijective dari modul ke dalam kelas - membuat modul menjadi berlebihan.
  • Meningkatnya jumlah file membuatnya kurang nyaman untuk membaca kelas secara santai (kecuali jika Anda memiliki IDE yang membuatnya sederhana) - membuatnya kurang dapat diakses oleh orang-orang tanpa alat.

Apakah Anda dicegah dari memecah kelompok kelas menjadi modul yang berbeda ketika Anda merasa diinginkan dari sudut pandang ruang nama dan organisasi?

Tidak.

Dari Zen Python , yang mencerminkan filosofi dan prinsip di mana ia tumbuh dan berkembang:

Namespaces adalah salah satu ide bagus - mari kita lakukan lebih dari itu!

Tetapi marilah kita ingat bahwa itu juga mengatakan:

Flat lebih baik daripada bersarang.

Python sangat bersih dan mudah dibaca. Ini mendorong Anda untuk membacanya. Menempatkan setiap kelas yang terpisah dalam file yang terpisah akan menghambat pembacaan. Ini bertentangan dengan filosofi inti Python. Lihatlah struktur Perpustakaan Standar , sebagian besar modul adalah modul file tunggal, bukan paket. Saya akan menyampaikan kepada Anda bahwa kode Python idiomatik ditulis dengan gaya yang sama dengan lib standar CPython.

Berikut kode aktual dari modul kelas dasar abstrak . Saya suka menggunakannya sebagai referensi untuk denotasi berbagai jenis abstrak dalam bahasa.

Apakah Anda mengatakan bahwa masing-masing kelas ini harus memerlukan file terpisah?

class Hashable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            try:
                for B in C.__mro__:
                    if "__hash__" in B.__dict__:
                        if B.__dict__["__hash__"]:
                            return True
                        break
            except AttributeError:
                # Old-style class
                if getattr(C, "__hash__", None):
                    return True
        return NotImplemented


class Iterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if _hasattr(C, "__iter__"):
                return True
        return NotImplemented

Iterable.register(str)


class Iterator(Iterable):

    @abstractmethod
    def next(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if _hasattr(C, "next") and _hasattr(C, "__iter__"):
                return True
        return NotImplemented


class Sized:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if _hasattr(C, "__len__"):
                return True
        return NotImplemented


class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented


class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            if _hasattr(C, "__call__"):
                return True
        return NotImplemented

Jadi haruskah mereka masing-masing memiliki file sendiri?

Saya harap tidak.

File-file ini bukan hanya kode - mereka adalah dokumentasi tentang semantik Python.

Mereka mungkin rata-rata 10 hingga 20 baris. Mengapa saya harus pergi ke file yang benar-benar terpisah untuk melihat 10 baris kode lainnya? Itu akan sangat tidak praktis. Lebih lanjut, akan ada impor boilerplate yang hampir identik pada setiap file, menambahkan lebih banyak baris kode yang berlebihan.

Saya merasa cukup berguna untuk mengetahui bahwa ada satu modul di mana saya dapat menemukan semua Kelas Dasar Abstrak ini, daripada harus melihat daftar modul. Melihat mereka dalam konteks satu sama lain memungkinkan saya untuk lebih memahami mereka. Ketika saya melihat bahwa Iterator adalah Iterable, saya dapat dengan cepat meninjau apa yang terdiri dari Iterable dengan menoleh ke atas.

Kadang-kadang saya memiliki beberapa kelas yang sangat singkat. Mereka tetap dalam file, bahkan jika mereka perlu tumbuh lebih besar dari waktu ke waktu. Terkadang modul yang matang memiliki lebih dari 1000 baris kode. Tapi ctrl-f mudah, dan beberapa IDE membuatnya mudah untuk melihat garis besar file - jadi tidak peduli seberapa besar file, Anda dapat dengan cepat pergi ke objek atau metode apa pun yang Anda cari.

Kesimpulan

Arah saya, dalam konteks Python, adalah untuk memilih untuk menjaga definisi kelas yang terkait dan secara semantik serupa dalam file yang sama. Jika file tumbuh sangat besar sehingga menjadi berat, maka pertimbangkan reorganisasi.

Aaron Hall
sumber
1
Yah, sementara saya mengerti, berkat kode yang Anda kirimkan bahwa tidak masalah memiliki beberapa kelas dalam file yang sama, saya tidak dapat menemukan argumen yang sangat meyakinkan. Sebagai contoh, sangat umum di PHP untuk memiliki seluruh file dengan hanya kode yang mirip dengan ini:class SomeException extends \Exception {}
Olivier Malki
3
Komunitas yang berbeda memiliki standar pengkodean yang berbeda. Orang Jawa melihat python dan mengatakan "mengapa itu memungkinkan beberapa kelas per file !?". Orang Python melihat Java dan berkata "mengapa setiap kelas memerlukan file sendiri !?". Cara terbaik adalah mengikuti gaya komunitas tempat Anda bekerja.
Gort the Robot
Saya bingung di sini juga. Tampaknya saya salah mengerti beberapa hal tentang Python dengan jawaban saya. Tetapi apakah kita akan menerapkan "flat lebih baik daripada bersarang" untuk menambahkan metode sebanyak mungkin ke kelas? Secara umum saya akan berpikir prinsip-prinsip kohesi dan SRP masih berlaku untuk modul yang mendukung modul yang menyediakan kelas yang berhubungan erat satu sama lain dalam fungsionalitas (meskipun mungkin sangat baik lebih dari satu kelas karena modul memodelkan konsep paket kasar daripada kelas tunggal) ), terutama karena variabel lingkup modul (meskipun diharapkan dihindari secara umum) akan meningkat dalam cakupan.
1
Zen Python adalah daftar prinsip yang saling tegang. Seseorang mungkin merespons sesuai dengan poin Anda dengan, "Jarang lebih baik daripada padat." - yang segera mengikuti, "Flat lebih baik daripada bersarang." Garis-garis individual dari The Zen of Python dapat dengan mudah disalahgunakan dan dibawa ke ekstrem, tetapi secara keseluruhan, ini dapat membantu dalam seni pengkodean dan menemukan landasan bersama di mana orang-orang yang masuk akal mungkin tidak setuju. Saya tidak berpikir orang akan menganggap contoh kode saya padat, tetapi apa yang Anda gambarkan terdengar sangat padat bagi saya.
Aaron Hall
Terima kasih Jeffrey Albertson / Guy Buku Komik. :) Sebagian besar pengguna Python tidak boleh menggunakan metode khusus (garis bawah dua kali lipat), tetapi mereka mengizinkan desainer inti / arsitek untuk terlibat dalam meta-pemrograman untuk menggunakan operator, pembanding, notasi subskrip, konteks, dan lainnya secara khusus. fitur bahasa. Selama mereka tidak melanggar prinsip paling tidak mengejutkan, saya pikir rasio kerugian terhadap nilai sangat kecil.
Aaron Hall
4

Saat menyusun aplikasi Anda dengan Python, Anda perlu berpikir dalam hal paket dan modul.

Modul adalah tentang file yang sedang Anda bicarakan. Tidak masalah untuk memiliki banyak kelas dalam modul yang sama. Tujuannya adalah bahwa semua kelas dalam modul yang sama harus melayani tujuan / logika yang sama. Jika modul terlalu lama maka pikirkan untuk membagi kembali dengan mendesain ulang logika Anda.

Jangan lupa membaca dari waktu ke waktu tentang Proposal Peningkatan Indeks Python .


sumber
2

Jawaban sebenarnya untuk ini adalah umum, dan tidak tergantung pada bahasa yang digunakan: Apa yang seharusnya ada dalam file tidak terutama tergantung pada berapa banyak kelas yang didefinisikannya. Itu tergantung pada keterhubungan logis, dan pada kompleksitas. Titik.

Jadi, jika Anda memiliki beberapa kelas yang sangat kecil yang sangat saling berhubungan, mereka harus dibundel ke dalam file yang sama. Anda harus membagi kelas jika tidak terhubung dengan erat ke kelas lain, atau jika terlalu rumit untuk disertakan dengan kelas lain.

Yang mengatakan, aturan satu-kelas-per-file biasanya heuristik yang baik. Namun, ada pengecualian penting: Kelas pembantu kecil yang benar-benar hanya detail implementasi dari kelas pengguna tunggal umumnya harus digabungkan ke dalam file kelas pengguna itu. Demikian juga, jika Anda memiliki tiga kelas vector2,, vector3dan vector4, ada kemungkinan kecil alasan untuk mengimplementasikannya dalam file terpisah.

cmaster - mengembalikan monica
sumber