ความแตกต่างระหว่าง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
ยกเว้นคุณต้องการทราบคลาสที่แม่นยำของอินสแตนซ์
str
และunicode
(ซึ่งคุณสามารถตรวจสอบได้basestring
) คุณสามารถใช้ tuple เพื่อตรวจสอบกับหลายประเภท เพื่อตรวจสอบว่าsomething
มีint
หรือใช้str
isinstance(something, (int, str))