Какой канонический способ проверить тип в Python?

1278

Каков наилучший способ проверить, принадлежит ли данный объект заданному типу? Как насчет проверки, наследуется ли объект от данного типа?

Допустим, у меня есть объект o. Как я могу проверить, является ли это str?

Herge
источник
7
Ну, канонический подход в Python - вообще не проверять тип (если только вы не отлаживаете). Обычно вы просто пытаетесь использовать его как строку (например, объединить с другими строками, вывести на консоль и т. Д.); если вы думаете, что это может не сработать, используйте try / exception или hasattr. Тем не менее, принятый ответ является каноническим способом сделать то, что вы обычно «не должны делать» в мире Python. Для получения дополнительной информации, Google "Python Duck Typing " или прочитайте их: voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/…
Джон Кумбс
9
Я думаю, что г-н Кумбс пропускает примеры как сериализуемые не-JSON классы. Если вы помещаете большой кусок данных через функцию (чей код не может влиять), вам может потребоваться преобразовать определенные части этих данных, например, в <str> перед передачей. По крайней мере, так я оказался на этой странице ...
Джон Каррелл
2
Кажется, самая распространенная причина для этого - то, что нужно различать строки и повторяемости строк. Это сложный вопрос, потому что строки - это итерируемые строки. Односимвольная строка - это даже сама по себе последовательность (в прошлый раз, когда я проверял - вероятно, не стоит полагаться на нее). Но кто-нибудь когда-нибудь использовал бы что-нибудь подобное? Да . Итак, ответ на вопрос «Что я должен сделать, чтобы различать строки и другие элементы строк?» правильно: «Это зависит от того, что вы пытаетесь сделать». :-D
клик
2
Аннотации типа Python теперь важны. Взгляните на mypy
Шина

Ответы:

1522

Чтобы проверить, oявляется ли экземпляр strили какой-либо подкласс класса str, используйте isinstance (это будет «канонический» способ):

if isinstance(o, str):

Чтобы проверить, oточно ли тип str(исключая подклассы):

if type(o) is str:

Следующее также работает, и может быть полезно в некоторых случаях:

if issubclass(type(o), str):

См. Встроенные функции в Библиотеке Python для получения соответствующей информации.

Еще одно замечание: в этом случае, если вы используете Python 2, вы можете использовать:

if isinstance(o, basestring):

потому что это также будет ловить строки Unicode ( unicodeне является подклассом str; оба strи unicodeявляются подклассами basestring). Обратите внимание, что basestringбольше не существует в Python 3, где существует строгое разделение строк ( str) и двоичных данных ( bytes).

Кроме того, isinstanceпринимает кортеж классов. Это вернет, Trueесли oявляется экземпляром любого подкласса любого из (str, unicode):

if isinstance(o, (str, unicode)):
Фредрик Йоханссон
источник
31
Подклассы str .__ __ () возвращают только прямые подклассы str и не делают то же самое, что issubclass () или isinstance (). (Для этого вам придется рекурсивно вызывать .__ подклассы __ ().
Томас Воутерс
15
Это хороший ответ, но я думаю, что это действительно должно начаться с предупреждения, что вам обычно не следует делать это в Python. На самом деле, кажется, подтверждается предположение, что это «каноническая вещь в Python», а это не так.
Джон Кумбс
4
Это ответы Python2. Например, в python3 нет базовой строки.
dfrankow
4
В чем разница между экземпляром и «точно»? Если type(a) is Objectтогда не правда ли это тоже isinstance(a, Object). Однако, если type(a) is SubClassOfObject, тогда type(a) is Object == False, но isinstance(a, Object) == True. Правильно?
mavavilj
1
@mavavilj - a is bозначает, что a и b - это одно и то же, то есть ссылки на один и тот же объект в памяти. Так aи bдолжно быть точно такой же класс, а не подклассы, как с isinstance(). Смотрите, например, stackoverflow.com/a/133024/1072212
Терри Браун
196

Наиболее Pythonic способ проверить тип объекта является ... не проверить.

Поскольку Python поощряет Duck Typing , вы должны просто try...exceptиспользовать методы объекта так, как вы хотите их использовать. Так что если ваша функция ищет доступный для записи объект файла, не проверяйте, является ли он подклассом file, просто попробуйте использовать его .write()метод!

Конечно, иногда эти красивые абстракции ломаются и isinstance(obj, cls)это то, что вам нужно. Но используйте экономно.

Дэн Ленски
источник
75
ИМХО, самый питонский способ - справиться с любым аргументом, который дается. В моем коде я часто не могу узнать, получаю ли я объект или массив объектов, и я использую внутреннюю проверку типов для преобразования одного объекта в список из одного элемента.
Састанин
14
Вместо того, чтобы просто пытаться использовать его метод записи, бывают ситуации, когда вы хотите сделать это без исключения. В этом случае вы могли бы сделать ... if hasattr(ob, "write") and callable(ob.write): Или сохранить некоторый доступ к func = getattr(ob, "write", None) if callable(func): ...
диктату
142
Утиная печать - это использование библиотеки. Проверка типов - это написание библиотеки. Не тот же проблемный домен.
RickyA
16
@RickyA, я не согласен. Утиная типизация - это взаимодействие с объектами с использованием интерфейсов с известной семантикой. Это может относиться либо к коду библиотеки, либо к коду, который использует такую ​​библиотеку.
Дэн Ленски,
6
@ nyuszika7h, в Python3 подавляется hasattrтолько ошибка AttributeError - см. docs.python.org/3.4/library/functions.html#hasattr
ideasman42
57

isinstance(o, str)вернет, Trueесли oявляется strили имеет тип, который наследуется от str.

type(o) is strвернется, Trueесли и только если oэто ул. Он вернетсяFalse если oимеет тип, который наследуется от str.

Herge
источник
6
Конечно, это не удастся, если объект не является экземпляром 'str', а вместо этого является чем-то вроде строки. Как Unicode, Mmap, UserString или любой другой пользовательский тип. Обычный подход в Python - не выполнять проверки типов.
Томас Воутерс
6
Вы не должны извиняться, это нормально, чтобы ответить на свой вопрос. ТАК для ответов, а не для кармы.
Эли Бендерский
2
Это очень полезно. Потому что разница между isinstanceи type(var) == type('')не ясна.
Састанин
30

После того, как вопрос был задан и получен ответ, в Python были добавлены подсказки типа . Подсказки типов в Python позволяют проверять типы, но совсем не так, как в статически типизированных языках. Подсказки типов в Python связывают ожидаемые типы аргументов с функциями как доступные во время выполнения данные, связанные с функциями, и это позволяет проверять типы. Пример синтаксиса подсказки типа:

def foo(i: int):
    return i

foo(5)
foo('oops')

В этом случае мы хотим, чтобы была вызвана ошибка, foo('oops')поскольку аннотированный тип аргумента - int. Добавленная подсказка типа не вызывает возникновению ошибки при нормальной работе скрипта. Однако в функцию добавляются атрибуты, описывающие ожидаемые типы, которые другие программы могут запрашивать и использовать для проверки ошибок типов.

Одной из этих других программ, которые можно использовать для обнаружения ошибки типа, является mypy:

mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"

(Вам может понадобиться установить mypy из вашего менеджера пакетов. Я не думаю, что он поставляется с CPython, но, похоже, имеет некоторый уровень "официальности".)

Проверка типов этим способом отличается от проверки типов в статически типизированных компилируемых языках. Поскольку в Python типы являются динамическими, проверка типов должна выполняться во время выполнения, что требует затрат - даже для правильных программ - если мы настаиваем на том, чтобы это происходило при каждом удобном случае. Явные проверки типов также могут быть более строгими, чем необходимо, и приводить к ненужным ошибкам (например, действительно ли аргумент должен быть точноlist типа или достаточно итеративен?).

Преимущество явной проверки типов в том, что она может отлавливать ошибки раньше и давать более четкие сообщения об ошибках, чем утка. Точные требования к типу утки могут быть выражены только с помощью внешней документации (надеюсь, она тщательна и точна), и ошибки несовместимых типов могут возникать далеко от их происхождения.

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

В typingпеременных пакета предложений типа , которые могут быть использованы в намеков типа выражать необходимые модели поведения , не требуя определенных типов. Например, он включает в себя такие переменные, какIterable и Callableдля подсказок, чтобы указать необходимость любого типа с этими поведениями.

В то время как подсказки типов - это самый Pythonic способ проверки типов, часто Pythonic вообще не проверяет типы вообще и полагается на типизацию с утиным типом. Типовые подсказки являются относительно новыми, и жюри все еще остается в стороне, когда они являются наиболее питонским решением. Относительно неоспоримое, но очень общее сравнение: подсказки типов предоставляют форму документации, которую можно применять, позволяют генерировать код раньше и легче понимать ошибки, могут отлавливать ошибки, которые не могут быть введены при печати, и могут быть проверены статически (в необычном случае). смысл, но это все еще вне времени выполнения). С другой стороны, типизация утки долгое время была питонской, не накладывает когнитивных издержек на статическую типизацию, менее многословна и будет принимать все жизнеспособные типы, а затем и некоторые.

Praxeolitic
источник
2
-1: mypy определенно называет себя «статическая проверка типов», поэтому я не уверен, откуда вы взяли «проверка типов должна выполняться во время выполнения».
Кевин
@Kevin В ретроспективе это было ненужное отступление, но чтобы углубиться в него, подсказки типов Python превращаются в данные времени выполнения и mypyпредставляют собой модуль Python, который использует importlibдля доступа к этим данным. Является ли это «статической проверкой типов» - это философский вопрос, но он отличается от того, чего большинство ожидают, поскольку в нем задействованы обычный интерпретатор языка и механизм импорта.
Праксеолит
4
Это тоже не правда. Он использует typed_ast, который сам по себе является просто клоном ast с дополнительными функциями. ast не импортирует модули; он разбирает их в абстрактное синтаксическое дерево.
Кевин,
18

Вот пример, почему печатание на уток - зло, не зная, когда это опасно. Например: вот код Python (возможно, без правильного отступа), обратите внимание, что этой ситуации можно избежать, если позаботиться о функциях isinstance и issubclassof, чтобы убедиться, что когда вам действительно нужна утка, вы не получите бомбу.

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()
Дмитрий
источник
36
Даже с проверкой типа вы можете создать class EvilDuck(Duck)и переопределить talk (). Или, более вероятно, class ChineseCancerDuck(Duck)с неприятным побочным эффектом, который проявляется только спустя годы. Тебе было бы лучше просто присматривать за своим ребенком (и тщательно проверять его игрушки :)
Бретт Томас,
36
Бомбы не разговаривают. Не добавляйте бессмысленные методы, и этого не произойдет.
правее
7
@Dmitry, это общая критика Duck Typing: en.wikipedia.org/wiki/Duck_typing#Criticism ... вы в основном говорите, что любой интерфейс, для которого семантика не поддерживается языком, является злом. Я считаю, что это больше подход Java. Смысл утилитарной типизации Python в том, что она работает только тогда, когда существует общепринятое соглашение о том, что означают конкретные интерфейсы. Например, вы можете создать много кода Python, переопределив __file__атрибут (обычно используемый для идентификации файловых объектов), чтобы обозначить что-то другое.
Дэн Ленски,
2
Все это сводится к старой шутке «Доктор, мне больно, когда я это делаю». ... «Тогда не делай этого». Неудовлетворительно для того, кто привык «если он компилируется, он запускается», но именно поэтому навязчивая идея тестирования выросла из динамического языкового мира.
глэк
1
@clacke, в принципе, слишком дорого применять типы во время выполнения строго, потому что ВСЁ должно быть объектом (для преобразования из строки в любой возможный тип), и слишком удобно, чтобы не иметь ducktyping, потому что ducktyping позволяет действительно мощные методы создания прототипов, которые преодолевают вещи, которые как правило, очень трудно сделать с жесткими интерфейсами. Кроме того, любой статический язык сталкивается с точкой, в которой ему необходимо создавать типизацию утки с помощью динамических библиотек, вычислений и строк, или интерфейсов, и эти вещи по своей сути не делают его злым, просто очень мощным.
Дмитрий
12
isinstance(o, str)

Ссылка на документы

Александр Кожевников
источник
1
Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится.
EKons
7

Я думаю, что классная вещь в использовании динамического языка, такого как Python, заключается в том, что вам не нужно проверять что-то подобное.

Я бы просто вызвал необходимые методы для вашего объекта и поймал бы AttributeError. Позже это позволит вам вызывать ваши методы с другими (казалось бы, не связанными) объектами для выполнения различных задач, таких как насмешка над объектом для тестирования.

Я использовал это много, когда получаю данные из Интернета с urllib2.urlopen() которых возвращал файл, подобный объекту. Это, в свою очередь, может быть передано практически любому методу, который читает из файла, потому что он реализует то же самоеread() метод, что и реальный файл.

Но я уверен, что есть время и место для использования isinstance(), иначе его, вероятно, не было бы :)

Уилл Хардинг
источник
Хороший пример того, когда вы должны использовать это, если вы анализируете динамический объект json. Вы не знаете заранее, является ли поле строкой или словарем.
Серый
6

Для более сложных проверок типов мне нравится подход валидации typeguard, основанный на аннотациях подсказок типа Python:

from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

Вы можете выполнять очень сложные проверки в очень чистой и удобочитаемой форме.

check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo) 
Granitosaurus
источник
6

Вы можете проверить тип переменной, используя __name__ типа.

Пример:

>>> a = [1,2,3,4]  
>>> b = 1  
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
Еррамсетты Рохит
источник
Спасибо, это секретный код, который я хотел, когда я отображал его в качестве обратной связи с пользователем. Мне
Аарон Д. Мараско
5

Хьюго:

Вы, вероятно, имеете в виду, listа неarray , но это указывает на всю проблему с проверкой типов - вы не хотите знать, является ли рассматриваемый объект списком, вы хотите знать, является ли это какой-то последовательностью или это один объект. Поэтому попробуйте использовать его как последовательность.

Допустим, вы хотите добавить объект в существующую последовательность или, если это последовательность объектов, добавить их все

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

Одна хитрость в этом заключается в том, что если вы работаете со строками и / или последовательностями строк - это сложно, поскольку строку часто рассматривают как отдельный объект, но это также последовательность символов. Хуже того, так как это действительно последовательность строк одинарной длины.

Я обычно выбираю дизайн своего API так, чтобы он принимал только одно значение или последовательность - это облегчает работу. Нетрудно [ ]обойти ваше единственное значение, когда вы передаете его, если это необходимо.

(Хотя это может привести к ошибкам со строками, так как они выглядят (являются) последовательностями.)

Крис Баркер
источник
0

Простой способ проверить тип - сравнить его с чем-то, чей тип вы знаете.

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
Ultrablendz
источник
-1

Я думаю, что лучший способ - это правильно набирать переменные. Вы можете сделать это с помощью библиотеки "typing".

Пример:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313) `

См. Https://docs.python.org/3/library/typing.html.

cherah30
источник
-7

Вы можете проверить с помощью строки ниже, чтобы проверить, какой тип символа заданное значение:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)
Венкатесан
источник
3
Вы уверены, что не хотите удалять этот ответ @Venkatesan?
Серый