Каковы различия между type () и isinstance ()? - PullRequest
1106 голосов
/ 11 октября 2009

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

Ответы [ 7 ]

1153 голосов
/ 11 октября 2009

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

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

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

basestring - это , однако, это особый случай - встроенный тип, который существует только , позволяющий использовать 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.

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

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

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

308 голосов
/ 11 октября 2009

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

84 голосов
/ 06 февраля 2015

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

Проверка типов с помощью

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

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

type(obj) is Base

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


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

59 голосов
/ 11 октября 2009

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

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

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

if isinstance(b, basestring):
    do_something_else()
12 голосов
/ 11 октября 2009

В соответствии с документацией на Python есть заявление:

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

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

То есть isinstance() следует отдавать предпочтение перед type().

0 голосов
/ 25 мая 2019

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

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

isinstance(True, int)

и

isinstance(False, int)

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

type(True) == int

возвращает False.

0 голосов
/ 10 мая 2018

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...