~={green}Определения:=~ - Функция - именованный блок кода который выполняет определенную задачу или возвращает значение. * Функция - набор операторов, которые возвращают некоторое значение вызывающему объекту. В функцию также может быть передано ноль или более аргументов, которые могут использоваться при выполнении тела функции. - Такое определение функций допустимо, но вторая функция перезатрет первую. ```python def f():     print("QWERTY") def f():     print("ABC") f() f() ``` - Любая функция в python возвращает значение. Значение возвращается при помощи оператора return. Если функция не содержит оператор return, то по умолчанию такая функция вернет значение None. - Функция должна выполнять только одну операцию. Она должна выполнять ее хорошо. И ничего другого она делать не должна. Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем функции, то эта функция выполняет одну операцию. - Будьте последовательны в выражениях возврата: либо все операторы return в функции должны возвращать выражение, либо ни один из них не должен. Если какой-либо оператор return возвращает выражение, то оставшиеся операторы return тоже должны явно возвращать значение, не смотря на то, что python по умолчанию возвращает None. Статья по оформлению [[https://pythonchik.ru/osnovy/imenovanie-v-python]] ~={green}Пример оформления простой функции:=~ ```python # is - в названии функциии # нет лишних return def is_even(x):     return x%2 == 0 # пример использования без лишних сравнений number = int(input("Введите число: ")) while is_even(number): print(f'{number} является четным') number = int(input("Введите число: ")) print(f"Вы ввели нечетное число {number}, программа завершилась") ``` ~={green}Возврат кортежа=~ ```python # в return можно не ставить скобки, все равно будет возвращен кортеж def calc_square_and_perimeter(a, b): retun a * b, 2 * (a + b) ``` ~={green}Возврат списка=~ ```python # в данном случае нужны скобки [ ] def calc_square_and_perimeter(a, b): return [a * b, 2 * (a + b)] ``` ~={green}Передача аргументов=~ - При комбинированной передачи аргументов сначала должны быть указаны позиционные аргументы, а только потом именованные! - Параметры являются локальными переменными и они определяются в момент вызова функции. В параметры присваиваются ссылки на объекты, переданные в аргументы. - Параметры функции делятся на `_обязательные_` и `_необязательные_`. - Никогда не используйте изменяемые объекты в качестве значений по умолчанию. - Значение по умолчанию вычисляется только один раз при определении функции. ~={green}Изменяемые объекты в качестве параметров по умолчанию=~ * сперва присваивайте параметру значению _`None`_ * внутри функции проверяйте, если параметр принимает _`None`_, значит создаем пустой изменяемый объект ```python def append_to_list_2(value, my_list=None):     if my_list is None:         my_list = []     my_list.append(value)   print(my_list, id(my_list))     return my_list ``` ~={green}Множественное присваивание=~ Остальные значения сохраняются в виде списка с переменную "с" ```python a, b, *c = [1, True, 4, 6, 'hello ', 7, 9] 1, True, [4, 6, 'hello ', 7, 9] a, *b, c = [1, True, 4, 6, 'hello ', 7, 9] 1, [True, 4, 6, 'hello ', 7], 9 a, b, *c = [1, True, 4, 6, 'hello ', 7, 9] 1, True, [4, 6, 'hello ', 7, 9] a, *b, c = 'hello moto' h ['e', 'l', 'l', 'o', ' ', 'm', 'o', 't'] o a, *b, c = [1, 4] 1 [] 4 ``` ~={green}Передача переменного количество аргументов=~ ```python def my_func(*args) def my_func(**kargs) # Необязательный аргумент после *args def my_func(*args, foo=True) # Объединенный вариант передачи аргументов def my_func(a, b, *args, c, d=4, **kwargs): ``` В args будет ~={red}кортеж=~. В kwargs будет ~={red}словарь=~. ```python # При распаковке словаря передаются значения def print_args(a, b, c=15): print(a, b, c) dct = {'a': 5, 'b': 10} print_args(**dct) >>> 5, 10 15 # Распаковка при передачи списка и словаря my_list =[5, 19, 23, 88] my_dict = {'a': 11, 'b': 23} item_sum(*my_list, **my_dict) ``` ~={green}Как нельзя передавать аргументы=~ ~={red}Нельзя передавать позиционные аргументы после именованных!=~ ```python def my_func(a, b, *args): print(f'{a=}, {b=}, {args=}') # Будет ошибка при передаче аргументов по ключам my_func(a=20, b=20, 30, 40, 50) ``` ~={green}Только ключевые аргументы=~ Все параметры, которые стоят справа от `*` должны принимать значения только по ключу. Все параметры, которые стоят слева от `/` должны принимать значения только по позиции. `*` - только ~={magenta}ключевые=~ аргументы `/` - как ~={magenta}позиционные=~ так и ~={magenta}ключевые=~ аргументы ```python def my_func(*, a, b): print(f'{a=}, {b=}') # Так передать можно my_func(b=30, a=40) # А так нельзя my_func(10, b=20) ``` ~={green}Объект первого класса=~ - С ним можно работать как с переменными - Он может быть передан в функцию как аргумент - Он может быть возвращен из функции как результат - Он может быть включен в другие структуры данных. Например, быть элементом словаря или списка ~={green}Пространство имен=~ **Встроенное пространство имен** (`build-in namespace`) представляет собой набор имен всех встроенных функций и объектов в Python.  ```python # Выведет все объекты встроенного пространства имен print(dir(__builtins__)) ``` **Глобальное пространство имен** (`global namespace`) содержит имена, определенные на уровне основной программы, и создаётся сразу при запуске тела этой программы. **Локальное пространство имен** (`local namespace`) содержит имена, которые доступны только внутри определенной функции. Локальная область создается при вызове функций, причем каждый раз, когда вы вызываете функцию, будет создаваться новая локальная область видимости. Все параметры и имена, которые вы создаете внутри функции, образуют локальную область этой функции в пределах одного конкретного вызова. Когда функция завершает работу, локальная область видимости уничтожается, а имена забываются ```python # Вывести локальное пространство имен print(locals()) ``` **Объемлющее пространство имен** возникает когда определение одной функции вкладывается в другую. ```python def main_func(): a = 1 def inner_func(): print('Печатаем a из inner_func', a) print('Печатаем b из inner_func', b) # Вот тут будет ошибка NameError inner_func() b = 2 print('Печатаем a из main_func', a) print('Печатаем b из main_func', b) main_func() ``` Для изменения переменной `a` в inner нужно использовать `nonlocal`. ~={green}Создание атрибутов функциям =~ ```python def get_product(a: int, b: int) -> int: return a + b get_product.category = 'math' get_product.type_action = 'arithmetic operation' # Или через setattr setattr(obj, name_attr, value) ``` ~={green}Вложенные функции (inner function)=~ Вложенные функции имеют доступ к параметрам и к переменным, определенным во внешней функции. Применяются в замыканиях. Определяются в момент вызова родительской функции. ~={yellow}Функция высшего порядка=~ - это функция, которая может принимать другие функции в качестве аргументов и/или возвращать функции в качестве выходных данных. ```python def get_math_func(operation='+'): def add(a, b): return a + b # Вызов сразу с помощью дополнительного оператора вызова print(get_math_func('+')(3, 4)) ``` Аннотация вложенных функций: ```python from typing import Callable def get_math_func(operation: str) -> Callable[[int, int], int]: ... ``` ~={green}Замыкания=~ Замыкание - функция, которая находится внутри другой функции и ссылается на переменные объявленные в теле объемлющей функции (нелокальные переменные). Замыкание заставляет внутреннюю функцию сохранять состояние ее окружения при вызове. ```python def make_greeter(greeting): def greet(name): return f"{greenting}, {name}!" return greet greeter = make_greeter("Hello") print(greeter("World")) # Выведет "Hello, World!" print(greeter("Ivan")) # Выведет "Hello, Ivan!" ``` ~={green}Декораторы=~ Декоратор - это функция, которая принимает исходную функцию и возвращает другую функцию, как правило, с дополненным поведением. ```python def decorator(func):     def inner(*args, **kwargs):         print('Стартуем декоратор')         func(*args, **kwargs)         print('Заканчиваем декоратор')     return inner my_fun = decorator(my_func) ``` ```python # Декоратор с параметром def decorator_factory(a, b):     print('Запуск функции создания декоратора')     def decorator(fn):         print('Запуск декоратора')         def wrapper(*args, **kwargs):             print('Запуск функции wrapper')             print('Переданные аргументы: ', a, b)             return fn(*args, **kwargs)         return wrapper     return decorator @decorator_factory() # Обратите внимание на оператор вызова def original_func(10, 20): print('Запуск оригинальной функции') ``` ~={green}Итераторы=~ - ~={magenta}Итерация=~ - процесс перебора элементов объекта в цикле - ~={magenta}Итерируемый объект=~ - это объект, который поддерживает итерацию. - ~={magenta}Итератор=~ - это специальный объект-перечислитель, обязанность которого заключается в перемещении элементов коллекции. Итератор выдает по одному следующий элемент последовательности. Когда элементов больше не останется, он выбросит исключение StopIteration. Любой объект, который может взаимодействовать с функцией ~={magenta}next=~, считается итератором. ```python # создание итератора my_str = 'qwery' iterator = iter(my_str) ``` next() - получает следующее значения итератора ```python my_list = [1, 2, 3, 4, 5] it = iter(my_list) print(next(it)) # получаем первый элемент из итератора print(next(it)) # получаем второй элемент из итератора print(next(it)) print(next(it)) print(next(it)) # получаем пятый элемент из итератора # следующий вызов next(it) выбросит исключение StopIteration # print(next(it)) ``` ~={magenta}Цикл for =~при помощи ~={magenta}iter=~ запрашивает у объекта его ~={magenta}итератор=~. ```python s = 'Python' for letter in s: print(letter) # for будет развернут в: s = 'Python' it = iter(s) while True: try: print(next(it)) except StopIteration: del it break ``` ~={magenta}Итератор=~ - это не сама коллекция, а механизм обхода элементов или способ получения следующего элемента в коллекции. Поэтому итераторы намного эффективнее расходуют память в отличие от итерируемых объектов. Функции ~={magenta}map, filter, zip, enumerate, open=~ возвращают ~={magenta}итератор=~. ~={green}Функции all и any=~ Любое непустое значение преобразуется к значению ~={magenta}True=~, пустые значения - к ~={magenta}False=~. ```python print(bool(0)) # False print(bool('')) # False print(bool([])) # False print(bool({})) # False print(bool(None)) # False print(bool(False)) # False print(bool(67)) # True print(bool(-45)) # True print(bool('.')) # True print(bool(' ')) # True print(bool('qwerty')) # True print(bool([0])) # True print(bool(True)) # True ``` ~={magenta}Функция all()=~ принимает итерируемую последовательность и возвращает `True` , когда все элементы в этой последовательности правдивы. Если хотя бы один элемент пустой, вернется `False`. ~={magenta}Функция any()=~ принимает на вход итерируемую последовательность и возвращает `True`, _если хотя бы один_ из элементов коллекции будет правдивым (непустым). Если все элементы последовательности ложные (пустые), вернется `False`. ```python value = 100 condition_1 = value % 2 != 0 condition_2 = value > 50 condition_3 = value < 1000 print(all((condition_1, condition_2, condition_3))) # False print(any([condition_1, condition_2, condition_3])) # True ``` ```python words = ['notice', 'deter', 'west', 'brush', 'stadium', 'package', 'price', 'clothes', 'sword', 'apology'] check = [len(word) >= 4 for word in words] print(all(check)) # Все ли слова больше или равны 4х символов print(all([len(word) > 4 for word in words])) # Все ли слова больше 4х символов print(any([len(word) > 7 for word in words])) # Хотя бы одно слово больше 7х символов print(any([len(word) >= 7 for word in words])) # Хотя бы одно слово больше или равно 7 символов ``` ~={yellow}Функция map=~ Возвращает итератор ```python # Преобразование к списку a = [-1, 2, -3, 4, -5] b = list(map(abs, a)) print(b) # Можно передавать методы объктов a = ['hello', 'hi', 'good morning'] b = list(map(str.upper, a)) print(b) # Можно передвать lambda list_strings = ['hello', 'hi', 'good morning'] b = list(map(lambda x: x[::-1], list_strings)) print(b) ``` ~={yellow}Функция map и несколько последовательностей=~ ```python def power(base: int, exp: int) -> int: return base ** exp bases = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100] exponents = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] result = list(map(power, bases, exponents)) ``` ~={yellow}Функция filter=~ ```python def greater_10(x: int) -> bool: return x > 10 numbers = [14, 0, 5, 79, 645, 7952, 18, 0, 192, 471] numbers_gt_10 = filter(greater_10, numbers) ```