Каковы различия между type () и isinstance ()?

каковы различия между этими двумя фрагментами кода? Используя 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()

6 ответов


суммировать содержание других (уже хорошо!) ответы, isinstance обслуживает наследование (экземпляр производного класса это экземпляр базового класса тоже), при проверке на равенство type не делает (он требует идентичности типов и отвергает экземпляры подтипов, он же подклассы).

обычно, в Python, вы хотите, чтобы ваш код поддерживал наследование, конечно (поскольку наследование так удобно, было бы плохо остановить код, используя ваш это!), так isinstance менее плохо, чем проверка идентичности types, потому что он легко поддерживает наследование.

дело не в этом isinstance is хороший, имейте в виду-это просто меньше чем проверка равенства типов. Нормальные, подходящие для Python, предпочтительное решение-это почти всегда "утиной типизацией": попробуйте использовать аргумент как будто он был определенного желаемого типа, сделать это в try/except оператор ловит все исключения, которые могут возникнуть если аргумент не был на самом деле этого типа (или любого другого типа, хорошо имитирующего его; -), а в except предложение, попробуйте что-то еще (используя аргумент "как будто", он был другого типа).

basestring is, однако, совершенно особый случай-встроенный тип, который существует только можно использовать isinstance (как str и unicode подкласс basestring). Строки-это последовательности (вы можете перебирать их, индексировать, срезать ...), но вы вообще хотите рассматривать их как" скалярные " типы-это несколько нецелесообразно (но достаточно частый случай использования) для обработки всех видов строк (и, возможно, других скалярных типов, т. е. тех, на которых вы не можете зацикливаться) в одну сторону, все контейнеры (списки, наборы, дикты,...) по-другому, и basestring плюс isinstance помогает вам сделать это-общая структура этой идиомы что-то вроде:

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

можно сказать, что basestring это Абстрактный Базовый Класс ("ABC") - он не предлагает никакой бетон функциональность для подклассов, а скорее существует как "маркер", в основном для использования с isinstance. Концепция, очевидно, растет в Python, так как PEP 3119, который вводит его обобщение, был принят и был реализован, начиная с Python 2.6 и 3.0.

ОПТОСОЗ дает понять, что, хотя Азбука часто может заменить типирование утки, как правило, нет большого давления, чтобы сделать это (см. здесь). ABCs, реализованный в недавнем Python однако версии предлагают дополнительные плюсы: isinstanceissubclass) теперь может означать больше, чем просто "[экземпляр] производного класса" (в частности, любой класс может быть "зарегистрирован" с помощью ABC, так что он будет отображаться как подкласс, а его экземпляры-как экземпляры ABC); и ABCs также может предложить дополнительное удобство для фактических подклассов очень естественным образом с помощью приложений шаблонов шаблонов (см. здесь и здесь [[Часть II]] для получения дополнительной информации о TM DP, в общие и конкретно в Python, независимо от ABCs).

для базовой механики поддержки ABC, предлагаемой в Python 2.6, см. здесь; для их версии 3.1, очень похожей, см. здесь. В обеих версиях стандартный библиотечный модуль коллекции (это версия 3.1-для очень похожей версии 2.6 см. здесь) предлагает несколько полезных АБ.

для целей настоящего ответа, главное сохранить о ABCs (помимо, возможно, более естественного размещения для функциональности TM DP, по сравнению с классической альтернативой Python классов mixin, таких как UserDict.DictMixin) является то, что они делают isinstanceissubclass) гораздо более привлекательными и распространенными (в Python 2.6 и в будущем), чем раньше (в 2.5 и ранее), и поэтому, напротив, проверка равенства типов еще хуже практикуется в последних версиях Python, чем это уже было.


вот почему 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?


различия между isinstance() и type() в Python?

Type-проверка с

isinstance(obj, Base)

позволяет для экземпляров подклассов и нескольких возможных баз:

isinstance(obj, (Base1, Base2))

тогда как проверка типа с

type(obj) is Base

поддерживает только указанный тип.


как sidenote,is, вероятно, более подходит, чем

type(obj) == Base

потому что занятия одиночки.

избегайте проверки типа - используйте полиморфизм (утиный тип)

в Python обычно вы хотите разрешить любой тип для своих аргументов, относиться к нему, как ожидалось, и если объект не ведет себя так, как ожидалось, он вызовет соответствующую ошибку. Это известно как полиморфизм, также известный, как утка-типирование.

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() в Python?

позвольте мне продемонстрировать разницу:

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

если мы попытаемся передать в дикт, который является подклассом dict (как мы должны быть в состоянии, если мы ожидаем, что наш код будет следовать принципу Лисков Замены, что подтипы могут быть заменены типами) наш код ломается!:

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, мы можем поддержать подстановки Лисков!:

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 поддерживает подклассы, которые могут быть заменены для родителя без нарушения программы, свойство, известное как подстановка Лисков.

еще лучше, однако, инвертировать зависимости и не проверять наличие определенных типов вообще.

вывод

так как мы хотим поддержать замену подклассов, в большинстве случаев мы хотите избежать проверки типа с помощью type и предпочитайте проверку типа с isinstance - если вам действительно нужно знать точный класс экземпляра.


последнее предпочтительнее, потому что оно будет обрабатывать подклассы должным образом. На самом деле, ваш пример может быть написан еще проще, потому что isinstance()вторым параметром может быть кортеж:

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

или, используя basestring абстрактный класс:

if isinstance(b, basestring):
    do_something_else()

согласно документации python вот заявление:

8.15. types-имена встроенных типов

начиная с Python 2.2, встроенный заводские функции, такие как int() и str() также имена соответствующий тип.

так isinstance() должно быть предпочтительнее type().


для реальных различий мы можем найти его в code, но я не могу найти реализацию по умолчанию isinstance().

однако мы можем получить аналогичный 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)