Типы параметров функций в Python

Если я не ошибаюсь, создание функции в Python работает так:

def my_func(param1, param2):
    # stuff

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

11 ответов


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

Это не имеет ничего общего с имена. А имя в Python нет "типа": если и когда имя определено, имя относится к объект и объект имеет тип (но это на самом деле не заставляет тип на имя: имя - это имя).

имя в Python вполне может ссылаться на разные объекты в разное время (как и в большинстве языков программирования, хотя и не во всех) - и нет никаких ограничений на имя, так что, если оно когда-то ссылалось на объект типа X, то оно навсегда ограничено ссылаться только на другие объекты типа X. Ограничения на имена не являются частью концепции "сильного набора текста", хотя некоторые энтузиасты static печатать (где имена do get constrained, и в статическом, АКА время компиляции, мода тоже) злоупотребляют термином таким образом.


другие ответы проделали хорошую работу по объяснению ввода утки и простой ответ от tzot:

Python не имеет переменных, как и другие языки, где переменные имеют тип и значение; он имеет имена, указывающие на объекты, которые знают их тип.

, одна интересная вещь изменилась с 2010 года (когда был впервые задан вопрос), а именно реализация PEP 3107 (реализовано в Python 3). Теперь вы можете указать тип параметра и тип возвращаемого типа функции такой:

def pick(l: list, index: int) -> int:
    return l[index]

мы можем здесь видеть, что pick принимает 2 параметра, список l и целое число index. Он также должен возвращать целое число.

Итак, здесь подразумевается, что l - это список целых чисел, которое можно увидеть без особых усилий, но для более сложных функций, это может быть немного запутанным, как к тому, что список должен содержать. Мы также хотим значение по умолчанию index к 0. Чтобы решить эту проблему, вы можете написать pick вроде вот этого:

def pick(l: "list of ints", index: int = 0) -> int:
    return l[index]

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

важно отметить, что Python не будет поднимать TypeError если вы передаете поплавок в index, причина этого является одним из основных моментов в Философия дизайна Python: "мы все здесь взрослые люди по обоюдному согласию", что означает, что вы должны знать, что вы можете передать функции и что вы не можете. Если вы действительно хотите написать код, который выдает TypeErrors, вы можете использовать isinstance функция для проверки того, что переданный аргумент имеет правильный тип или подкласс этого типа:

def pick(l: list, index: int = 0) -> int:
    if not isinstance(l, list):
        raise TypeError
    return l[index]

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

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


аннотация типа получила гораздо больше внимания в Python 3.5 с введением PEP 484 который вводит стандартный модуль для подсказок типа.

этот синтаксис пришел из дополнительного инструмента проверки статического типа mypy (GitHub), что находится в разработке (и PEP 484 совместимость).

с модулем ввода поставляется с довольно полной коллекцией подсказок типа, в том числе:

  • List, Tuple, Set, Map - for list, tuple, set и map соответственно.
  • Iterable - полезные для генераторов.

вы не указываете тип. Метод завершится ошибкой (во время выполнения), только если он попытается получить доступ к атрибутам, которые не определены в переданных параметрах.

Итак, эта простая функция:

def no_op(param1, param2):
    pass

... не потерпит неудачу, независимо от того, какие два args передаются.

однако, этой функции:

def call_quack(param1, param2):
    param1.quack()
    param2.quack()

... сбой во время выполнения, если param1 и param2 не оба имеют вызываемые атрибуты с именем quack.


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

в других языках, когда вы говорите:

a = 1

затем (обычно целочисленная) переменная изменяет свое содержимое на значение 1.

В Python,

a = 1

означает "использовать имя a для обозначения объекта 1". Вы можете сделать следующее в интерактивном сеансе Python:

>>> type(1)
<type 'int'>

функции type С объектом 1; поскольку каждый объект знает свой тип, легко для type чтобы узнать указанный тип и вернуть его.

аналогично, когда вы определяете функцию

def funcname(param1, param2):

функция получает два объекта и называет их param1 и param2, независимо от их типов. Если вы хотите убедиться, что полученные объекты относятся к определенному типу, Закодируйте функция, как если бы они были необходимого типа(ов) и поймать исключения, которые выдаются, если они не являются. Исключения, как правило,TypeError (вы использовали недопустимую операцию) и AttributeError (вы пытались получить доступ к несуществующему члену (методы тоже являются членами) ).


Python не сильно типизирован в смысле статической проверки или проверки типа во время компиляции.

большинство кода Python подпадает под так называемый "Утиной Типизацией" -- например, вы ищете метод read на объекте -- вам все равно, является ли объект файлом на диске или сокетом, вы просто хотите прочитать из него N байтов.


As Алекс Мартелли объясняет,

нормальный, обновления, предпочтительное решение-это почти всегда "утиной типизацией": попробуйте использовать этот аргумент, как если бы он был определенного желательного типа, сделать это в try/except выписка ловить все исключения, которые могут возникнуть, если аргумент был не в том, что тип (или любого другого типа красиво утку,-подражая его;-), а в предложении except, попробовать что-то другое (с помощью аргумента "как если бы" он был какой-то другой тип.)

прочитайте остальную часть его поста для получения полезной информации.


Python не заботится о том, что вы передаете его функциям. Когда вы звоните my_func(a,b), переменные param1 и param2 будут содержать значения a и b. Python не знает, что вы вызываете функцию с соответствующими типами, и ожидает, что программист позаботится об этом. Если ваша функция будет вызываться с различными типами параметров, вы можете обернуть код доступа к ним с помощью блоков try/except и оценить параметры любым способом.


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

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


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

, когда


в Python все имеет тип. Функция Python будет делать все, что ее попросят сделать, если тип аргументов поддерживает ее.

пример: foo добавить все, что может быть __add__ed ;) не беспокоясь о своем типе. Так что, чтобы избежать неудачи, вы должны предоставить только те вещи, которые поддерживают добавление.

def foo(a,b):
    return a + b

class Bar(object):
    pass

class Zoo(object):
    def __add__(self, other):
        return 'zoom'

if __name__=='__main__':
    print foo(1, 2)
    print foo('james', 'bond')
    print foo(Zoo(), Zoo())
    print foo(Bar(), Bar()) # Should fail

Я не видел этого в других ответах, поэтому я добавлю это в банк.

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

одним из основных инструментов для этого является isinstance() функция.

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

def process(data):
    if not isinstance(data, bytes) and not isinstance(data, bytearray):
        raise TypeError('Invalid type: data must be a byte string or bytearray, not %r' % type(data))
    # Do more stuff

Python также предоставляет всевозможные инструменты для копания в объектах. Если вы смелы, вы даже можете использовать importlib для создания собственных объектов произвольных классов на лету. Я сделал это, чтобы воссоздать объекты из JSON данных. Это был бы кошмар в статике. язык как C++.