Bagaimana cara saya memeriksa (saat runtime) jika satu kelas adalah subkelas dari yang lain?


196

Katakanlah saya memiliki Suit kelas dan empat subclass suit: Heart, Spade, Diamond, Club.

class Suit:
   ...
class Heart(Suit):
   ...
class Spade(Suit):
   ...
class Diamond(Suit):
   ...
class Club(Suit):
   ...

Saya memiliki metode yang menerima setelan sebagai parameter, yang merupakan objek kelas, bukan instance. Lebih tepatnya, itu mungkin hanya menerima satu dari empat nilai: Heart, Spade, Diamond, Club. Bagaimana saya bisa membuat pernyataan yang memastikan hal seperti itu? Sesuatu seperti:

def my_method(suit):
   assert(suit subclass of Suit)
   ...

Saya menggunakan Python 3.


1
@Leopd: Benarkah tidak jelas? Saya telah menyatakan dengan tepat apa saja empat nilai yang mungkin my_methoddapat diperoleh sebagai parameter: "mungkin hanya menerima satu dari empat nilai: Heart, Spade, Diamond, Club". Nilai-nilai itu adalah objek kelas, bukan instance kelas. Tampaknya cukup jelas bagi saya, meskipun saya kira Anda benar tentang ketidakjelasan karena jawabannya mencakup kedua kemungkinan. Anda dipersilakan untuk mengedit pertanyaan jika Anda memiliki kata yang lebih jelas untuk itu. Terima kasih atas komentarnya.
berliku

@nakile ya tidak jelas. Karena mengandalkan kebenaran ekspresi diri siapa pun adalah es tipis dalam topik ini. Banyak pendatang baru tidak bisa mendapatkan semuanya-adalah-objek-dalam-python, dapat mengekspresikan satu hal tetapi berpikir lain. Itu kenyataan dan, selain itu, cukup rasional untuk mengharapkan perilaku ini dari pendatang baru. Membiarkan reputasi Anda menunjukkan satu-satunya petunjuk langsung apakah ekspresi Anda di sini benar, atau harus saya katakan, "dalam hal kebenaran". Saya mengerti keinginan untuk memperhitungkan pengetahuan Anda dan masih tidak masuk akal untuk tidak memperhitungkan pendatang baru yang terus memperbaharui.
n611x007

1
@snakile itu, dan hal yang mungkin masuk akal untuk menggunakan konvensi penamaan yang sufiks dengan nama parameter seperti itu _class, menjadikannya seperti suit_class. Saya mengusulkan konvensi penamaan seperti itu dalam pertanyaan yang relevan .
n611x007

Sarankan menambah contoh kode empat baris my_method(Heart) my_method(Spade)...
Bob Stein

Untuk kasus-kasus di mana variabel yang diuji tidak dijamin menjadi kelas, Anda bisa menambahkan kondisi inspect.isclassatau hanya menggunakan isinstance(myvar, type)Python 3, karena issubclassakan memunculkan kesalahan jika melewati non-kelas. Lihat jawaban ini . Saya akan mengomentari jawaban di bawah ini, tetapi tidak akan pernah melihat cahaya hari.
totalhack

Jawaban:


224

Anda bisa menggunakan issubclass()seperti ini assert issubclass(suit, Suit).


55
"Tapi mengapa kamu ingin melakukan hal seperti itu?" - karena Anda memiliki kelas wadah yang perlu Anda pastikan homogen, dan satu-satunya cara untuk melakukannya adalah dengan memeriksa jenis setelah dimasukkan?
Adam Parkin

140
Jika ada satu hal yang konstan pada Stack Overflow, itu adalah setiap pertanyaan dengan jawaban yang menyiratkan isinstance atau issubclass juga akan disertai dengan kuliah tentang mengetik bebek!
Ben Roberts

26
Saya menemukan pertanyaan ini mencoba mencari cara mendeteksi apakah tipe numpy saya adalah pelampung atau int untuk aplikasi pemrosesan gambar. Jika ini pelampung, konvensi akan dinormalisasi antara 0,0 dan 1,0, jika int maka konvensi adalah 0 hingga 255. Saya bisa melalui semua jenis contortions untuk mencoba dan mendapatkan gambar untuk dipadamkan, tetapi jauh lebih lurus ke depan untuk tanyakan saja "apakah Anda bebek" dan skala operasi saya sesuai.
Omegaman

28
Pengujian subclass membuat unit testing banyak hal, terutama model Django, jauh lebih mudah. "Python bukan Java." Mengapa programmer python harus memiliki chip seperti itu di pundak mereka?
Michael Bacon

20
Tidak kecewa, murni karena komentar yang tidak imajinatif dan agak arogan pada akhirnya. Penjelasan tentang mengapa seseorang mungkin tidak perlu melakukan ini akan lebih ramah dan lebih membantu.
Michael Scheper


26

Anda dapat menggunakan isinstancejika Anda memiliki instance, atau issubclassjika Anda memiliki kelas. Biasanya berpikir itu ide yang buruk. Biasanya dalam Python Anda bekerja jika suatu objek mampu melakukan sesuatu dengan mencoba melakukan hal itu padanya.


Bagaimana jika Anda tahu Anda tidak bisa melakukan hal itu dengannya? Apakah Anda menangkap pengecualian dan mencoba sesuatu yang lain?
nama pengguna salah

2
@ nama pengguna: Itu adalah cara 'Pythonic', ya. Saya pikir kebiasaan ini pantas, jadi saya mengikutinya selama itu membuat kode saya jelas. Ada diskusi yang bagus tentang ini di sini: stackoverflow.com/questions/7604636/…
Michael Scheper

8
@ Michael Scheper: Saya harus mengatakan, jika itu cara pythonic, maka saya benar-benar membenci cara pythonic. IMO, pengecualian harus TIDAK PERNAH digunakan untuk aliran kontrol. Jika Anda berpikir kesalahan mungkin terjadi, lindungi dari itu ... jangan memperlakukannya seperti GOTO. Tautan menarik yang Anda poskan
leviathanbadger

@ aboveyou00: Kedengarannya seperti jenis kasus yang Anda bicarakan melanggar 'selama kode saya tetap jelas'. Saya bukan penggemar semua kebiasaan Pythonic, karena banyak orang menyalahgunakan prinsip EAFP dan akhirnya menciptakan bug yang sulit ditemukan.
Michael Scheper

Pemeriksaan ini berguna saat menentukan kontrak. Misalnya, konstruktor yang menerima objek: kami ingin menegaskan bahwa objek yang diterima adalah turunan dari kelas yang diberikan, dan dengan demikian kami menginformasikan kepada pembaca kode apa yang diharapkan konstruktor.
mastropi

21

Fungsi issubclass(sub, sup)boolean mengembalikan true jika subclass yang diberikan submemang merupakan subclass dari superclass sup.


9
Jawabannya tanpa kuliah salah arah +1.
Gringo Suave

2

issubclass contoh runnable minimal

Berikut adalah contoh yang lebih lengkap dengan beberapa pernyataan:

#!/usr/bin/env python3

class Base:
    pass

class Derived(Base):
    pass

base = Base()
derived = Derived()

# Basic usage.
assert issubclass(Derived, Base)
assert not issubclass(Base, Derived)

# True for same object.
assert issubclass(Base, Base)

# Cannot use object of class.
try:
    issubclass(derived, Base)
except TypeError:
    pass
else:
    assert False

# Do this instead.
assert isinstance(derived, Base)

GitHub hulu .

Diuji dalam Python 3.5.2.


1

Anda bisa menggunakan builtin issubclass. Tetapi pengecekan tipe biasanya dianggap tidak perlu karena Anda bisa menggunakan bebek-mengetik.


1

Menggunakan issubclass sepertinya cara yang bersih untuk menulis loglevels. Agak terasa aneh menggunakannya ... tapi sepertinya lebih bersih daripada opsi lain.

class Error(object): pass
class Warn(Error): pass
class Info(Warn): pass
class Debug(Info): pass

class Logger():
    LEVEL = Info

    @staticmethod
    def log(text,level):
        if issubclass(Logger.LEVEL,level):
            print(text)
    @staticmethod
    def debug(text):
        Logger.log(text,Debug)   
    @staticmethod
    def info(text):
        Logger.log(text,Info)
    @staticmethod
    def warn(text):
        Logger.log(text,Warn)
    @staticmethod
    def error(text):
        Logger.log(text,Error)

0

Menurut dokumen Python , kita juga bisa menggunakan class.__mro__atribut atau class.mro()metode:

class Suit:
    pass
class Heart(Suit):
    pass
class Spade(Suit):
    pass
class Diamond(Suit):
    pass
class Club(Suit):
    pass

>>> Heart.mro()
[<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>]
>>> Heart.__mro__
(<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>)

Suit in Heart.mro()  # True
object in Heart.__mro__  # True
Spade in Heart.mro()  # False

-5
#issubclass(child,parent)

class a:
    pass
class b(a):
    pass
class c(b):
    pass

print(issubclass(c,b))#it returns true

1
Jawaban hanya kode tidak dihargai pada SO, Anda harus selalu menambahkan beberapa teks penjelasan ke kode. Namun, jawaban ini berlebihan: tidak menambahkan informasi yang belum disebutkan dalam jawaban sebelumnya.
PM 2Ring
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.