ฉันจะตรวจสอบ (ที่รันไทม์) ได้อย่างไรถ้าคลาสหนึ่งเป็นคลาสย่อยของอีกคลาส?


197

สมมติว่าฉันมีคลาส Suit และคลาสย่อยสี่ชุด: Heart, Spade, Diamond, Club

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

ฉันมีวิธีการที่ได้รับชุดสูทเป็นพารามิเตอร์ซึ่งเป็นวัตถุคลาสไม่เป็นตัวอย่าง แม่นยำมากขึ้นอาจได้รับเพียงหนึ่งในสี่ค่า: Heart, Spade, Diamond, Club ฉันจะยืนยันซึ่งยืนยันสิ่งนั้นได้อย่างไร สิ่งที่ต้องการ:

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

ฉันใช้ Python 3


1
@Leopd: มันไม่ชัดเจนจริงๆเหรอ? ฉันได้ระบุสิ่งที่เป็นไปได้สี่ค่าซึ่งmy_methodอาจเป็นพารามิเตอร์ได้: "มันอาจได้รับเพียงหนึ่งในสี่ค่า: Heart, Spade, Diamond, Club" ค่าเหล่านั้นเป็นวัตถุคลาสไม่ใช่อินสแตนซ์ของคลาส ดูเหมือนจะชัดเจนสำหรับฉันแม้ว่าฉันคิดว่าคุณพูดถูกเกี่ยวกับความคลุมเครือเพราะคำตอบนั้นครอบคลุมความเป็นไปได้ทั้งสองอย่าง คุณยินดีที่จะแก้ไขคำถามถ้าคุณมีคำที่ชัดเจนกว่านี้ ขอบคุณสำหรับความคิดเห็น
snakile

@snakile ใช่มันไม่ชัดเจน เนื่องจากการพึ่งพาความถูกต้องของการแสดงออกของตัวเองของทุกคนเป็นน้ำแข็งบางในหัวข้อนี้ ผู้มาใหม่หลายคนไม่สามารถได้รับทุกอย่าง - เป็น - วัตถุ - ใน - หลามอาจแสดงสิ่งหนึ่ง แต่คิดอีก นั่นเป็นความจริงและความบริสุทธิ์ต่างหากมันค่อนข้างสมเหตุสมผลที่จะคาดหวังพฤติกรรมนี้จากผู้มาใหม่ การปล่อยให้ชื่อเสียงของคุณชี้เฉพาะคำใบ้โดยตรงว่าการแสดงออกของคุณที่นี่ถูกต้องหรือฉันควรจะพูดว่า "ในแง่ของความถูกต้อง" ฉันเข้าใจความต้องการที่จะนำความรู้ของคุณมาพิจารณาและมันก็ไม่มีเหตุผลที่จะไม่คำนึงถึงผู้ที่เพิ่งเข้ามาใหม่
n611x007

1
@snakile นั้นและสิ่งที่มันอาจจะเป็นเหตุผลที่จะใช้การประชุมตั้งชื่อที่ต่อท้ายชื่อพารามิเตอร์ดังกล่าวกับทำให้พวกเขาชอบ_class suit_classผมนำเสนอการประชุมการตั้งชื่อดังกล่าวในคำถามที่เกี่ยวข้อง
n611x007

แนะนำให้เพิ่มเข้าไปในโค้ดตัวอย่างสี่บรรทัดmy_method(Heart) my_method(Spade)...
Bob Stein

สำหรับกรณีที่ตัวแปรที่กำลังทดสอบไม่รับประกันว่าจะเป็นคลาสคุณสามารถเพิ่มเงื่อนไขinspect.isclassหรือใช้isinstance(myvar, type)ใน Python 3 ได้เช่นเดียวกับที่issubclassจะทำให้เกิดข้อผิดพลาดหากผ่านการไม่ใช่คลาส ดูคำตอบนี้ ฉันจะได้แสดงความคิดเห็นกับคำตอบด้านล่าง แต่มันจะไม่เคยเห็นแสงของวัน
Totalhack

คำตอบ:


225

คุณสามารถใช้เช่นนี้issubclass()assert issubclass(suit, Suit)


55
"แต่ทำไมคุณถึงต้องการทำสิ่งนี้?" - เนื่องจากคุณมีคลาส container ที่คุณต้องแน่ใจว่าเป็นเนื้อเดียวกันและวิธีเดียวที่จะทำคือตรวจสอบชนิดของเม็ดมีด?
Adam Parkin

140
หากมีสิ่งหนึ่งที่คงที่ใน Stack Overflow ก็คือคำถามใด ๆ ที่มีคำตอบที่แสดงถึง isinstance หรือ isscclass จะมาพร้อมกับการบรรยายเกี่ยวกับการพิมพ์เป็ด!
Ben Roberts

26
ฉันเจอคำถามนี้เพื่อพยายามหาวิธีตรวจสอบว่า dtype ที่เป็น numpy ของฉันเป็นลูกลอยหรือ int สำหรับแอปพลิเคชั่นประมวลผลภาพ ถ้ามันลอยแบบแผนคือการทำให้เป็นมาตรฐานระหว่าง 0.0 และ 1.0 ถ้ามันเป็นแบบนั้นการประชุมคือ 0 ถึง 255 ฉันสามารถผ่านทุกประเภทของความพยายามที่จะลองรับภาพเพื่อต้มตุ๋น แต่มันตรงไปข้างหน้ามากขึ้น แค่ถามว่า "คุณคือเป็ด" และปรับการทำงานของฉันให้เหมาะสม
Omegaman

28
การทดสอบคลาสย่อยทำให้การทดสอบหน่วยในหลาย ๆ อย่างโดยเฉพาะอย่างยิ่งรุ่นของ Django ง่ายกว่ามาก "Python ไม่ใช่ Java" ทำไมโปรแกรมเมอร์ไพ ธ อนจึงต้องมีชิปบนไหล่?
Michael Bacon

20
ไม่ต้องถอนรากถอนโคนอย่างหมดจดเพราะเป็นคำพูดที่หยาบคายและหยิ่งผยองในตอนท้าย คำอธิบายเกี่ยวกับสาเหตุที่อาจไม่จำเป็นต้องทำสิ่งนี้จะเป็นมิตรและเป็นประโยชน์มากกว่า
Michael Scheper


26

คุณสามารถใช้isinstanceถ้าคุณมีตัวอย่างหรือissubclassถ้าคุณมีชั้นเรียน โดยปกติแล้วมันเป็นความคิดที่ไม่ดี โดยปกติแล้วใน Python คุณจะสามารถทำงานได้ถ้าวัตถุมีความสามารถบางอย่างโดยพยายามทำสิ่งนั้นกับมัน


ถ้าคุณรู้ว่าคุณไม่สามารถทำสิ่งนั้นกับมันได้? คุณมีข้อยกเว้นและลองอย่างอื่นหรือไม่
ต้อง

2
@wrongusername: นั่นเป็นวิธี 'Pythonic' ใช่ ฉันคิดว่าประเพณีนี้มีข้อดีฉันจึงทำตามตราบเท่าที่ยังรักษารหัสของฉันไว้ มีการอภิปรายที่ดีเกี่ยวกับเรื่องนี้ที่นี่: stackoverflow.com/questions/7604636/…
Michael Scheper

8
@Michael Scheper: ฉันต้องบอกว่าถ้าเป็นวิธี pythonic แล้วฉันเกลียดวิธี pythonic จริงๆ IMO, ข้อยกเว้นไม่ควรใช้สำหรับโฟลว์การควบคุม หากคุณคิดว่าอาจเกิดข้อผิดพลาดให้ป้องกันมัน ... อย่าทำเหมือน GOTO ลิงค์ที่น่าสนใจที่คุณโพสต์
leviathanbadger

@ aboveyou00: ดูเหมือนว่าคุณกำลังพูดถึงกรณีที่ละเมิด 'ตราบใดที่รหัสของฉันยังคงชัดเจนอยู่' ฉันไม่ได้เป็นแฟนของประเพณี Pythonic ทั้งหมดเนื่องจากผู้คนจำนวนมากละเมิดหลักการ EAFP และจบลงด้วยการสร้างข้อบกพร่องที่หายาก
Michael Scheper

การตรวจสอบนี้มีประโยชน์เมื่อกำหนดสัญญา ตัวอย่างเช่นตัวสร้างที่รับวัตถุ: เราต้องการยืนยันว่าวัตถุที่ได้รับนั้นเป็นตัวอย่างของคลาสที่กำหนดและโดยการทำเช่นนั้นเราจะแจ้งผู้อ่านโค้ดถึงสิ่งที่ตัวสร้างคาดหวัง
mastropi

21

issubclass(sub, sup)ฟังก์ชั่นบูลผลตอบแทนจริงถ้า subclass กำหนดsubแน่นอน subclass supของซับคลาส


9
คำตอบที่ไม่มีการบรรยายที่เข้าใจผิด +1
Gringo Suave

2

issubclass ตัวอย่าง runnable น้อยที่สุด

นี่คือตัวอย่างที่สมบูรณ์มากขึ้นด้วยการยืนยันบางอย่าง:

#!/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 ต้นน้ำ

ทดสอบใน Python 3.5.2


1

คุณสามารถใช้ builtin issubclass แต่การตรวจสอบประเภทมักจะไม่จำเป็นเนื่องจากคุณสามารถใช้การพิมพ์เป็ดได้


1

การใช้ issubclass ดูเหมือนเป็นวิธีที่สะอาดในการเขียน loglevels มันรู้สึกแปลก ๆ ที่ใช้มัน ... แต่มันดูดีกว่าตัวเลือกอื่น ๆ

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

จากเอกสารของ Pythonเราสามารถใช้class.__mro__attribute หรือclass.mro()method:

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
คำตอบของรหัสเท่านั้นไม่ได้รับการยอมรับใน SO คุณควรเพิ่มข้อความอธิบายลงในรหัสเสมอ อย่างไรก็ตามคำตอบนี้ไม่จำเป็น: เพิ่มข้อมูลที่ยังไม่ได้กล่าวถึงในคำตอบก่อนหน้านี้
PM 2Ring
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.