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

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

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

Ответы

Ответ 1

Чтобы проверить, является ли 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, если x является экземпляром любого подкласса любого из (str, unicode):

if isinstance(o, (str, unicode)):

Ответ 2

Самый питонский способ проверить тип объекта - не проверять его.

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

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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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()

Ответ 7

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

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

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

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

Ответ 8

К Hugo:

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

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

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

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

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

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

Ответ 9

Для более сложных проверок типов мне нравится подход проверки типа, основанный на подсказках типа 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) 

Ответ 10

Вы можете проверить тип переменной, используя __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'

Ответ 11

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

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

chr_type("12)