Как я могу проверить типы переменных в Python?

У меня есть функция Python, которая принимает числовой аргумент, который должен быть целым числом для правильного поведения. Каков предпочтительный способ проверки этого в Python?

Моя первая реакция - сделать что-то вроде этого:

def isInteger(n):
    return int(n) == n

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

Предоставляет ли Python какие-либо собственные средства проверки типов переменных? Или это считается нарушением динамически типизированного дизайна языка?

РЕДАКТИРОВАТЬ: поскольку многие люди спрашивали - рассматриваемое приложение работает с префиксами IPv4, получая данные из плоских текстовых файлов. Если какой-либо ввод преобразован в число с плавающей запятой, эту запись следует рассматривать как искаженную и игнорировать.


person Murali Suriar    schedule 20.01.2009    source источник
comment
Дубликат: stackoverflow.com/questions/378927/   -  person S.Lott    schedule 21.01.2009
comment
Итак, n должно быть целым числом или должно быть только целым числом?   -  person    schedule 21.01.2009


Ответы (9)


isinstance(n, int)

Если вам нужно знать, действительно ли это действительно тип int, а не подкласс int (как правило, этого делать не нужно):

type(n) is int

это:

return int(n) == n

не такая уж хорошая идея, поскольку сравнения перекрестных типов могут быть правдой, особенно int(3.0)==3.0

person bobince    schedule 21.01.2009
comment
Я обнаружил функцию «тип» примерно через 5 минут после публикации вопроса. :) Ради интереса, есть ли какое-либо существенное снижение производительности при использовании isInstance по сравнению с типом? - person Murali Suriar; 21.01.2009
comment
Во всяком случае, я ожидал бы, что isinstance будет быстрее. Однако разница не должна быть большой (и если вы действительно обеспокоены производительностью, почему вы используете Python?) - person David Z; 21.01.2009
comment
@David: Меня не беспокоит производительность именно этой программы; это прототип хобби-проекта, от которого можно или не отказаться. Тем не менее, меня интересует разница между двумя подходами и компромиссы, связанные с каждым из них. Больше скорости - никогда не плохо. :) - person Murali Suriar; 21.01.2009
comment
timeit.Timer () говорит, что type-is-int примерно на 4% быстрее, чем isinstance-int на моей машине с Python 2.6. В любом случае это довольно ничтожно, выбирайте тот, который лучше всего говорит о том, что вы имеете в виду. - person bobince; 21.01.2009
comment
-1: проверка типов - не лучшая идея. Вы должны сказать это в своем ответе. - person nosklo; 21.01.2009
comment
Конечно, есть случаи, когда уместна проверка типов. Не зная, чем занимается OP - только то, что он должен быть целым числом - пока рано осуждать. - person bobince; 21.01.2009
comment
@bobince: проверка типов - это плохо во всех ситуациях. Я не могу представить себе ситуацию, в которой проверка типа принесла бы пользу. - person nosklo; 21.01.2009
comment
nosklo: аргументы вариантов, конструкторы вариантов, обработка str / unicode, проверка исключений, getitem ... проверка типов осуществляется повсюду в Python. Отбросьте догму OO и живите с ней. - person bobince; 21.01.2009
comment
@bobince, проверка типов не требуется. Вместо этого поймайте TypeError, чтобы знать, что он несовместим. - person Evan Fosmark; 21.01.2009
comment
Эван: Кто сказал, что будет исключение? Он может работать, но вести себя некорректно. Без дополнительного контекста из OP мы не знаем, подходит ли это место для проверки типов. - person bobince; 21.01.2009
comment
@bobince: проверка типов никогда не подходит. Это глупо. Доказательство тому, что вы не могли придумать ситуацию, в которой его следовало бы использовать. Ни один из ваших примеров: аргументы вариантов, конструкторы вариантов, обработка str / unicode, проверка исключений, getitem каким-либо образом использует проверку типов. - person nosklo; 22.01.2009
comment
Все они используют проверку типов, поэтому я упомянул их. У вас может быть аргумент, если OP определяли свои собственные типы значений, но факт в том, что вы не можете поместить свои собственные инкапсулированные действия внутри int и других встроенных типов. Тем не менее, спасибо, что сказал мне, что это глупо - хороший аргумент! - person bobince; 23.01.2009
comment
@bobince: они не используют проверку типов! Варианты аргументов никаким образом не используют проверку типов, а только * args, возвращающие кортеж. конструкторы вариантов тоже этого не делают, вы просто создаете для этого дополнительные методы класса. str / unicode также обрабатывает ... просто относитесь ко всему как к unicode. Встроенная проверка исключений. - person nosklo; 23.01.2009
comment
@bobince: а получить? Я даже не понимаю, как это будет использовать проверку типов. И тот факт, что я не могу поместить свои действия во встроенные типы, является самой причиной того, что проверка типов плохая !! Без проверки типов мне вообще не нужно использовать встроенные типы, поэтому мои действия и все еще работают. - person nosklo; 23.01.2009
comment
getitem может принимать в качестве аргумента числовую позицию, произвольный ключ или объект среза. Это пример вариантных аргументов (не переменной длины). Как узнать, что вы прошли? С isinstance. dict может быть создан из последовательности или сопоставления с другой семантикой и т. д. - person bobince; 23.01.2009
comment
... и обнаружение разницы между str и unicode жизненно важно в программе, которая не хочет в конечном итоге получить случайные UnicodeDecodeErrors. Я не утверждаю, что проверка типов - это здорово, и, конечно, ее следует избегать, когда вы контролируете интерфейсы, но полностью избегать этого нецелесообразно. - person bobince; 23.01.2009
comment
@bobince В getitem я использую try: start, end, step = item.indices (), кроме AttributeError: something (). diff между unicode и str бесполезен, если вы декодируете все, что получаете извне python, и перекодируете весь вывод, поэтому вы заканчиваете только unicode внутренне (это то, что представляет собой py3.0 str) - person nosklo; 23.01.2009
comment
@bobince Если я напишу свой собственный класс dict, я предпочитаю использовать альтернативный конструктор для сопоставлений, но я мог бы попробовать: arg = arg.iteritems () except AttributeError: pass then for key, value in arg - person nosklo; 23.01.2009
comment
@bobince: Дело в том, что ваше последнее предложение неверно. Я всегда могу полностью избежать проверки типов и быть счастливым. Фактически, я делаю это во всех написанных мною программах. - person nosklo; 23.01.2009
comment
Как бы я ни ненавидел проверку типов, в некоторых случаях она необходима - если вам нужно рекурсивно работать с контейнерами, которые заканчиваются строками, найдите хороший способ гарантировать, что вы не перебираете строки без проверки типов. Тем не менее, избегание - лучший ответ, если только в этом нет крайней необходимости. - person Gareth Latty; 08.01.2013

Да, как сказал Эван, не проверяйте типаж. Просто попробуйте использовать значение:

def myintfunction(value):
   """ Please pass an integer """
   return 2 + value

Здесь нет проверки типов. Это намного лучше! Посмотрим, что будет, когда я попробую:

>>> myintfunction(5)
7

Это работает, потому что это целое число. Хм. Давайте попробуем текст.

>>> myintfunction('text')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in myintfunction
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Он показывает ошибку TypeError, что он должен делать в любом случае. Если вызывающий хочет это поймать, это возможно.

Что бы вы сделали, если бы проверили тип? Показывать ошибку верно? Таким образом, вам не нужно выполнять проверку типов, потому что ошибка уже появляется автоматически.

Кроме того, поскольку вы не выполняли проверку типов, ваша функция работает с другими типами:

Поплавки:

>>> print myintfunction(2.2)
4.2

Комплексные числа:

>>> print myintfunction(5j)
(2+5j)

Десятичные дроби:

>>> import decimal
>>> myintfunction(decimal.Decimal('15'))
Decimal("17")

Даже совершенно произвольные объекты, умеющие складывать числа!

>>> class MyAdderClass(object):
...     def __radd__(self, value):
...             print 'got some value: ', value
...             return 25
... 
>>> m = MyAdderClass()
>>> print myintfunction(m)
got some value:  2
25

Таким образом, вы явно ничего не получите от проверки типов. И много потеряешь.


ОБНОВИТЬ:

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

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

Но ... Если восходящая функция будет вести себя некорректно и убьет некоторых детей, если вы передадите ей число с плавающей запятой (я все еще очень сомневаюсь в этом), тогда просто вызовите int():

def myintfunction(value):
   """ Please pass an integer """
   return upstreamfunction(int(value))

Вы по-прежнему не проверяете типы, поэтому вы получаете максимальную пользу от отказа от проверки типов.


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

assert isinstance(...)
assert type() is xxxx

Таким образом, мы можем отключить asserts и удалить эту <sarcasm> функцию </sarcasm> из программы, вызвав ее как

python -OO program.py
person nosklo    schedule 21.01.2009
comment
Он сказал, что функция не будет работать, если аргумент не является int. Работа функции с числами с плавающей запятой и комплексными числами, поскольку она не проверяется по типу, является ошибкой, а не функцией. - person Nick; 21.01.2009
comment
@Nick: Если это не сработает, если аргумент не является int, что он будет делать вместо этого? Поднять ошибку? Ну, Python это уже делает. Кроме того, если функция работает с другими типами, такими как float, то это не ошибка, это особенность, потому что функция работает. Сделать так, чтобы это не работало, было бы искусственным ограничением. - person nosklo; 21.01.2009
comment
@Nick: Как вы можете узнать, что функция не будет работать без int? Что, если я предоставлю объект, который не является int, но ведет себя точно так же, как int? Почему это не должно работать? - person nosklo; 21.01.2009
comment
@nosklo: функция может «работать» при передаче другого аргумента, но для целей задачи результат должен быть «неопределенным». Я никогда не слышал о нецелой длине префикса. - person Murali Suriar; 22.01.2009
comment
@Murali: Тогда просто используйте int () для значения. Это превратит его в целое число. - person nosklo; 22.01.2009
comment
@nosklo: Может, я не понимаю? Если полученное значение не является int, значит, ввод недопустим, и программа не должна с ним ничего делать. - person Murali Suriar; 23.01.2009
comment
@ Мурали, да, но почему? Это искусственное ограничение. Доказательство плохо разработанной спецификации. - person nosklo; 23.01.2009
comment
@nosklo: общепринятым форматом для префиксов CIDR IPv4 является A.B.C.D / E, где A-D - целые числа в диапазоне 0–255, а E - это целое число в диапазоне 0–32. Если вышеуказанные условия не выполняются, входной файл имеет неправильный формат, и его следует игнорировать. Я упустил здесь что-то очевидное? Что делает этот дизайн плохим? - person Murali Suriar; 23.01.2009
comment
@Murali: Следует игнорировать плохой дизайн. Кроме того, похоже, что задача библиотеки подчеркивания - правильно обрабатывать эти числа, а не вашей библиотеки. И просто вызов int () для значения означает, что вы можете передавать строки, числа с плавающей запятой ... и при этом соответствовать критериям, поэтому я не понимаю, почему бы и нет. - person nosklo; 23.01.2009
comment
@Murali: вызов int () быстрее, проще, читаемый код, который будет работать во всех случаях, за исключением искусственного ограничения, которое должно быть целым числом или умереть. И вы теряете возможность использовать другие типы. Так что отказ от проверки типов - беспроигрышный вариант - person nosklo; 23.01.2009
comment
@Murali: Лучшим вариантом будет: Общепринятый формат для префиксов IPv4 CIDR - A.B.C.D / E, где A-D - целые числа в диапазоне 0–255, а E - это целое число в диапазоне 0–32. Если вышеуказанные условия не выполняются и значения не могут быть легко преобразованы в целые числа, должна возникнуть ошибка. - person nosklo; 23.01.2009
comment
@nosklo: Это другой дизайн; не обязательно лучше. Если значения необходимо преобразовать в целые числа (легко или нет), то входные данные не соответствуют ожидаемому формату; что еще может быть не так с вводом? Я предпочитаю игнорировать это раньше, чем исправлять все возможные ошибки позже. - person Murali Suriar; 24.01.2009
comment
@Murali: Хорошо, если хотите. В конце моего ответа добавлен способ сделать это так, что вы можете отключить его, если хотите. - person nosklo; 24.01.2009
comment
@kigurai: он решает проблему, потому что он гарантирует, что недопустимые данные не будут переданы в функцию восходящего потока, сначала преобразовав что-либо в int. Если вызывающий абонент не хочет, он все равно должен передать мне int, как описано в документации, так что я стараюсь изо всех сил. - person nosklo; 26.03.2009
comment
@kigurai: и я рекомендую только восходящую функцию (int (value)), если восходящая функция имеет побочные эффекты, которые вызовут взрыв компьютера, если не передать int. Это очень необычно. Проверка типов бесполезна почти во всех случаях. - person nosklo; 26.03.2009
comment
@nosklo: если у вас есть функция, которая требует в качестве аргумента int, скажем, потому что это длина массива или какое-то другое целое число, тогда использование int () для сжатия может скрыть ошибки. Во многих случаях программы должны преждевременно выходить из строя при получении недопустимого ввода, а не пытаться молча исправить ввод. - person Nick; 07.04.2009
comment
Ник, конечно, тогда не используйте int () в этом случае. Просто передайте ценность нетронутой. Тип списка вызовет ошибку автоматически, нет необходимости проверять. - person nosklo; 07.04.2009
comment
@nick, дело в том, что там нет места для проверки типов, если только вы не добавите искусственные ограничения. - person nosklo; 07.04.2009

Python теперь поддерживает постепенный ввод через модуль ввода и mypy . Модуль typing является частью stdlib начиная с Python 3.5 и может быть загружен из PyPi, если вам нужны резервные копии для Python 2 или предыдущей версии Python 3. Вы можете установить mypy, запустив pip install mypy из командной строки.

Короче говоря, если вы хотите убедиться, что какая-то функция принимает int, float и возвращает строку, вы должны аннотировать свою функцию следующим образом:

def foo(param1: int, param2: float) -> str:
    return "testing {0} {1}".format(param1, param2)

Если ваш файл был назван test.py, вы можете проверить тип после установки mypy, запустив mypy test.py из командной строки.

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

def foo(param1, param2):
    # type: (int, float) -> str
    return "testing {0} {1}".format(param1, param2)

Вы используете ту же команду mypy test.py для файлов Python 3 и mypy --py2 test.py для файлов Python 2.

Аннотации типов полностью игнорируются интерпретатором Python во время выполнения, поэтому накладные расходы минимальны или вообще отсутствуют - обычный рабочий процесс заключается в работе над кодом и периодическом запуске mypy для выявления ошибок и ошибок. Некоторые IDE, такие как PyCharm, понимают подсказки типов и могут предупреждать вас о проблемах и несоответствиях типов в вашем коде во время непосредственного редактирования.

Если по какой-то причине вам нужно, чтобы типы проверялись во время выполнения (возможно, вам нужно проверить большой ввод?), Вы должны следовать советам, указанным в других ответах, например используйте isinstance, issubclass и т.п. Есть также некоторые библиотеки, такие как enforce, которые пытаются выполнить проверку типов (с учетом аннотаций вашего типа) во время выполнения, хотя я не уверен, насколько они готовы к производству на момент написания.

Дополнительную информацию и подробности см. На веб-сайте mypy, mypy FAQ и PEP 484.

person Michael0x2a    schedule 29.06.2016
comment
Было бы здорово, если бы была возможность утверждать аннотации типов. И твоя аватарка качается. - person Ciro Santilli 新疆再教育营六四事件ۍ 29.03.2017

if type(n) is int

Это проверяет, является ли n Python int и только int. Он не принимает подклассы int.

Однако проверка типов не соответствует «пути Python». Вам лучше использовать n как int, и если он вызывает исключение, поймайте его и действуйте в соответствии с ним.

person Nikhil Chelliah    schedule 21.01.2009
comment
Давайте, ребята. Ответ на вопрос был полезным, хотя и не очень. Вместо того, чтобы голосовать против, исправьте это, потому что, если многие люди согласятся, что проверка типов не очень Pythonic, правильное сообщение может быть легче доставлено. - person tzot; 21.01.2009

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

class MyInt(int):
    # ... extra stuff ...
person Evan Fosmark    schedule 21.01.2009
comment
Если вы используете isinstance (), вы можете покрыть этот случай. Некоторым функциям действительно нужны целые числа, и утиная печать просто скроет возможную ошибку. - person Nick; 21.01.2009
comment
+1: Лучше не проверка типов. @Nick: вы можете привести пример такой функции? Почему я не могу передать значение с плавающей запятой в функцию, которой требуется целое число? Вместо проверки типов следует использовать int () для значения. - person nosklo; 21.01.2009
comment
@nosklo: если у вас есть функция, которая требует в качестве аргумента int, скажем, потому что это длина массива или какое-то другое целое число, тогда использование int () для сжатия может скрыть ошибки. Во многих случаях программы должны преждевременно выходить из строя при получении недопустимого ввода, а не пытаться молча исправить ввод. - person Nick; 07.04.2009

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

Из интерактивной командной строки вы можете запустить такую ​​инструкцию:

int('sometext')

Это вызовет ошибку - ipython сообщает мне:

<type 'exceptions.ValueError'>: invalid literal for int() with base 10: 'sometext'

Теперь вы можете написать такой код:

try:
   int(myvar) + 50
except ValueError:
   print "Not a number"

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

person basswulf    schedule 25.03.2009

Я бы хотел что-то вроде:

def check_and_convert(x):
    x = int(x)
    assert 0 <= x <= 255, "must be between 0 and 255 (inclusive)"
    return x

class IPv4(object):
    """IPv4 CIDR prefixes is A.B.C.D/E where A-D are 
       integers in the range 0-255, and E is an int 
       in the range 0-32."""

    def __init__(self, a, b, c, d, e=0):
        self.a = check_and_convert(a)
        self.b = check_and_convert(a)
        self.c = check_and_convert(a)
        self.d = check_and_convert(a)
        assert 0 <= x <= 32, "must be between 0 and 32 (inclusive)"
        self.e = int(e)

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

person James Brooks    schedule 24.11.2009

как насчет:

def ip(string):
    subs = string.split('.')
    if len(subs) != 4:
        raise ValueError("incorrect input")
    out = tuple(int(v) for v in subs if 0 <= int(v) <= 255)
    if len(out) != 4:
        raise ValueError("incorrect input")
    return out

конечно, есть стандартная функция isinstance (3, int) ...

person Lars    schedule 24.11.2011

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

assert(type(X) == int(0))

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

person Jek Denys    schedule 24.05.2020