Подтвердить что ты не робот

Проверка типов аргументов Python

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

Я использую функцию type() как в:

    if type(n) == type(Node):
        do this
    elif type(n) == type(str)
        do this

Это хороший способ сделать это?

Обновление 1: Python 3 имеет аннотацию для параметров функции. Их можно использовать для проверки типов с помощью инструмента: http://mypy-lang.org/

4b9b3361

Ответ 1

Используйте isinstance(). Пример:

if isinstance(n, unicode):
    # do this
elif isinstance(n, Node):
    # do that
...

Ответ 2

>>> isinstance('a', str)
True
>>> isinstance(n, Node)
True

Ответ 3

Нет, аргументы typechecking в Python не нужны. Это никогда необходимо.

Если ваш код принимает адреса как rawstring или как объект Node, ваш дизайн нарушен.

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

Typechecking вредит повторному использованию кода и снижает производительность. Имея функцию который выполняет разные действия в зависимости от типа переданного объекта подвержен ошибкам и имеет более сложное поведение, чтобы понять и поддерживать.

У вас есть следующие варианты:

  • Создайте конструктор объекта Node, который принимает rawstrings или функцию который преобразует строки в объекты Node. Сделайте свою функцию аргумент передается Node. Таким образом, если вам необходимо пройти string к функции, вы просто выполните:

    myfunction(Node(some_string))
    

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

  • Сделайте две функции, которые принимают объекты Node, и объекты, которые принимают rawstrings. Вы можете сделать один вызов другим внутренне, в большинстве случаев удобный способ (myfunction_str может создать объект Node и вызвать myfunction_node, или наоборот).

  • У объектов Node есть метод __str__ и внутри вашей функции, вызовите str() в полученный аргумент. Таким образом, вы всегда получаете строку путем принуждения.

В любом случае не typecheck. Это совершенно не нужно и имеет только отрицательные стороны. Рефакторируйте свой код вместо того, чтобы вам не нужно было проверять тип. Вы получаете только выгоду в этом, как в краткосрочной, так и в долгосрочной перспективе.

Ответ 4

Звучит так, будто вы ищете "универсальную функцию", которая работает по-разному в зависимости от приведенных аргументов. Это немного похоже на то, как вы получите другую функцию при вызове метода для другого объекта, но вместо того, чтобы просто использовать первый аргумент (объект/себя) для поиска функции, вместо которой вы используете все аргументы.

Turbogears использует что-то вроде этого для принятия решения о том, как преобразовать объекты в JSON - если я правильно помню.

Есть статья от IBM об использовании пакета диспетчера для такого рода вещей:

Из этой статьи:

import dispatch
@dispatch.generic()
def doIt(foo, other):
    "Base generic function of 'doIt()'"
@doIt.when("isinstance(foo,int) and isinstance(other,str)")
def doIt(foo, other):
    print "foo is an unrestricted int |", foo, other
@doIt.when("isinstance(foo,str) and isinstance(other,int)")
def doIt(foo, other):
    print "foo is str, other an int |", foo, other
@doIt.when("isinstance(foo,int) and 3<=foo<=17 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 3 and 17 |", foo, other
@doIt.when("isinstance(foo,int) and 0<=foo<=1000 and isinstance(other,str)")
def doIt(foo, other):
    print "foo is between 0 and 1000 |", foo, other

Ответ 5

Вы также можете использовать try catch для проверки типа:

def my_function(this_node):
    try:
        # call a method/attribute for the Node object
        if this_node.address:
             # more code here
             pass
    except AttributeError, e:
        # either this is not a Node or maybe it a string, 
        # so behavior accordingly
        pass

Вы можете увидеть пример этого в начале Python во втором о генераторах (страница 197 в моем выпуске), и я верю в Поваренную книгу Python. Много раз ловить AttributeError или TypeError проще и, по-видимому, быстрее. Кроме того, он может работать лучше всего таким образом, потому что тогда вы не привязаны к определенному дереву наследования (например, ваш объект может быть Node, или это может быть что-то другое, что имеет то же поведение, что и Node).