Типы параметров функции в Python
Если я не ошибаюсь, создание функции в Python работает так:
def my_func(param1, param2):
# stuff
однако на самом деле вы не даете типы этих параметров. Кроме того, если я помню, Python является строго типизированным языком, как таковой, похоже, что Python не должен позволять вам передавать параметр другого типа, чем ожидал создатель функции. Однако, как Python знает, что пользователь функции передает в соответствующие типы? Будет ли программа просто умереть, если это неправильный тип, предполагая, что функция действительно использует параметр? Вы должны указать тип?
11 ответов:
Python строго типизирован, потому что каждый объект и тип, каждый объект знает его тип, невозможно случайно или намеренно использовать объект типа "как если бы" это был объект разные тип, и все элементарные операции над объектом делегируются для своего типа.
Это не имеет никакого отношения к имена. А имя в Python не "есть тип": если и когда имя определено, имя относится к объект и объект имеет тип (но это на самом деле не заставляет тип на имя: имя - это имя).
имя в Python может отлично ссылаться на разные объекты в разное время (как и в большинстве языков программирования, хотя и не все) - и нет никаких ограничений на имя, так что, если оно когда-то ссылалось на объект типа X, то оно навсегда ограничено ссылаться только на другие объекты типа X. Ограничения на имена не являются частью концепции "сильного набора текста", хотя некоторые энтузиасты статический ввод текста (где имена do получить ограничение, и в статическом, АКА время компиляции, мода тоже) не злоупотреблять термином таким образом.
другие ответы сделали хорошую работу по объяснению утка набрав и простой ответ от tzot:
Python не имеет переменных, как и другие языки, где переменные имеют тип и значение; он имеет имена, указывающие на объекты, которые знают их тип.
, одна интересная вещь изменилась с 2010 года (когда вопрос был впервые задан), а именно реализация Пеп 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: "мы все здесь взрослые по обоюдному согласию", что означает, что вы должны быть в курсе того, что вы можете передать в функцию и что нельзя. Если вы действительно хотите писать код, который бросает типа ошибок вы можете использовать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- когда может нет. Сокращение для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, например,Сфинкс некоторые из вышеперечисленных функций можно получить, написав docstrings в следующем формате:
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, который сильно использует намек типа можно найти здесь.
вы не указываете тип. Метод завершится ошибкой (во время выполнения) только в том случае, если он попытается получить доступ к атрибутам, которые не определены в передаваемых параметрах.
Итак, эта простая функция:
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 байтов.
нормальный, обновления, предпочтительное решение-это почти всегда "утиной типизацией": попробуйте использовать этот аргумент, как если бы он был определенного желательного типа, сделать это в 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 stuffPython также предоставляет все виды инструментов для копания в объектах. Если вы смелы, вы даже можете использовать importlib для создания собственных объектов произвольных классов, на лету. Я сделал это, чтобы воссоздать объекты из JSON данных. Такая вещь была бы кошмаром в статике язык как C++.
Comments