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

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

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

19 ответов

Решение

Чтобы проверить, если 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)):

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

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

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

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

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

в python 3.10, ты можешь использовать | в isinstance

      >>> isinstance('1223', int | str) 
True

>>> isinstance('abcd', int | str) 
True

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

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

Вот пример того, почему печатание на утке - зло, не зная, когда это опасно. Например: вот код 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()

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

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

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

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

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

Принятый ответ отвечает на вопрос, поскольку дает ответы на заданные вопросы.

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

A: Используйте проверять по типам.

Однако, как быстро указывают другие ответы и комментарии, идея «проверки типов» гораздо шире, чем в python. С добавлением Python 3 и подсказок типов также многое изменилось. Ниже я расскажу о некоторых трудностях с проверкой типов, утиным набором текста и обработкой исключений. Для тех, кто считает, что проверка типов - это не то, что нужно (обычно это не так, но мы здесь), я также указываю, как вместо них можно использовать подсказки типов.

Проверка типа

Проверка типов не всегда уместна в Python. Рассмотрим следующий пример:

      def sum(nums):
    """Expect an iterable of integers and return the sum."""
    result = 0
    for n in nums:
        result += n
    return result

Чтобы проверить, является ли ввод итерацией целых чисел, мы сталкиваемся с серьезной проблемой. Единственный способ проверить, является ли каждый элемент целым числом, - это пройти цикл для проверки каждого элемента. Но если мы переберем весь итератор, то для предполагаемого кода ничего не останется. У нас есть два варианта в такой ситуации.

  1. Проверяем, как мы петли.

  2. Проверяйте заранее, но сохраняйте все, как мы проверяем.

У варианта 1 есть обратная сторона - усложнение нашего кода, особенно если нам нужно выполнить аналогичные проверки во многих местах. Это заставляет нас перемещать проверку типов из верхней части функции везде, где мы используем итерацию в нашем коде.

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

Можно также подумать, что проверка того, что проверка всех элементов - это слишком много, тогда, возможно, мы можем просто проверить, имеет ли сам вход повторяемый тип, но на самом деле нет итерируемого базового класса. Реализация любого типа повторяется.

Обработка исключений и утка

Альтернативный подход состоял бы в том, чтобы полностью отказаться от проверки типов и вместо этого сосредоточиться на обработке исключений и утином вводе. Другими словами, оберните свой код в блок try-except и перехватите все возникающие ошибки. Или же ничего не делайте и позвольте исключениям естественным образом возникать из вашего кода.

Вот один из способов перехвата исключения.

      def sum(nums):
    """Try to catch exceptions?"""
    try:
        result = 0
        for n in nums:
            result += n
        return result
    except TypeError as e:
        print(e)

По сравнению с предыдущими вариантами это, безусловно, лучше. Мы проверяем, как запускаем код. Если есть где-нибудь, мы узнаем. Нам не нужно ставить галочку везде, где мы перебираем ввод. И нам не нужно сохранять ввод, когда мы его перебираем.

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

Однако точные причины, которые делают обработку исключений хорошей, также могут быть их недостатком.

  1. А не является, но он удовлетворяет поведенческим требованиям для работы.

  2. Также плохой практикой является оборачивание всего кода блоком try-except.

Сначала это может показаться не проблемой, но вот несколько причин, по которым вы можете передумать.

  1. Пользователь больше не может ожидать, что наша функция вернет как предполагалось. Это может нарушить код в другом месте.

  2. Поскольку исключения могут поступать из самых разных источников, использование try-except для всего блока кода может привести к перехвату исключений, которые вы не собирались делать. Мы только хотели проверить, есть ли был повторяемым и имел целочисленные элементы.

  3. В идеале мы хотели бы перехватывать исключения нашими генераторами кода и создавать вместо них более информативные исключения. Это не весело, когда исключение возникает из чужого кода без объяснения, кроме строки, которую вы не писали, и что некоторые произошло.

Чтобы исправить обработку исключений в ответ на вышеупомянутые пункты, наш код превратился бы в эту ... мерзость.

      def sum(nums):
    """
    Try to catch all of our exceptions only.
    Re-raise them with more specific details.
    """
    result = 0

    try:
        iter(nums)
    except TypeError as e:
        raise TypeError("nums must be iterable")

    for n in nums:
        try:
            result += int(n)
        except TypeError as e:
            raise TypeError("stopped mid iteration since a non-integer was found")

    return result

Вы можете увидеть, к чему все идет. Чем больше мы пытаемся «правильно» проверить вещи, тем хуже выглядит наш код. По сравнению с исходным кодом он вообще не читается.

Мы могли бы возразить, что, возможно, это немного экстремально. Но с другой стороны, это всего лишь очень простой пример. На практике ваш код, вероятно, намного сложнее этого.

Типовые подсказки

Мы видели, что происходит, когда мы пытаемся изменить наш небольшой пример, чтобы «включить проверку типов». Вместо того, чтобы сосредотачиваться на попытке форсировать определенные типы, подсказка типов позволяет сделать типы понятными для пользователей.

      from typing import Iterable

def sum(nums: Iterable[int]) -> int:
    result = 0
    for n in nums:
        result += n
    return result

Вот некоторые преимущества использования подсказок типа.

  • Теперь код действительно выглядит хорошо!

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

  • Они хранятся в функции / классе, что делает их динамически доступными, например typeguard а также dataclasses.

  • Они появляются для функций при использовании .

  • Нет необходимости проверять правильность вашего типа ввода на основе описания или, что еще хуже, его отсутствия.

  • Вы можете "напечатать" подсказку на основе структуры, например, "есть ли у нее этот атрибут?" без необходимости создания подклассов пользователем.

Оборотная сторона хинтинга типа?

  • Подсказки типа - это не что иное, как синтаксис и отдельный текст. Это не то же самое, что проверка типов.

Другими словами, он на самом деле не отвечает на вопрос, потому что не обеспечивает проверку типов. Однако, несмотря на это, если вы здесь для проверки типов, вам также следует использовать подсказки типа. Конечно, если вы пришли к выводу, что проверка типов на самом деле не нужна, но вам нужно некоторое подобие набора текста, тогда подсказки типа для вас.

Хьюго:

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

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

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

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

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

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

Чтобы проверить, принадлежит ли объект заданному типу или наследуется от заданного типа, вы можете использоватьisinstance()функция. Вот как вы можете проверить, имеет ли объект o тип str:

      o = "Hello, world!"

if isinstance(o, str):
    print("The object is of type str.")
else:
    print("The object is not of type str.")

Если вам нужно проверить типstrилиintпожалуйста, используйтеinstanceof. Как уже упоминалось другими, объяснение также включает подклассы. Одним из важных примеров подклассов, с моей точки зрения, являются Enums с типом данных, напримерIntEnumилиStrEnum. Это довольно хороший способ определить связанные константы. Однако это немного раздражает, если библиотеки не принимают такие типы.

Пример:

      import enum

class MyEnum(str, enum.Enum):
    A = "a"
    B = "b"

print(f"is string: {isinstance(MyEnum.A, str)}")   # True
print(f"is string: {type(MyEnum.A) == str}")       # False!!!
print(f"is string: {type(MyEnum.A.value) == str}") # True

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

Пример:

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

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

В Python вы можете использовать встроенныйisinstance()Функция для проверки, относится ли объект к заданному типу или наследуется ли он от заданного типа.

Чтобы проверить, относится ли объект o к типу str, вы должны использовать следующий код:

      if isinstance(o, str):
# o is of type str

Вы также можете использоватьtype()Функция для проверки типа объекта.

      if type(o) == str:
# o is of type str

Вы также можете проверить, является ли объект подклассом определенного класса, используяissubclass()функция.

      if issubclass(type(o),str):
# o is sub class of str

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

стр (х).isdigit()

Это будет работать для 111 '111' u'111 '

Это не будет работать с числами типа 111.1.

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

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True

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

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

chr_type("12)
Другие вопросы по тегам