В чем различия между этими двумя фрагментами кода?
Использование 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()
Подводя итог содержанию других (уже хорошо!) отвечает, isinstance
обслуживает наследование (экземпляр производного класса is an экземпляр базового класса тоже), в то время как проверка равенства type
не делает (он требует идентичности типов и отклоняет экземпляры подтипов, AKA подклассы).
Обычно в Python вы хотите, чтобы ваш код поддерживал наследование, конечно (поскольку наследование очень удобно, было бы плохо запретить использование кода вашим!), поэтому «существо» менее плохо, чем проверка идентичности «типов», потому что оно плавно поддерживает наследование.
Дело не в том, что «существо» - это good, заметьте, это просто less bad, чем проверка равенства типов. Нормальный, Pythonic, предпочтительное решение почти всегда &"утка печатает &": попробуйте использовать аргумент as if определенного желаемого типа, сделайте это в try
/ excl" утверждении всех исключений, которые могут возникнуть, если аргумент фактически не относится к этому типу (или любой другой тип, красиво имитирующий утку;-) и в пункте
кроме`, попробуй что-нибудь еще (используя аргумент &"как будто &" это был какой-то другой тип).
basestring
- это , однако, совершенно особый случай - встроенный тип, который существует только , чтобы позволить вам использовать isinstance
(подкласс str
и unicode
basestring
). Строки - это последовательности (вы можете зацикливаться на них, индексировать их, нарезать их, ...), но вы обычно хотите относиться к ним как к «скалярным» типам - это несколько неудобно (но довольно частое использование) обрабатывать все виды строк (и, возможно, другие скалярные типы, т.е.те, которые вы не можете зацикливать) в одну сторону, все контейнеры (списки, наборы, дикты, ...) по-другому, и basestring
plus isinstance
помогает вам сделать это - общая структура этой идиомы примерно равна:
if isinstance(x, basestring)
return treatasscalar(x)
try:
return treatasiter(iter(x))
except TypeError:
return treatasscalar(x)
Можно сказать, что basestring
- это Abstract Base Class («ABC») - он не предлагает конкретной функциональности для подклассов, а скорее существует как «маркер», в основном для использования с «isinstance». Концепция, очевидно, растет в Python, поскольку PEP 3119, который вводит его обобщение, был принят и реализован начиная с Python 2.6 и 3.0.
ПКП ясно дает понять, что, хотя ABC часто могут заменить набор текста утки, обычно нет большого давления для этого (см. Здесь). ABC, реализованные в последних версиях Python, тем не менее, предлагают дополнительные вкусности: «существо» (и issubclass
) теперь может означать больше, чем просто &"экземпляр] производный класс &" ,(в частности, любой класс может быть &"зарегистрировано &" с ABC, чтобы он показывался как подкласс, и его экземпляры как экземпляры ABC) и ABC также могут предложить дополнительное удобство для реальных подклассов очень естественным образом с помощью приложений шаблонов проектирования метода шаблонов (видеть [Вот]3] а также [Вот]4] ,[часть II] для большего на ТМ ДП, в целом и конкретно на Python, независимый от азбуки).
Базовая механика поддержки ABC, предлагаемая в Python 2.6, см. В здесь; их версия 3.1, очень похожая, см. здесь. В обеих версиях стандартный модуль библиотеки коллекции (это версия 3.1 - для очень похожей версии 2.6 см. Здесь) предлагает несколько полезных ABC.
Для целей этого ответа, ключевая вещь, чтобы сохранить о ABC (за пределами, возможно, более естественного размещения для функциональности TM DP, по сравнению с классической альтернативой Python для классов микшинов, таких как [UserDict.DictMixin]9] в том, что они делают "существо" (и issubclass
) гораздо привлекательнее и распространеннее (в Python 2.6 и в будущем) чем раньше (в 2,5 и раньше) и поэтому, напротив, сделать проверку равенства типов еще худшей практикой в последних версиях Python, чем это было раньше.
[9]: http://docs.python.org/library/userdict.html?highlight = userdict # UserDict.DictMixin
Вот пример, где 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.
Другими словами, «существо» верно и для подклассов.
Также см .: < https://stackoverflow.com/q/707674/1341006>
Различия между
isinstance ()
иtype ()
в Python?
Проверка типа с
isinstance(obj, Base)
допускает экземпляры подклассов и несколько возможных оснований:
isinstance(obj, (Base1, Base2))
тогда как проверка типа с
type(obj) is Base
поддерживает только тип ссылки.
Как sidenote, 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
Но если мы используем «существо», мы можем поддержать Лисковскую замену!:
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» - если вам действительно не нужно знать точный класс экземпляра.
Последний является предпочтительным, потому что он будет обрабатывать подклассы должным образом. Фактически, ваш пример можно написать еще проще, потому что вторым параметром isinstance ()
может быть кортеж:
if isinstance(b, (str, unicode)):
do_something_else()
или, используя basestring
abstract class:
if isinstance(b, basestring):
do_something_else()
Согласно документации Python, вот утверждение:
8.15. типы - имена для встроенных типов
Начиная с Python 2.2, встроенный заводские функции, такие как
int ()
иstr ()
также являются названиями для соответствующие типы.
Таким образом, isinstance ()
следует отдавать предпочтение [type ()
](http://docs.python.org/2 / library/functions.html#type).
Практическая разница в использовании заключается в том, как они справляются с booleans
:
True
и False
- это просто ключевые слова, которые означают 1
и 0
в питоне. Таким образом,
isinstance (True, int)
а также
isinstance (False, int)
оба возвращают True
. Оба логических значения являются экземпляром целого числа. type ()
, однако, более умный:
type (True) == int
возвращает False
.
Для реальных различий мы можем найти его в code
, но я не могу найти реализацию поведения по умолчанию isinstance ()
.
Однако мы можем получить аналогичный abc.\ _ \ instancecheck \ \ _ в соответствии с [_ instancecheck \ \ _] ( https:/docs.python.org/3/reference/datamoding.html.
Сверху 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)})
Кстати: лучше не смешивать использование относительно и абсолютно импортировано
, использовать абсолютно import
из project_dir (добавлено sys.path
)