Функции
Введение
Много интересного про функции можно прочитать в официальной документации
funcdef | ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite |
decorators | ::= decorator+ |
decorator | ::= "@" assignment_expression NEWLINE |
parameter_list | ::= defparameter ("," defparameter)* "," "/" ["," [parameter_list_no_posonly]] | parameter_list_no_posonly |
parameter_list_no_posonly | ::= defparameter ("," defparameter)* ["," [parameter_list_starargs]] | parameter_list_starargs |
parameter_list_starargs | ::= "*" [parameter] ("," defparameter)* ["," ["**" parameter [","]]] | "**" parameter [","] |
parameter | ::= identifier [":" expression] |
defparameter | ::= parameter ["=" expression] |
funcname | ::= identifier |
О чём говорит первая строка этой таблицы:
- Функция определяется с помощью ключевого слова def
- Перед def может быть объявлен декоратор
Пример использования
Создайте файл с раширением .py
Например functions_demo.py
Рассмотрим простейшую функцию, которая ничего не делает.
Чтобы её объявить используйте слово def затем придумайте название - например lazy
поставьте круглые скобки () и двоеточие
Общий синтаксис следующий
def имя_функции(): какой-то код
В нашем случае получается
def lazy(): pass
pass означает просто продолжать код ничего не предпринимая. Он применяется далеко не во всех функциях.
Наша функция создана, иначе говоря объявлена. Это значит, что она существует где-то в коде и может что-то сделать.
Но не делает. Чтобы функция что-то делала её нужно вызвать. В нашем случае достаточно написать её имя и круглые скобки.
lazy()
python functions_demo.py
Если никаких ошибок не допущено вывод будет пустым
def
Ключевое слово def используется для определения новых функций.
Оно связывает объект функции с именем
Выполняется в runtime
Hello World!
Объявим и вызовем функцию, которая пишет Hello World!
def hello(): print("Hello World!")
hello()
python functions_demo.py
Hello World!
Функцию не обязательно писать в одну строку. Лучше после двоеточия перейти на новую строку и сделать отступ из четырёх пробелов.
def hello():
print("Hello World!")
hello()
python functions_demo.py
Hello World!
Напишем функцию, которая складывает два числа.
def sum(first, second):
print(first + second)
sum(3,4)
python functions_demo.py
7
РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе
Конец рекламы. Если там пусто считайте это рекламой моей телеги
type hints
В соответствии с PEP 484 рекомендуется указывать ожидаемые типы данных как для параметров, так и для возвращаемого значения
# typehints.py def sum(first: float, second: float) -> float: return(first + second) print(sum(3.0, 4.6))
python typehints.py
7.6
Подробнее с подсказками о типах вы можете познакомиться в статье Type Hints
Параметры и аргументы
Параметры и аргументы это почти одно и то же, но термин параметры применяют во время
объявления функции а аргументы в момент вызова.
В некоторых источниках вместо термина
параметры
используется термин
формальные аргументы (formal arguments)
Проще всего понять на примере
def add(a, b): return a+b add(2, 3)
a и b это параметры, а 2 и 3 это аргументы
Типы аргументов
Аргументы могут передаваться как позиционные (positional) и как именованные (keyword).
Позиционные аргументы
Позиционные аргументы сопоставляются с параметрами (parameter или formal argument) в
соответсвии с порядком следования.
Пример передачи двух позиционных аргументов
a = sub(2, 3)
Предположим, функция sub() вычитает из первого аргумента второй.
sub(2, 3) = -1, sub(3, 2) = 1. Порядок имеет значение
Именованные аргументы
Именованные аргументы сопоставляются с параметрами по их имени.
Пример передачи двух именованных аргументов
a = div(divident=4, divisor=2)
Предположим, функция div() делит divident (делимое) на divisor (делитель). Порядок в котом передаются именованные аргументы не важен, значения будут присвоены по совпадении имён.
Способ, которым передаётся определённый аргумент, определяется при вызове функции.
Один и тот же аргумент может быть передан как позиционный и как именованный.
def main(): print(div()) print(div(9, 3)) print(div(divisor=5, divident=25)) def div(divident=2, divisor=1): return divident/divisor if __name__ == "__main__": main()
2.0 3.0 5.0
Сперва нужно передавать позиционные аргументы, затем именованные
# Вызов print(div(divisor=5, 25)) # Приведёт к ошибке
print(div(divisor=5, 25)) ^ SyntaxError: positional argument follows keyword argument
Ещё один пример здесь
Про передачу заранее неопределёного количества аргументов читайте статью *args **kwargs
Аргументы по умолчанию
Аргументы, у которых есть значения по умолчанию, должны быть переданы после аргументов, у которых их нет.
# Arguments with default values must come # after those without default values. def banner(message, border="-"): line = border * len(message) print(line) print(message) print(line) banner("www.HeiHei.ru") # will use default "-" banner("www.TopBicycle.ru", "*") banner("www.KickBrains.ru", border="|")
python default_argumet_values.py
------------- www.HeiHei.ru ------------- ***************** www.TopBicycle.ru ***************** ||||||||||||||||| www.KickBrains.ru |||||||||||||||||
Значения по умолчанию
Mutable vs Immutable
В качестве значений по умолчанию лучше использовать неизменяемые (immutable) объекты.
Если и использовать изменяемые объекты, то нужно понимать, что они получают значения
один раз - когда интерпретатор проходит объявление функции (def)
Пример
import time print("time.ctime()", time.ctime()) def show_default(arg=time.ctime()): print(arg) show_default() time.sleep(2) print("time.ctime()", time.ctime()) show_default()
time.ctime() Wed Jun 8 11:16:04 2022 Wed Jun 8 11:16:04 2022 time.ctime() Wed Jun 8 11:16:06 2022 Wed Jun 8 11:16:04 2022
Как видно из вывода в терминал - время уже ушло вперёд - 11:16:06 а функция как возвращала значение по умолчанию созданное в 11:16:04 так и продолжает это делать.
Рассмотрим ещё один пример. Теперь в качестве изменяемого объекта возьмём список
def add_spam(menu=[]): menu.append("spam") return menu breakfast = ['bacon', 'eggs'] print(add_spam(breakfast)) lunch = ['borsh'] print(add_spam(lunch)) print(add_spam()) print(add_spam()) print(add_spam())
['bacon', 'eggs', 'spam'] ['borsh', 'spam'] ['spam'] ['spam', 'spam'] ['spam', 'spam', 'spam']
В первых двух случаях (завтрак и обед) функция работает как задумано. Но, если вызвать её без аргументов создасться пустой список и он не будет пересоздан - в него просто добавляются новые и новые элементы.
Решить эту проблему можно сделав аргумент по умолчанию неизменяемым
def add_spam(menu=None): if menu is None: menu = [] menu.append("spam") return menu breakfast = ['bacon', 'eggs'] print(add_spam(breakfast)) lunch = ['borsh'] print(add_spam(lunch)) print(add_spam()) print(add_spam()) print(add_spam())
['bacon', 'eggs', 'spam'] ['borsh', 'spam'] ['spam'] ['spam'] ['spam']
return
Рассмотрим скрипт test_return.py
def test_return(): return "A" v = test_return() print(v)
python test_return.py
A
Всё выглядит хорошо, функция вернула строку, мы видим её в терминале.
Но что будет, если эту функцию импортировать, а не вызвать в том же скрипте
tree return_ex
return_ex ├── parent_test_return.py └── test_return.py 0 directories, 2 files
# test_return def test_return(): return "A"
# parent_test_return from test_return import test_return v = test_return() print(v)
python parent_test_return.py
A
Области видимости
Рассмотрим четыре стандартные области видимости
Local: | Внутри текущей функции |
Enclosing: | Внутри функции, вызывающей текущую |
Global: | Внутри модуля |
Built-in: | Встроенные в Python функции |
Эта четвёрка описывается акронимом LEGB
Области видимости не имеют прямой связи с блоками кода.
Например, цикл for, хотя и нуждется в дополнительном отступе, не вводит новых областей видимости.
count = 0 def show_count(): print(count) def set_count(c): count = c show_count() # 0 set_count(5) show_count() # 0
0 0
count = 0 def set_count(c): global count count = c show_count() # 0 set_count(5) show_count() # 0
0 5
Продолжить изучение областей видимости функции можно в статье о локальных функциях замыкания: области видимости
Вызываемые объекты
Рассмотрим файл callables.py
def is_even(x): return x % 2 == 0 print(callable(is_even)) # True is_odd = lambda x: x % 2 == 1 print(callable(is_odd)) # True # Classes are callable print(callable(list)) # True # Methods are callable print(callable(list.append)) # True class CallMe: def __call__(self): print("Called!") my_call_me = CallMe() print(callable(my_call_me)) # True # Strings are not callable print(callable("This is no callable")) # False
python callables.py
True True True True True False
from functools import reduce import operator print(operator.add(8, 9)) print(reduce(operator.add, [1, 2, 3, 4, 5])) numbers = [1, 2, 3, 4, 5] accumulator = operator.add(numbers[0], numbers[1]) for item in numbers[2:]: accumulator = operator.add(accumulator, item) print(accumulator) def mul(x, y): print(f"mul {x} {y}") return x * y print(reduce(mul, range(1, 10))) # reduce(mul, []) # TypeErr reduce(mul, [1]) # returns element without calling reduce print(reduce(mul, [1])) # 1 # Initial value is added as a first accumulated value values = [1, 2, 3]
17 15 15 mul 1 2 mul 2 3 mul 6 4 mul 24 5 mul 120 6 mul 720 7 mul 5040 8 mul 40320 9 362880 1 6 0 6
Встроенные функции и теория
all() |
any() |
chr() |
copy() |
enumerate() |
isinstance() |
iter() |
map() |
next() |
ord() |
random() |
sorted() |
type() |
uuid() |
unichr() |
zip() |