т. (383) 381-86-26

Блог о создании вебсайтов

 

Python: в чем разница между type и isinstance

 

Действительно, а чем они отличаются. Казалось бы и type, и isinstance с первого взгляда делают одну и ту же работу. Но так ли это на самом деле? На StackOverflow было обсуждение этого вопроса, но как мне кажется пример, на котором разбирали различия между этими функциями, был выбран неудачно. Рассмотрим такой код:

>>> import types
>>> class Klass(object):
>>>    ...
>>> k = Klass()
>>> isinstance(k, Klass)
True
>>> type(k) == type(Klass)
False

Если внимательно посмотреть на этот код, то различия в принципе очевидны: type в данном контексте (о том как ещё можно магически использовать type в другой раз расскажем) выдает <тип> тип своего аргумента, а isinstance проверяет иерархию (является ли k наследником Кlass). Это и есть основное различие между ними.

Самые въедливые сейчас возразят:"Ну как же! Ведь k в примере произведен от Klass, значит и тип у них должен быть один и тот же". М-м-м-м, может быть, но не в питонячей галактике. И я легко это докажу:

>>> type(k) == type(Klass)
False
>>> type(k) == Klass
True
>>> Klass

>>> k
<__main__.Klass object at 0xb7205eac>

Как мы видим <тип> k действительно <Klass>, а вот <тип> Klass есть <type>. Причина этого кроется во внутреннем строении системы объектов и типов Python (о ней следует говорить много и последовательно, так что в другой раз)

Нормальный питонячий подход для проверки типа — это вообще не проверять его, а использовать duck typing: работать с объектом так, как если бы он действительно был объектом ожидаемого типа. Обернуть это в try/except, отлавливая ошибки которые могут возникнуть, если данный объект будет неверного типа.

Как и в любом правиле, в этом также есть исключения. К примеру тип basestring. Его не существует в чистом виде, он нужен только для организации строковой иерархии. В каком-то смысле basestring — это абстрактный базовый класс (Abstract Base Class, PEP 3119) и по большей части он нужен в основном для того, чтобы правильно работала isinstance

Концепция абстрактных базовых классов в Питоне набирала силу и вылилась в PEP 3119. Как следует из этого PEP abc может выступать заменой duck typing:

Does the introduction of ABCs mean the end of Duck Typing? I don't think so. Python will not require that a class derives from BasicMapping or Sequence when it defines a __getitem__ method, nor will the x[y] syntax require that x is an instance of either ABC. You will still be able to assign any "file-like" object to sys.stdout, as long as it has a write method.

Of course, there will be some carrots to encourage users to derive from the appropriate base classes; these vary from default implementations for certain functionality to an improved ability to distinguish between mappings and sequences. But there are no sticks. If hasattr(x, "__len__") works for you, great! ABCs are intended to solve problems that don't have a good solution at all in Python 2, such as distinguishing between mappings and sequences.

Однако, язык не будет насильно заставлять пользоваться abc, хотя их использование и может дать некоторые дополнительные плюшки. В общем, не буду дальше лезть в дебри и вернусь к изначальной теме разницы между type и isinstance и сделаю убийственно очевидный вывод, что это не одно и тоже и что проверять равенство типов объектов задача более деликатная, чем кажется изначально. А благодаря появлению ABC она становится еще более деликатной.

В качестве послесловия

В следующий раз я надеюсь написать подробней о некоторых особенностях внутренней механики Python, о которых упомянул ранее. И да пребудет с тобой сила, Люк.

Подпишитесь на рассылку, будет интересно!