Типы параметров функций в Python - PullRequest
226 голосов
/ 22 марта 2010

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

def my_func(param1, param2):
    # stuff

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

Ответы [ 12 ]

560 голосов
/ 27 января 2014

Другие ответы хорошо объяснили типизацию утки и простой ответ от 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 - для list, tuple, set и map соответственно.
  • Iterable - полезно для генераторов.
  • Any - когда это может быть что угодно.
  • Union - когда это может быть что-либо в указанном наборе типов, в отличие от Any.
  • Optional - когда может быть None. Сокращение для Union[T, None].
  • TypeVar - используется с дженериками.
  • Callable - используется в основном для функций, но может использоваться для других функций.

Это наиболее распространенные типовые подсказки. Полный список можно найти в документации для модуля ввода .

Вот старый пример с использованием методов аннотации, введенных в модуле ввода:

from typing import List

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

Одной из мощных функций является Callable, которая позволяет вам вводить аннотированные методы, которые принимают функцию в качестве аргумента. Например:

from typing import Callable, Any, Iterable

def imap(f: Callable[[Any], Any], l: Iterable[Any]) -> List[Any]:
    """An immediate version of map, don't pass it any infinite iterables!"""
    return list(map(f, l))

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


Ранее, когда один документированный код Python, например, с Sphinx , некоторые из вышеперечисленных функций можно было получить, написав строки документов, отформатированные так:

def pick(l, index):
    """
    :param l: list of integers
    :type l: list
    :param index: index at which to pick an integer from *l*
    :type index: int
    :returns: integer at *index* in *l*
    :rtype: int
    """
    return l[index]

Как видите, для этого требуется ряд дополнительных строк (точное число зависит от того, насколько явно вы хотите быть и как вы форматируете строку документации).Но теперь вам должно быть ясно, как PEP 3107 предоставляет альтернативу, которая во многих (всех?) Отношениях превосходна.Это особенно верно в сочетании с PEP 484 , который, как мы видели, предоставляет стандартный модуль, который определяет синтаксис для этих подсказок / аннотаций типов, который можно использовать таким образом, чтобы он был однозначным и точнымно гибкий, создавая мощную комбинацию.

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


Пример кода Python, который интенсивно использует подсказки типов, можно найти здесь .

140 голосов
/ 22 марта 2010

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

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

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

13 голосов
/ 22 марта 2010

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

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

def no_op(param1, param2):
    pass

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

Однако эта функция:

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

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

8 голосов
/ 03 апреля 2010

Многие языки имеют переменные определенного типа и значения. 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 (вы пытались получить доступ к несуществующему члену (методы тоже являются членами)).

7 голосов
/ 22 марта 2010

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

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

5 голосов
/ 22 марта 2010

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

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

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

3 голосов
/ 22 марта 2010

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

2 голосов
/ 31 июля 2015

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

Когда str функция вызывает __str__ метод класса, она тонко проверяет свой тип:

>>> class A(object):
...     def __str__(self):
...         return 'a','b'
...
>>> a = A()
>>> print a.__str__()
('a', 'b')
>>> print str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __str__ returned non-string (type tuple)

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

2 голосов
/ 22 марта 2010

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

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

1 голос
/ 07 апреля 2017

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

Как уже говорили другие, 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 ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...