Вопрос-Ответ

What are the differences between type() and isinstance()? [duplicate]

В чем разница между 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()
Переведено автоматически
Ответ 1

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

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

Дело не в том, что это isinstance хорошо, имейте в виду — это просто менее плохо, чем проверка равенства типов. Обычным, питоновским, предпочтительным решением почти всегда является "утиный ввод": попробуйте использовать аргумент, как если бы он был определенного желаемого типа, сделайте это в операторе 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, поскольку PEP 3119, который представляет ее обобщение, был принят и реализован, начиная с Python 2.6 и 3.0.

PEP дает понять, что, хотя ABCs часто может заменить утиный ввод, как правило, нет большого давления для этого (см. Здесь ). Однако 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 в виде смешанных классов, таких как UserDict .DictMixin) заключается в том, что они делают isinstanceissubclass) намного более привлекательными и распространенными (в Python 2.6 и в дальнейшем), чем они были раньше (в 2.5 и ранее), и, следовательно, напротив, делают проверку равенства типов еще худшей практикой в последних версиях Python, чем это уже было раньше.

Ответ 2

Вот пример, в котором isinstance достигается то, что type невозможно:

class Vehicle:
pass

class Truck(Vehicle):
pass

В этом случае Truck объектом является Vehicle, но вы получите следующее:

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

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


В качестве дополнительного примечания, is вероятно, более уместно, чем

type(obj) == Base

потому что классы являются одиночными.

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

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

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

Если приведенный выше код работает, мы можем предположить, что нашим аргументом является duck . Таким образом, мы можем передать другие вещи, являющиеся реальными подтипами duck:

function_of_duck(mallard)

или это работает как утка:

function_of_duck(object_that_quacks_and_swims_like_a_duck)

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

Однако в некоторых случаях желательно явно проверять тип. Возможно, у вас есть разумные способы работы с разными типами объектов. Например, объект фрейма данных Pandas может быть сконструирован из 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 можно использовать для проверки на соответствие нескольким классам с помощью 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()
python