Saya membutuhkan pendekatan kerja untuk mendapatkan semua kelas yang diwarisi dari kelas dasar dengan Python.
Kelas gaya baru (yaitu subclass dari object
, yang merupakan default di Python 3) memiliki __subclasses__
metode yang mengembalikan subclass:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
Berikut adalah nama-nama subclass:
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
Berikut adalah subkelasnya sendiri:
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
Konfirmasi bahwa subclass memang mendaftar Foo
sebagai basis mereka:
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
Catatan jika Anda ingin subkelas, Anda harus mengulang:
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}
Perhatikan bahwa jika definisi kelas dari subclass belum dieksekusi - misalnya, jika modul subclass belum diimpor - maka subclass itu belum ada, dan __subclasses__
tidak akan menemukannya.
Anda menyebutkan "diberi nama". Karena kelas Python adalah objek kelas satu, Anda tidak perlu menggunakan string dengan nama kelas sebagai pengganti kelas atau semacamnya. Anda bisa menggunakan kelas secara langsung, dan Anda mungkin harus melakukannya.
Jika Anda memiliki string yang mewakili nama kelas dan Anda ingin menemukan subclass kelas itu, maka ada dua langkah: temukan kelas yang diberi nama, dan kemudian temukan subclass dengan __subclasses__
seperti di atas.
Cara menemukan kelas dari nama tergantung pada tempat Anda mengharapkannya. Jika Anda berharap menemukannya dalam modul yang sama dengan kode yang mencoba menemukan kelas, maka
cls = globals()[name]
akan melakukan pekerjaan itu, atau dalam kasus yang tidak Anda harapkan untuk menemukannya di penduduk setempat,
cls = locals()[name]
Jika kelas bisa dalam modul apa pun, maka string nama Anda harus berisi nama yang sepenuhnya memenuhi syarat - sesuatu seperti 'pkg.module.Foo'
bukan hanya 'Foo'
. Gunakan importlib
untuk memuat modul kelas, lalu ambil atribut yang sesuai:
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
Namun Anda menemukan kelas, cls.__subclasses__()
maka akan mengembalikan daftar subkelasnya.
Jika Anda hanya ingin subclass langsung maka
.__subclasses__()
berfungsi dengan baik. Jika Anda ingin semua subclass, subclass dari subclass, dan sebagainya, Anda akan memerlukan fungsi untuk melakukannya untuk Anda.Inilah fungsi sederhana dan mudah dibaca yang secara rekursif menemukan semua subclass dari kelas yang diberikan:
sumber
all_subclasses
menjadiset
untuk menghilangkan duplikat?A(object)
,B(A)
,C(A)
, danD(B, C)
.get_all_subclasses(A) == [B, C, D, D]
.Solusi paling sederhana dalam bentuk umum:
Dan metode kelas jika Anda memiliki satu kelas di mana Anda mewarisi dari:
sumber
Python 3.6 -
__init_subclass__
Seperti jawaban lain yang disebutkan, Anda dapat memeriksa
__subclasses__
atribut untuk mendapatkan daftar subclass, karena python 3.6 Anda dapat memodifikasi pembuatan atribut ini dengan mengganti__init_subclass__
metode.Dengan cara ini, jika Anda tahu apa yang Anda lakukan, Anda dapat mengesampingkan perilaku
__subclasses__
dan menghilangkan / menambahkan subclass dari daftar ini.sumber
__init_subclass
pada kelas induknya.Catatan: Saya melihat bahwa seseorang (bukan @unutbu) mengubah jawaban yang direferensikan sehingga tidak lagi menggunakan
vars()['Foo']
- sehingga titik utama dari posting saya tidak berlaku lagi.FWIW, inilah yang saya maksudkan tentang jawaban @ unutbu hanya bekerja dengan kelas yang ditentukan secara lokal - dan bahwa menggunakan
eval()
bukannyavars()
akan membuatnya bekerja dengan kelas yang dapat diakses, tidak hanya yang didefinisikan dalam lingkup saat ini.Bagi mereka yang tidak suka menggunakan
eval()
, cara juga ditunjukkan untuk menghindarinya.Pertama, inilah contoh nyata yang menunjukkan potensi masalah dengan penggunaan
vars()
:Ini dapat ditingkatkan dengan memindahkan fungsi
eval('ClassName')
ke bawah ke dalam fungsi yang didefinisikan, yang membuatnya lebih mudah tanpa kehilangan generalitas tambahan yang diperoleh dengan menggunakaneval()
yang tidak sepertivars()
itu tidak peka konteks:Terakhir, itu mungkin, dan mungkin bahkan penting dalam beberapa kasus, untuk menghindari penggunaan
eval()
karena alasan keamanan, jadi inilah versi tanpa itu:sumber
eval()
- lebih baik sekarang?Versi yang jauh lebih pendek untuk mendapatkan daftar semua subclass:
sumber
Kita tentu bisa dengan mudah melakukan ini mengingat akses ke objek itu sendiri, ya.
Cukup diberi namanya adalah ide yang buruk, karena mungkin ada beberapa kelas dengan nama yang sama, bahkan didefinisikan dalam modul yang sama.
Saya membuat implementasi untuk jawaban lain , dan karena menjawab pertanyaan ini dan itu sedikit lebih elegan daripada solusi lain di sini, ini dia:
Pemakaian:
sumber
Ini bukan jawaban yang sebaik menggunakan
__subclasses__()
metode kelas bawaan yang @unutbu sebutkan, jadi saya menyajikannya hanya sebagai latihan. Thesubclasses()
fungsi yang didefinisikan kembali sebuah kamus yang memetakan semua nama subclass ke subclass sendiri.Keluaran:
sumber
Inilah versi tanpa rekursi:
Ini berbeda dari implementasi lain karena mengembalikan kelas asli. Ini karena membuat kode lebih sederhana dan:
Jika get_subclasses_gen terlihat sedikit aneh itu karena itu dibuat dengan mengubah implementasi tail-recursive menjadi generator looping:
sumber