Правильный способ утверждения типа переменной в Python

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

Вот пример поддельной функции, пытающейся сделать именно это, прежде чем продолжить свою роль:

def my_print(begin, text, end):
    """Print 'text' in UPPER between 'begin' and 'end' in lower

    """
    for i in (begin, text, end):
        assert isinstance(i, str), "Input variables should be strings"
    out = begin.lower() + text.upper() + end.lower()
    print out

def test():
    """Put your test cases here!

    """
    assert my_print("asdf", "fssfpoie", "fsodf")
    assert not my_print("fasdf", 33, "adfas")
    print "All tests passed"

test()

Является ли утверждение правильным подходом? Должен ли я вместо этого использовать try/except?

Кроме того, мой набор тестов assert, похоже, не работает должным образом: S

Спасибо, питонеры.


person Morlock    schedule 07.04.2010    source источник
comment
Я думаю, вы столкнулись с самой большой слабостью Python: нет формального способа указать типы, когда вы хотите. Меньший аспект этой проблемы заключается в том, что вам нужно проверять типы вручную (как в вашем вопросе). Большая проблема в том, что ваши инструменты не могут вам помочь. Python был бы идеальным языком, если бы только он поддерживал динамическую типизацию, но также имел возможность указывать типы, когда динамическая не нужна.   -  person Tom    schedule 19.04.2010
comment
Примечание от 2018 года: даже с появлением модуля typing в python 3.6, mypy и других инструментах приведенное выше утверждение остается верным: было бы здорово иметь версию python со статической типизацией.   -  person Evgeny    schedule 14.06.2018


Ответы (4)


Встроенный isinstance является предпочтительным способом, если вам это действительно нужно, но даже лучше состоит в том, чтобы помнить девиз Python: «Легче попросить прощения, чем разрешения»!-) (На самом деле это был любимый девиз Грейс Мюррей Хоппер;-). То есть:

def my_print(text, begin, end):
    "Print 'text' in UPPER between 'begin' and 'end' in lower"
    try:
      print begin.lower() + text.upper() + end.lower()
    except (AttributeError, TypeError):
      raise AssertionError('Input variables should be strings')

Это, кстати, позволяет функции прекрасно работать со строками Unicode — без каких-либо дополнительных усилий!-)

person Alex Martelli    schedule 07.04.2010
comment
Есть ли тогда смысл в «утверждении»? В тестировании? - person Morlock; 07.04.2010
comment
assert иногда полезно для проверки работоспособности, которая вам нужна только на этапе разработки (не в оптимизированном производственном коде, где он оптимизируется) - так что не проверяет допустимые входные данные (те, которые вы должны сделать в противном случае), а проверяет работоспособность вашего собственная логика (варианты циклов, инварианты классов и т. д.) - конечно, не связанная с типом. Сторонняя среда тестирования py.test использует assert так же, как стандарт unittest использует такие методы, как assertEqual и т. д. - person Alex Martelli; 07.04.2010
comment
Спасибо тебе за пояснение. - person Morlock; 07.04.2010
comment
Функцию my_print не должно волновать, имеют ли аргументы тип str или unicode; ему просто важно, чтобы аргументы имели методы lower(), upper() и __add__(). Это называется утиной типизацией (она выглядит как утка и говорит как утка (т.е. имеет те же методы), даже если на самом деле это вовсе не утка), и это предпочтительный метод для python. Если вы собираетесь вызвать исключение из-за того, что переданные аргументы имеют неправильный тип, вы должны вызвать исключение TypeError. - person ; 07.04.2010
comment
@007brendan, en.wikipedia.org/wiki/Duck_typing в разделе История приписывает мне самое раннее отслеживаемое использование термина утиная типизация (10 лет назад, когда я был новичком в Python), хотя, если вы читаете мой пост, на который они указывают, я на самом деле не использую фразу в этом посте ( Я придумываю много аналогий с утками, но в основном использую более традиционные термины, такие как переключение типов) — во всяком случае, я думаю, что это делает меня достаточно знакомым с концепцией ;-). Я согласен, что ошибка типа является хорошим совпадением (try/except может превратить ошибки атрибутов, всегда гибридные, в ошибки типа). - person Alex Martelli; 07.04.2010
comment
@ Алекс, я использую твои советы и решения во многих своих программах, поэтому я понимаю, что у тебя невероятный багаж знаний и опыта во всех этих областях (я даже использую твои собственные аналогии!). Мой пост был предназначен больше для ОП, чтобы дать некоторый контекст относительно того, почему ваше решение является каноническим. - person ; 08.04.2010
comment
Меня беспокоит, нужно ли поднимать AssertionError вместо TypeError в последней строке. Глядя на рекомендации pandas, кажется, что они повысят TypeError. - person Ioannis Filippidis; 23.11.2014
comment
Комментарий о прощении очень полезен! При каждом утверждении я буду думать, что может привести к прекращению действия утверждения. - person strpeter; 20.01.2015
comment
@strpeter, в эти дни на самом деле в городе появился новый ребенок, который я назвал гусиной печатью (сверка с базовыми классами abstract), но она не предлагает никакой помощи для строк, поэтому утиная печать по-прежнему правит в большинстве места :-) - person Alex Martelli; 20.01.2015

Вы можете попробовать этот пример для версии 2.6 Python.

def my_print(text, begin, end):
    "Print text in UPPER between 'begin' and 'end' in lower."
    for obj in (text, begin, end):
        assert isinstance(obj, str), 'Argument of wrong type!'
    print begin.lower() + text.upper() + end.lower()

Однако рассматривали ли вы вместо этого возможность естественного сбоя функции?

person Noctis Skytower    schedule 07.04.2010
comment
Я немного ретроград и все еще использую 2.6: P Спасибо за функцию isinstance(). - person Morlock; 07.04.2010

Выполнение type('') фактически эквивалентно str и types.StringType

поэтому type('') == str == types.StringType будет оцениваться как "True"

Обратите внимание, что строки Unicode, которые содержат только ASCII, не пройдут проверку типов таким способом, поэтому вы можете сделать что-то вроде assert type(s) in (str, unicode) или assert isinstance(obj, basestring), последнее из которых было предложено в комментариях 007Brendan и, вероятно, предпочтительнее.

isinstance() полезно, если вы хотите узнать, является ли объект экземпляром класса, например:

class MyClass: pass

print isinstance(MyClass(), MyClass) # -> True
print isinstance(MyClass, MyClass()) # -> TypeError exception

Но для основных типов, например. str, unicode, int, float, long и т. д. с запросом type(var) == TYPE будут работать нормально.

person cryo    schedule 07.04.2010
comment
Вы можете сделать -- assert isinstance(obj, basestring) -- str и unicode наследуются от basestring, так что это будет работать для обоих. - person ; 07.04.2010
comment
Спасибо за это предложение - я добавил его в ответ. - person cryo; 07.04.2010

isinstance(x, str) лучше всего, если вы можете его использовать, но он не работает с дженериками. Например, вы не можете сделать:

isinstance(x, dict[str, int])

Это выдаст ошибку времени выполнения:

TypeError: isinstance() argument 2 cannot be a parameterized generic

Вместо этого вы можете использовать cast:

from typing import cast

x_as_dict = cast(dict[str, int], x)

Конечно, в отличие от isinstance(), он на самом деле не выполняет проверку типов, поэтому при необходимости вам придется самостоятельно проверять все ключи и значения.

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

person Timmmm    schedule 02.07.2021