python typing - Каковы различия между типами()и isinstance()?




ignore comments (6)

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

Answers

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


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


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

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

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

basestring - это , однако, особый случай - встроенный тип, который существует только для того, чтобы вы могли использовать isinstance (как isinstance str и unicode ). Строки - это последовательности (вы можете их переплетать, индексировать, нарезать их ...), но вы обычно хотите относиться к ним как к «скалярным» типам - это несколько неудобно (но достаточно частое использование) для лечения всех видов строки (и, возможно, другие скалярные типы, т. е. те, которые вы не можете использовать) в одном направлении, все контейнеры (списки, наборы, 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 дает понять, что, хотя ABC часто заменяют утиную печать, обычно нет большого давления для этого (см. here ). Однако ABC, реализованные в последних версиях Python, предлагают дополнительные isinstance : isinstanceissubclass ) теперь может означать больше, чем просто «[экземпляр] производного класса» (в частности, любой класс может быть «зарегистрирован» с ABC, так что он будет отображаться как подкласс и его экземпляры как экземпляры ABC); и ABC также могут предложить дополнительное удобство для фактических подклассов очень естественным образом с помощью шаблонов шаблонов (см. here и here [[часть II]] для получения дополнительной информации о TM DP, в общем и конкретно в Python, независимо от ABC) ,

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

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


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

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

isinstance(obj, Base)

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

isinstance(obj, (Base1, Base2))

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

type(obj) is Base

поддерживает только тип.

Как побочный элемент, вероятно, более уместен, чем

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 (как мы сможем, если мы ожидаем, что наш код будет следовать принципу Liskov Substitution , подтипы могут быть заменены типами), наш код прерывается !:

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 Substitution !:

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

Тем не менее, даже лучше инвертируйте свои зависимости и не проверяйте конкретные типы.

Заключение

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


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

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

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

if isinstance(b, basestring):
    do_something_else()

Одно использование для метаклассов - это добавление новых свойств и методов в экземпляр автоматически.

Например, если вы посмотрите на модели Django , их определение выглядит немного запутанным. Похоже, что вы определяете только свойства класса:

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

Однако во время выполнения объекты Person заполняются всякими полезными методами. Посмотрите source какой-то удивительной метаклассификации.





python oop inheritance types