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