อะไรคือความแตกต่างระหว่าง type () และ isinstance ()?


1247

อะไรคือความแตกต่างระหว่างส่วนย่อยของโค้ดทั้งสองนี้

การใช้type():

import types

if type(a) is types.DictType:
    do_something()
if type(b) in types.StringTypes:
    do_something_else()

การใช้isinstance():

if isinstance(a, dict):
    do_something()
if isinstance(b, str) or isinstance(b, unicode):
    do_something_else()

หมายเหตุ: ถ้าไม่ใช่strและunicode(ซึ่งคุณสามารถตรวจสอบได้basestring) คุณสามารถใช้ tuple เพื่อตรวจสอบกับหลายประเภท เพื่อตรวจสอบว่าsomethingมีintหรือใช้str isinstance(something, (int, str))
xuiqzy

คำตอบ:


1270

เพื่อสรุปเนื้อหาของคำตอบอื่น ๆ (ดีอยู่แล้ว!) isinstanceเหมาะสำหรับการสืบทอด (ตัวอย่างของคลาสที่ได้รับนั้นเป็นอินสแตนซ์ของคลาสพื้นฐานเช่นกัน) ในขณะที่การตรวจสอบความเท่าเทียมกันของtypeไม่ได้ (มันต้องการตัวตนประเภทและปฏิเสธอินสแตนซ์ ของชนิดย่อย, คลาสย่อย AKA)

โดยปกติใน Python คุณต้องการให้โค้ดของคุณรองรับการสืบทอดแน่นอน (เนื่องจากการสืบทอดนั้นมีประโยชน์มากมันจะไม่ดีที่จะหยุดการใช้โค้ดของคุณจากการใช้มัน!) ดังนั้นจึงisinstanceไม่ดีกว่าการตรวจสอบตัวตนของtypes เพราะมันรองรับอย่างราบรื่น มรดก

ไม่ว่าisinstanceจะเป็นสิ่งที่ดี , ใจคุณ - เป็นเพียงน้อยกว่าการตรวจสอบความเท่าเทียมกันของประเภท วิธีการแก้ปัญหาแบบธรรมดาที่ไพ ธ อนต้องการคือ "การพิมพ์เป็ด" อย่างคงที่: ลองใช้การโต้แย้งราวกับว่าเป็นประเภทที่ต้องการให้ทำในtry/ exceptแถลงการณ์เพื่อจับข้อยกเว้นทั้งหมดที่อาจเกิดขึ้นหากการโต้แย้งไม่ได้เกิดขึ้นจริง ประเภท (หรือประเภทอื่น ๆ อย่างเป็ด - ล้อเลียนมัน ;-) และในexceptข้อลองอย่างอื่น (ใช้อาร์กิวเมนต์ "ราวกับว่า" มันเป็นประเภทอื่น ๆ )

basestring เป็นแต่ค่อนข้างพิเศษกรณีเป็นชนิดที่มีอยู่ในตัวเท่านั้นที่จะช่วยให้คุณใช้isinstance(ทั้งstrและunicodesubclass basestring) Strings เป็นลำดับ (คุณสามารถวนรอบดัชนีเหล่านี้แบ่งมัน ... ) แต่โดยทั่วไปแล้วคุณต้องการที่จะใช้มันเป็น "เซนต์คิตส์และเนวิส" - มันค่อนข้าง incovenient (แต่เป็นกรณีที่ใช้บ่อย) เพื่อรักษาทุกชนิด สตริง (และประเภทสเกลาร์อื่น ๆ เช่นประเภทที่คุณไม่สามารถวนซ้ำได้) ทางเดียวตู้คอนเทนเนอร์ทั้งหมด (รายการชุดเซต dicts ... ) ในอีกทางหนึ่งและbasestringบวกisinstanceช่วยให้คุณทำเช่นนั้น - โครงสร้างโดยรวมของสิ่งนี้ สำนวนเป็นสิ่งที่ชอบ:

if isinstance(x, basestring)
  return treatasscalar(x)
try:
  return treatasiter(iter(x))
except TypeError:
  return treatasscalar(x)

อาจกล่าวได้ว่าbasestringเป็นนามธรรมพื้นฐานชั้น ( "เอบีซี") - มันมีฟังก์ชันการทำงานที่เป็นรูปธรรมเพื่อ subclasses แต่อยู่ในฐานะ "เครื่องหมาย" isinstanceส่วนใหญ่สำหรับการใช้งานกับ เห็นได้ชัดว่าแนวคิดดังกล่าวเป็นสิ่งที่กำลังเติบโตอย่างมากใน Python เนื่องจากPEP 3119ซึ่งนำเสนอการวางนัยทั่วไปของมันได้รับการยอมรับและมีการใช้งานเริ่มต้นด้วย Python 2.6 และ 3.0

PEP ทำให้ชัดเจนว่าในขณะที่ ABCs สามารถใช้แทนการพิมพ์เป็ดได้โดยทั่วไปไม่มีแรงกดดันที่จะทำเช่นนั้น (ดูที่นี่ ) ABCs ที่นำมาใช้ในเวอร์ชัน Python ล่าสุดมีข้อเสนอพิเศษสำหรับสินค้า: isinstance(และissubclass) สามารถมีความหมายมากกว่าแค่ "[ตัวอย่างของ] คลาสที่ได้รับ" (โดยเฉพาะอย่างยิ่งทุกคลาสสามารถ "ลงทะเบียน" กับ ABC ได้ แสดงเป็นคลาสย่อยและอินสแตนซ์ของมันเป็นอินสแตนซ์ของ ABC) และ ABCs ยังสามารถมอบความสะดวกสบายเป็นพิเศษให้กับคลาสย่อยจริงในแบบที่เป็นธรรมชาติผ่านแอพพลิเคชั่นรูปแบบการออกแบบเทมเพลต (ดูที่นี่และที่นี่ [[ตอนที่ 2]] เพื่อดูเพิ่มเติมเกี่ยวกับ TM DP โดยทั่วไปและเฉพาะใน Python ซึ่งเป็นอิสระจาก ABCs) .

สำหรับกลศาสตร์พื้นฐานของการสนับสนุนเอบีซีเป็นที่นำเสนอในหลาม 2.6 โปรดดูที่นี่ ; สำหรับรุ่น 3.1 ของพวกเขาคล้ายกันมากดูที่นี่ ในทั้งสองเวอร์ชันคอลเล็กชันโมดูลไลบรารีมาตรฐาน(นั่นคือเวอร์ชัน 3.1 - สำหรับเวอร์ชัน 2.6 ที่คล้ายคลึงกันมากดูที่นี่ ) มีตัวช่วย ABC ที่เป็นประโยชน์มากมาย

สำหรับจุดประสงค์ของคำตอบนี้สิ่งสำคัญที่ต้องจดจำเกี่ยวกับ ABCs (นอกเหนือจากตำแหน่งที่เป็นธรรมชาติมากขึ้นสำหรับการใช้งาน TM DP เปรียบเทียบกับทางเลือก Python คลาสสิกของคลาสMixinเช่นUserDict.DictMixin ) คือพวกมันสร้างisinstance(และissubclass) มากขึ้น น่าดึงดูดและแพร่หลาย (ใน Python 2.6 และก้าวไปข้างหน้า) มากกว่าที่เคยเป็น (ใน 2.5 และก่อนหน้า) ดังนั้นในทางกลับกันการตรวจสอบความเท่าเทียมกันของประเภทเป็นการปฏิบัติที่เลวร้ายยิ่งกว่าใน Python เวอร์ชันล่าสุดกว่าที่เคยเป็นมา


9
'ไม่ใช่ว่าเป็นสิ่งที่ดี แต่คุณก็เป็นสิ่งที่เลวร้ายกว่าการตรวจสอบความเท่าเทียมกันของประเภทต่างๆ วิธีการแก้ปัญหาแบบทั่วไปที่ไพ ธ อนต้องการคือ "การพิมพ์เป็ด" อย่างสม่ำเสมอนี่คือมุมมองที่ค่อนข้าง จำกัด : มีกรณีที่ดีมากสำหรับการใช้ isinstance () ในการพูดล่ามที่ประเภทสะท้อนไวยากรณ์ การเป็น "Pythonic" ไม่ใช่ทุกอย่าง!
ยีนสิทธิชัย

2
basestring ใช้งานไม่ได้ใน Python 3
erobertc

@GeneCallahan เพราะมีกรณีที่ดีมากไม่ได้หมายความว่าสิ่งที่พูดนั้นไม่ใช่กฎทั่วไปที่ดี ฉันยอมรับว่าการตรวจสอบประเภทล่วงหน้ามีสถานที่แน่นอน แต่การปล่อยให้เป็ดต้มตุ๋นควรครอบคลุมกรณีส่วนใหญ่มีความยืดหยุ่นและมีประสิทธิภาพมากขึ้น
Eric Ed Lohmar

@erobertc ตามมีอะไรใหม่ใน Python 3.0 "ประเภทนามธรรมที่เป็นเบสในตัวถูกลบออกใช้ str แทน"
neurite

344

นี่คือตัวอย่างที่isinstanceทำให้ได้สิ่งที่typeไม่สามารถทำได้:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

ในกรณีนี้วัตถุรถบรรทุกเป็นพาหนะ แต่คุณจะได้รับสิ่งนี้:

isinstance(Vehicle(), Vehicle)  # returns True
type(Vehicle()) == Vehicle      # returns True
isinstance(Truck(), Vehicle)    # returns True
type(Truck()) == Vehicle        # returns False, and this probably won't be what you want.

กล่าวอีกนัยหนึ่งisinstanceก็เป็นจริงสำหรับคลาสย่อยเช่นกัน

ดูเพิ่มเติม: วิธีเปรียบเทียบชนิดของวัตถุใน Python อย่างไร


143
เพราะมีกรณีที่คุณไม่ต้องการพฤติกรรม isInstance ฉันจะยืนยันว่าไม่มี "ดีกว่า" พวกเขาทำสิ่งที่แตกต่าง
philgo20

27
-1 เนื่องจาก "isinstance ดีกว่าประเภท" เป็นความคิดเห็นที่หลอกลวง เป็นที่เข้าใจกันว่า " typeเลิกใช้แล้วใช้isinstanceแทน" ได้อย่างรวดเร็วก่อน ตัวอย่างเช่นสิ่งที่ฉันต้องการคือการtype()ตรวจสอบอย่างแน่นอนแต่ฉันถูกเข้าใจผิดในช่วงเวลาสั้น ๆ (และต้องแก้จุดบกพร่องเล็กน้อย) ด้วยเหตุผลนั้น
ceremcem

8
มันเป็นตัวอย่างที่ดีของวิธีการทำงานที่แตกต่างกัน แต่ฉันก็วิ่งเข้าไปในกรณีที่ผมจำเป็นต้องเฉพาะและไม่ได้type() isinstance()หนึ่งไม่ดีกว่า พวกมันต่างกัน
EL_DON

103

ความแตกต่างระหว่างisinstance()และtype()ในงูหลาม?

ประเภทการตรวจสอบด้วย

isinstance(obj, Base)

อนุญาตสำหรับอินสแตนซ์ของคลาสย่อยและหลายฐานที่เป็นไปได้:

isinstance(obj, (Base1, Base2))

ในขณะที่การตรวจสอบประเภทด้วย

type(obj) is Base

สนับสนุนประเภทที่อ้างอิงเท่านั้น


ในฐานะที่เป็น sidenote isมีความเหมาะสมมากกว่า

type(obj) == Base

เพราะคลาสเป็นแบบซิงเกิล

หลีกเลี่ยงการตรวจสอบประเภท - ใช้ความหลากหลาย (การพิมพ์เป็ด)

ใน Python โดยปกติคุณต้องการอนุญาตให้อาร์กิวเมนต์ประเภทใดก็ตามปฏิบัติตามที่คาดไว้และหากวัตถุไม่ทำงานอย่างที่คาดไว้มันจะเพิ่มข้อผิดพลาดที่เหมาะสม สิ่งนี้เรียกว่า polymorphism หรือที่เรียกว่าการพิมพ์แบบเป็ด

def function_of_duck(duck):
    duck.quack()
    duck.swim()

หากโค้ดด้านบนใช้งานได้เราสามารถสันนิษฐานได้ว่าอาร์กิวเมนต์ของเราคือเป็ด ดังนั้นเราสามารถผ่านในสิ่งอื่น ๆ เป็นประเภทย่อยที่แท้จริงของเป็ด:

function_of_duck(mallard)

หรือว่าทำงานเหมือนเป็ด:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

และรหัสของเรายังคงใช้งานได้

อย่างไรก็ตามมีบางกรณีที่ต้องการตรวจสอบประเภทอย่างชัดเจน บางทีคุณอาจมีเหตุผลพอที่จะทำอะไรกับวัตถุชนิดอื่น ตัวอย่างเช่นวัตถุ Pandas Dataframe สามารถสร้างได้จาก dicts หรือบันทึก ในกรณีเช่นนี้รหัสของคุณจำเป็นต้องรู้ว่าอาร์กิวเมนต์ชนิดใดที่ได้รับเพื่อให้สามารถจัดการได้อย่างถูกต้อง

ดังนั้นเพื่อตอบคำถาม:

ความแตกต่างระหว่างisinstance()และtype()ในงูหลาม?

อนุญาตให้ฉันแสดงความแตกต่าง:

type

สมมติว่าคุณต้องการให้แน่ใจว่าพฤติกรรมบางอย่างถ้าฟังก์ชั่นของคุณได้รับการโต้แย้งชนิดหนึ่ง (กรณีใช้งานทั่วไปสำหรับผู้สร้าง) หากคุณตรวจสอบประเภทดังนี้:

def foo(data):
    '''accepts a dict to construct something, string support in future'''
    if type(data) is not dict:
        # we're only going to test for dicts for now
        raise ValueError('only dicts are supported for now')

หากเราพยายามส่งผ่านพจน์ที่เป็น subclass ของdict(อย่างที่เราควรจะทำได้ถ้าเราคาดหวังว่าโค้ดของเราจะเป็นไปตามหลักการของการทดแทน Liskov , subtypes นั้นสามารถถูกแทนที่ด้วยประเภท) การแบ่งรหัสของเรา!:

from collections import OrderedDict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

ทำให้เกิดข้อผิดพลาด!

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in foo
ValueError: argument must be a dict

isinstance

แต่ถ้าเราใช้isinstanceเราสามารถรองรับการทดแทน Liskov!:

def foo(a_dict):
    if not isinstance(a_dict, dict):
        raise ValueError('argument must be a dict')
    return a_dict

foo(OrderedDict([('foo', 'bar'), ('fizz', 'buzz')]))

ผลตอบแทน OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

ชั้นฐานบทคัดย่อ

ในความเป็นจริงเราสามารถทำได้ดียิ่งขึ้น collectionsจัดเตรียมคลาสฐานบทคัดย่อที่บังคับใช้โปรโตคอลน้อยที่สุดสำหรับชนิดต่างๆ ในกรณีของเราหากเราคาดหวังเพียงว่าMappingโปรโตคอลเราสามารถทำสิ่งต่อไปนี้และรหัสของเราจะมีความยืดหยุ่นมากยิ่งขึ้น:

from collections import Mapping

def foo(a_dict):
    if not isinstance(a_dict, Mapping):
        raise ValueError('argument must be a dict')
    return a_dict

การตอบสนองต่อความคิดเห็น:

ควรสังเกตว่าสามารถใช้ชนิดเพื่อตรวจสอบกับหลายคลาสที่ใช้ type(obj) in (A, B, C)

ใช่คุณสามารถทดสอบความเท่าเทียมกันของประเภท แต่แทนที่จะใช้ข้างต้นให้ใช้หลายฐานสำหรับการควบคุมการไหลเว้นแต่ว่าคุณจะอนุญาตเฉพาะประเภทเหล่านั้นเท่านั้น:

isinstance(obj, (A, B, C))

ความแตกต่างอีกครั้งคือisinstanceรองรับ subclasses ที่สามารถทดแทนได้สำหรับผู้ปกครองโดยไม่ทำลายโปรแกรมซึ่งเป็นคุณสมบัติที่เรียกว่าการทดแทน Liskov

ถึงแม้ว่าจะดีกว่าก็ให้ยกเลิกการอ้างอิงของคุณและอย่าตรวจสอบประเภทที่เฉพาะเจาะจงเลย

ข้อสรุป

ดังนั้นเนื่องจากเราต้องการสนับสนุน subclass subclass ดังนั้นในกรณีส่วนใหญ่เราต้องการหลีกเลี่ยงการตรวจสอบประเภทด้วยtypeและต้องการตรวจสอบประเภทด้วยisinstanceยกเว้นคุณต้องการทราบคลาสที่แม่นยำของอินสแตนซ์


หากคุณมี your_module.py ที่ซึ่งคุณตรวจสอบisinstance(instance, y)และใช้งานfrom v.w.x import yและคุณนำเข้าการตรวจสอบนั้น แต่เมื่อคุณสร้างอินสแตนซ์ให้instanceคุณใช้from x import yแทนวิธีการนำเข้า y ใน your_module.py การตรวจสอบ isinstance จะล้มเหลวแม้ว่าจะเป็นคลาสเดียวกันก็ตาม
toonarmycaptain

64

ต้องการหลังเนื่องจากจะจัดการกับคลาสย่อยได้อย่างถูกต้อง ในความเป็นจริงตัวอย่างของคุณสามารถเขียนได้ง่ายขึ้นเพราะisinstance()พารามิเตอร์ตัวที่สองอาจเป็นสิ่งอันดับ:

if isinstance(b, (str, unicode)):
    do_something_else()

หรือใช้basestringคลาสนามธรรม:

if isinstance(b, basestring):
    do_something_else()

15

ตามเอกสารของไพ ธ อนนี่คือข้อความ:

8.15 types - ชื่อสำหรับประเภทในตัว

เริ่มต้นใน Python 2.2 ฟังก์ชั่นจากโรงงานในตัวเช่นint()และ str()ยังเป็นชื่อสำหรับประเภทที่เกี่ยวข้อง

ดังนั้นควรจะต้องการมากกว่าisinstance()type()


9

ความแตกต่างในการใช้งานจริงคือวิธีจัดการbooleans:

TrueและFalseเป็นเพียงคำหลักที่มีความหมาย1และเป็น0งูหลาม ดังนั้น,

isinstance(True, int)

และ

isinstance(False, int)

Trueทั้งสองกลับมา บูลีนทั้งสองเป็นตัวอย่างของจำนวนเต็ม type()อย่างไรก็ตามมีความฉลาดกว่า:

type(True) == int

Falseผลตอบแทน


0

สำหรับความแตกต่างที่จริงเราสามารถหาได้ในแต่ฉันไม่สามารถหาการดำเนินการของการทำงานเริ่มต้นของcodeisinstance()

แต่เราจะได้รับหนึ่งที่คล้ายกันabc .__ instancecheck__ตาม__instancecheck__

จากด้านบนabc.__instancecheck__หลังจากใช้การทดสอบด้านล่าง:

# file tree
# /test/__init__.py
# /test/aaa/__init__.py
# /test/aaa/aa.py
class b():
pass

# /test/aaa/a.py
import sys
sys.path.append('/test')

from aaa.aa import b
from aa import b as c

d = b()

print(b, c, d.__class__)
for i in [b, c, object]:
    print(i, '__subclasses__',  i.__subclasses__())
    print(i, '__mro__', i.__mro__)
    print(i, '__subclasshook__', i.__subclasshook__(d.__class__))
    print(i, '__subclasshook__', i.__subclasshook__(type(d)))
print(isinstance(d, b))
print(isinstance(d, c))

<class 'aaa.aa.b'> <class 'aa.b'> <class 'aaa.aa.b'>
<class 'aaa.aa.b'> __subclasses__ []
<class 'aaa.aa.b'> __mro__ (<class 'aaa.aa.b'>, <class 'object'>)
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aaa.aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasses__ []
<class 'aa.b'> __mro__ (<class 'aa.b'>, <class 'object'>)
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'aa.b'> __subclasshook__ NotImplemented
<class 'object'> __subclasses__ [..., <class 'aaa.aa.b'>, <class 'aa.b'>]
<class 'object'> __mro__ (<class 'object'>,)
<class 'object'> __subclasshook__ NotImplemented
<class 'object'> __subclasshook__ NotImplemented
True
False

ฉันได้รับข้อสรุปนี้สำหรับtype:

# according to `abc.__instancecheck__`, they are maybe different! I have not found negative one 
type(INSTANCE) ~= INSTANCE.__class__
type(CLASS) ~= CLASS.__class__

สำหรับisinstance:

# guess from `abc.__instancecheck__`
return any(c in cls.__mro__ or c in cls.__subclasses__ or cls.__subclasshook__(c) for c in {INSTANCE.__class__, type(INSTANCE)})

BTW: ดีกว่าที่จะไม่ใช้ผสมrelative and absolutely importใช้absolutely importจาก project_dir (เพิ่มโดยsys.path)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.