Sự khác biệt giữa loại () và isinstance () là gì?


1248

Sự khác biệt giữa hai đoạn mã này là gì?

Sử dụng type():

import types

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

Sử dụng isinstance():

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

Lưu ý: Nếu không phải strunicode(nơi bạn chỉ có thể kiểm tra basestring), bạn có thể sử dụng bộ dữ liệu để kiểm tra đối với nhiều loại. Để kiểm tra nếu somethinginthoặc strsử dụng isinstance(something, (int, str)).
xuiqzy

Câu trả lời:


1271

Để tóm tắt nội dung của các câu trả lời khác (đã tốt!), isinstancePhục vụ cho kế thừa (một thể hiện của lớp dẫn xuất cũng là một thể hiện của lớp cơ sở), trong khi kiểm tra typetính bằng nhau của nó (nó yêu cầu nhận dạng các loại và từ chối các thể hiện của các kiểu con, các lớp con AKA).

Thông thường, trong Python, bạn muốn mã của mình hỗ trợ kế thừa, tất nhiên (vì tính kế thừa rất tiện dụng, sẽ rất tệ nếu bạn không sử dụng mã của mình để sử dụng nó!), Vì vậy isinstancesẽ ít tệ hơn so với việc kiểm tra danh tính của types vì nó hỗ trợ liền mạch di sản.

Nó không phải là isinstancetốt , quan tâm bạn, nó chỉ là ít xấu hơn kiểm tra bình đẳng giữa các loại. Giải pháp ưa thích, bình thường của Pythonic gần như là "gõ vịt": thử sử dụng đối số như thể nó thuộc loại mong muốn nhất định, thực hiện trong một câu lệnh try/ exceptbắt tất cả các ngoại lệ có thể phát sinh nếu thực tế không phải là đối số gõ (hoặc bất kỳ loại nào khác độc đáo bắt chước nó ;-) và trong exceptmệnh đề, hãy thử một cái gì đó khác (sử dụng đối số "như thể" nó thuộc loại khác).

basestring , tuy nhiên, khá một trường một kiểu dựng sẵn đặc biệt mà tồn tại chỉ để cho bạn sử dụng isinstance(cả hai strunicodelớp con basestring). Các chuỗi là các chuỗi (bạn có thể lặp qua chúng, lập chỉ mục cho chúng, cắt chúng, ...), nhưng bạn thường muốn coi chúng là các kiểu "vô hướng", điều đó hơi khó hiểu (nhưng là trường hợp sử dụng thường xuyên) để xử lý tất cả các loại một chuỗi (và có thể các loại vô hướng khác, tức là các chuỗi bạn không thể lặp) theo một cách, tất cả các thùng chứa (danh sách, bộ, dicts, ...) theo cách khác, và basestringcộng với isinstancegiúp bạn thực hiện điều đó cấu trúc tổng thể của điều này thành ngữ là một cái gì đó như:

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

Bạn có thể nói đó basestringLớp cơ sở trừu tượng ("ABC") - nó không cung cấp chức năng cụ thể cho các lớp con, mà tồn tại dưới dạng "điểm đánh dấu", chủ yếu để sử dụng isinstance. Khái niệm này rõ ràng là một khái niệm đang phát triển trong Python, vì PEP 3119 , giới thiệu khái quát hóa về nó, đã được chấp nhận và đã được triển khai bắt đầu với Python 2.6 và 3.0.

PEP làm rõ rằng, trong khi ABC thường có thể thay thế cho việc gõ vịt, nhìn chung không có áp lực lớn để làm điều đó (xem tại đây ). Tuy nhiên, các ABC được triển khai trong các phiên bản Python gần đây cung cấp thêm các ưu điểm: isinstance(và issubclass) giờ đây có thể có ý nghĩa nhiều hơn là "[một thể hiện] một lớp dẫn xuất" (đặc biệt, bất kỳ lớp nào cũng có thể được "đăng ký" với ABC để nó sẽ hiển thị dưới dạng một lớp con và các thể hiện của nó như các thể hiện của ABC); và ABC cũng có thể cung cấp thêm sự tiện lợi cho các lớp con thực tế theo cách rất tự nhiên thông qua các ứng dụng mẫu thiết kế Phương thức mẫu (xem tại đâytại đây [[phần II]] để biết thêm về TM DP, nói chung và cụ thể trong Python, độc lập với ABC) .

Để biết các cơ chế cơ bản của hỗ trợ ABC như được cung cấp trong Python 2.6, xem tại đây ; Đối với phiên bản 3.1 của họ, rất giống nhau, xem tại đây . Trong cả hai phiên bản, các bộ sưu tập mô-đun thư viện tiêu chuẩn (đó là phiên bản 3.1 cho phiên bản 2.6 rất giống nhau, xem tại đây ) cung cấp một số ABC hữu ích.

Với mục đích của câu trả lời này, điều cốt yếu để giữ lại các ABC (ngoài vị trí tự nhiên hơn đối với chức năng TM DP, so với thay thế Python cổ điển của các lớp mixin như UserDict.DictMixin ) là chúng tạo ra isinstance(và issubclass) nhiều hơn hấp dẫn và có sức lan tỏa (trong Python 2.6 và tiếp theo) so với trước đây (trong 2.5 và trước đó), và do đó, ngược lại, làm cho việc kiểm tra sự bình đẳng loại trở thành một thực tiễn thậm chí còn tồi tệ hơn trong các phiên bản Python gần đây so với trước đây.


9
'Không phải điều đó là tốt, hãy nhớ rằng bạn chỉ kém về việc kiểm tra sự bình đẳng của các loại. Giải pháp ưa thích, bình thường của Pythonic gần như là "gõ vịt", đây là một quan điểm khá hạn chế: có những trường hợp rất tốt để sử dụng isinstance (), giả sử, một trình thông dịch trong đó các loại phản ánh ngữ pháp. Trở thành "Pythonic" không phải là tất cả!
Gene Callahan

2
basestring không có sẵn trong Python 3.
erobertc

@GeneCallahan, vì có những trường hợp rất tốt, không có nghĩa là những gì được nói không phải là một quy tắc chung tốt. Tôi đồng ý rằng việc kiểm tra loại trước thời hạn chắc chắn có vị trí của nó, nhưng để cho vịt quạc nên bao gồm hầu hết các trường hợp linh hoạt và hiệu quả hơn.
Eric Ed Lohmar

@erobertc, theo What New trong Python 3.0 , "Loại trừu tượng cơ bản tích hợp đã bị xóa. Thay vào đó, hãy sử dụng str."
thần kinh

345

Đây là một ví dụ trong đó isinstanceđạt được một cái gì đó typekhông thể:

class Vehicle:
    pass

class Truck(Vehicle):
    pass

trong trường hợp này, một đối tượng xe tải là một Phương tiện, nhưng bạn sẽ nhận được điều này:

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.

Nói cách khác, isinstancecũng đúng cho các lớp con.

Xem thêm: Làm thế nào để so sánh loại đối tượng trong Python?


143
bởi vì có trường hợp bạn không muốn hành vi isstance tôi sẽ lập luận rằng không có "tốt hơn". Họ chỉ làm một cái gì đó khác nhau.
philgo20

27
-1, bởi vì "isinstance is than than type" là một nhận xét sai lệch. nó được hiểu như " typekhông được dùng nữa, sử dụng isinstancethay thế" ngay từ cái nhìn đầu tiên. ví dụ, những gì tôi muốn là type()kiểm tra chính xác , nhưng tôi đã bị nhầm lẫn trong một thời gian ngắn (và phải gỡ lỗi một chút) vì lý do đó.
ceremcem 30/05/2015

8
Đó là một ví dụ tốt về cách họ làm việc khác nhau, nhưng tôi chỉ gặp phải trường hợp tôi đặc biệt cần type()và không isinstance(). Một là không tốt hơn; chúng dành cho những thứ khác nhau
EL_DON

103

Sự khác nhau giữa isinstance()type()trong Python?

Kiểm tra loại với

isinstance(obj, Base)

cho phép các thể hiện của các lớp con và nhiều cơ sở có thể:

isinstance(obj, (Base1, Base2))

trong khi kiểm tra kiểu với

type(obj) is Base

chỉ hỗ trợ các loại tham chiếu.


Là một sidenote, iscó khả năng thích hợp hơn

type(obj) == Base

bởi vì các lớp học là singletons.

Tránh kiểm tra kiểu - sử dụng Đa hình (gõ vịt)

Trong Python, thông thường bạn muốn cho phép bất kỳ loại nào cho các đối số của mình, xử lý nó như mong đợi và nếu đối tượng không hoạt động như mong đợi, nó sẽ đưa ra một lỗi thích hợp. Điều này được gọi là đa hình, còn được gọi là gõ vịt.

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

Nếu đoạn mã trên hoạt động, chúng ta có thể đoán rằng đối số của chúng ta là một con vịt. Vì vậy, chúng ta có thể vượt qua trong những thứ khác là các loại vịt thực tế:

function_of_duck(mallard)

hoặc hoạt động như một con vịt:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

và mã của chúng tôi vẫn hoạt động.

Tuy nhiên, có một số trường hợp mong muốn kiểm tra kiểu rõ ràng. Có lẽ bạn có những điều hợp lý để làm với các loại đối tượng khác nhau. Ví dụ, đối tượng Pandas Dataframe có thể được xây dựng từ các bản ghi hoặc bản ghi. Trong trường hợp như vậy, mã của bạn cần biết loại đối số nào đang nhận để nó có thể xử lý đúng.

Vì vậy, để trả lời câu hỏi:

Sự khác nhau giữa isinstance()type()trong Python?

Cho phép tôi chứng minh sự khác biệt:

type

Giả sử bạn cần đảm bảo một hành vi nhất định nếu hàm của bạn có một loại đối số nhất định (trường hợp sử dụng chung cho các hàm tạo). Nếu bạn kiểm tra loại như thế này:

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')

Nếu chúng ta cố gắng vượt qua trong một dict là một lớp con của dict(như chúng ta có thể, nếu chúng ta hy vọng mã của chúng ta tuân theo nguyên tắc Thay thế Liskov , các kiểu con có thể được thay thế cho các loại) thì mã của chúng ta bị phá vỡ!:

from collections import OrderedDict

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

gây ra một lỗi!

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

isinstance

Nhưng nếu chúng tôi sử dụng isinstance, chúng tôi có thể hỗ trợ Thay thế 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')]))

trả lại OrderedDict([('foo', 'bar'), ('fizz', 'buzz')])

Các lớp cơ sở trừu tượng

Trong thực tế, chúng ta có thể làm tốt hơn nữa. collectionscung cấp các lớp cơ sở trừu tượng thực thi các giao thức tối thiểu cho các loại khác nhau. Trong trường hợp của chúng tôi, nếu chúng tôi chỉ mong đợi Mappinggiao thức, chúng tôi có thể thực hiện các thao tác sau và mã của chúng tôi thậm chí còn linh hoạt hơn:

from collections import Mapping

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

Trả lời bình luận:

Cần lưu ý rằng loại có thể được sử dụng để kiểm tra đối với nhiều lớp bằng cách sử dụng type(obj) in (A, B, C)

Có, bạn có thể kiểm tra sự bằng nhau của các loại, nhưng thay vì ở trên, hãy sử dụng nhiều cơ sở cho luồng điều khiển, trừ khi bạn chỉ cho phép các loại đó:

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

Sự khác biệt, một lần nữa, là isinstancehỗ trợ các lớp con có thể được thay thế cho cha mẹ mà không phá vỡ chương trình, một thuộc tính được gọi là thay thế Liskov.

Tuy nhiên, thậm chí tốt hơn, đảo ngược sự phụ thuộc của bạn và không kiểm tra các loại cụ thể nào cả.

Phần kết luận

Vì vậy, vì chúng tôi muốn hỗ trợ thay thế các lớp con, trong hầu hết các trường hợp, chúng tôi muốn tránh kiểm tra typekiểu và thích kiểm tra kiểu hơn isinstance- trừ khi bạn thực sự cần biết chính xác về một thể hiện.


Nếu bạn có your_module.py nơi bạn kiểm tra isinstance(instance, y)và sử dụng from v.w.x import yvà bạn nhập kiểm tra đó, nhưng khi bạn khởi tạo instancebạn sử dụng from x import ythay vì cách bạn đã nhập trong your_module.py, kiểm tra isinstance sẽ thất bại, mặc dù đó là cùng một lớp.
toonarmycaptain

64

Cái sau được ưa thích, bởi vì nó sẽ xử lý các lớp con đúng cách. Trong thực tế, ví dụ của bạn có thể được viết thậm chí dễ dàng hơn bởi vì isinstance()tham số thứ hai có thể là một tuple:

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

hoặc, sử dụng basestringlớp trừu tượng:

if isinstance(b, basestring):
    do_something_else()


9

Một sự khác biệt sử dụng thực tế là cách họ xử lý booleans:

TrueFalsechỉ là những từ khóa có nghĩa 10trong python. Như vậy

isinstance(True, int)

isinstance(False, int)

Cả hai trở về True. Cả hai booleans là một thể hiện của một số nguyên. type()tuy nhiên, thông minh hơn:

type(True) == int

trả lại False.


0

Đối với sự khác biệt thực sự, chúng ta có thể tìm thấy nó code, nhưng tôi không thể tìm thấy việc thực hiện hành vi mặc định của isinstance().

Tuy nhiên, chúng ta có thể nhận được một abc tương tự .__ instancecheck__ theo __instancecheck__ .

Từ trên abc.__instancecheck__, sau khi sử dụng thử nghiệm dưới đây:

# 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

Tôi nhận được kết luận này, cho type:

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

Dành cho 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: tốt hơn không trộn lẫn sử dụng relative and absolutely import, sử dụng absolutely importtừ project_dir (được thêm bởi sys.path)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.