Как я могу проверить переменные в Python? - PullRequest
31 голосов
/ 21 января 2009

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

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

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

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

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

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

Ответы [ 8 ]

32 голосов
/ 21 января 2009
isinstance(n, int)

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

type(n) is int

это:

return int(n) == n

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

16 голосов
/ 21 января 2009

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

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

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


UPDATE:

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

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

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

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

Вы все еще не проверяете типизацию, поэтому вы получаете большинство преимуществ, не проверяющих тип.


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

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

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

python -OO program.py
4 голосов
/ 29 июня 2016

Python теперь поддерживает постепенный набор с помощью модуля и mypy . Модуль typing является частью stdlib начиная с Python 3.5 и может быть загружен из PyPi , если вам нужны backports для 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 и т.п. Есть также некоторые библиотеки, такие как принудительно , которые пытаются выполнять проверку типов (с учетом ваших аннотаций типов) во время выполнения, хотя я не уверен, насколько они готовы к работе на момент написания.

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

4 голосов
/ 21 января 2009
if type(n) is int

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

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

1 голос
/ 25 марта 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).

1 голос
/ 21 января 2009

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

class MyInt(int):
    # ... extra stuff ...
0 голосов
/ 24 ноября 2011

как насчет:

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

0 голосов
/ 24 ноября 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)

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

...