Каковы различия между типами() и 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()

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

Вот пример, где 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?

Ответ 3

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

Проверка типа с

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

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

type(obj) is Base

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


В качестве sidenote, is, вероятно, более уместно, чем

type(obj) == Base

потому что классы синглтоны.

Избегайте проверки типов - используйте Polymorphism (duck-typing)

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

и наш код все еще работает.

Однако в некоторых случаях желательно явно проверять тип. Возможно, вы имеете дело с разными типами объектов. Например, объект Dataframe Pandas может быть построен из диктовок или записей. В таком случае ваш код должен знать, какой тип аргумента он получает, чтобы он мог правильно с ним справиться.

Итак, чтобы ответить на вопрос:

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

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

Заключение

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

Ответ 4

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

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

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

if isinstance(b, basestring):
    do_something_else()

Ответ 5

В соответствии с документацией на python вот инструкция:

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

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

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

Ответ 6

Для реальных отличий мы можем найти это в 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)})

Кстати: лучше не смешивать использовать relative and absolutely import, используйте absolutely import из project_dir (добавлено sys.path)

Ответ 7

Разница в практическом использовании заключается в том, как они обрабатывают booleans:

True и False - это просто ключевые слова, которые означают 1 и 0 в Python. Таким образом,

isinstance(True, int)

а также

isinstance(False, int)

оба возвращают True. Оба логических значения являются экземпляром целого числа. type(), однако, является более умным:

type(True) == int

возвращает False