function основное.md 19 KB

~={green}Определения:=~

  • Функция - именованный блок кода который выполняет определенную задачу или возвращает значение.
  • Функция - набор операторов, которые возвращают некоторое значение вызывающему объекту. В функцию также может быть передано ноль или более аргументов, которые могут использоваться при выполнении тела функции.
  • Такое определение функций допустимо, но вторая функция перезатрет первую.

    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}Пример оформления простой функции:=~

# 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}Возврат кортежа=~

# в return можно не ставить скобки, все равно будет возвращен кортеж
def calc_square_and_perimeter(a, b):
	retun a * b, 2 * (a + b)

~={green}Возврат списка=~

# в данном случае нужны скобки [ ]
def calc_square_and_perimeter(a, b):
	return [a * b, 2 * (a + b)]

~={green}Передача аргументов=~

  • При комбинированной передачи аргументов сначала должны быть указаны позиционные аргументы, а только потом именованные!
  • Параметры являются локальными переменными и они определяются в момент вызова функции. В параметры присваиваются ссылки на объекты, переданные в аргументы.
  • Параметры функции делятся на _обязательные_ и _необязательные_.
  • Никогда не используйте изменяемые объекты в качестве значений по умолчанию.
  • Значение по умолчанию вычисляется только один раз при определении функции.

~={green}Изменяемые объекты в качестве параметров по умолчанию=~

  • сперва присваивайте параметру значению None
  • внутри функции проверяйте, если параметр принимает None, значит создаем пустой изменяемый объект

    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}Множественное присваивание=~ Остальные значения сохраняются в виде списка с переменную "с"

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}Передача переменного количество аргументов=~

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}словарь=~.

# При распаковке словаря передаются значения
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}Нельзя передавать позиционные аргументы после именованных!=~

def my_func(a, b, *args):
    print(f'{a=}, {b=}, {args=}')

# Будет ошибка при передаче аргументов по ключам
my_func(a=20, b=20, 30, 40, 50)

~={green}Только ключевые аргументы=~ Все параметры, которые стоят справа от * должны принимать значения только по ключу. Все параметры, которые стоят слева от / должны принимать значения только по позиции. * - только ~={magenta}ключевые=~ аргументы / - как ~={magenta}позиционные=~ так и ~={magenta}ключевые=~ аргументы

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. 

# Выведет все объекты встроенного пространства имен
print(dir(__builtins__))

Глобальное пространство имен (global namespace) содержит имена, определенные на уровне основной программы, и создаётся сразу при запуске тела этой программы.

Локальное пространство имен (local namespace) содержит имена, которые доступны только внутри определенной функции.

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

# Вывести локальное пространство имен
print(locals())

Объемлющее пространство имен возникает когда определение одной функции вкладывается в другую.

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}Создание атрибутов функциям =~

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}Функция высшего порядка=~ - это функция, которая может принимать другие функции в качестве аргументов и/или возвращать функции в качестве выходных данных.

def get_math_func(operation='+'):
	def add(a, b):
		return a + b

# Вызов сразу с помощью дополнительного оператора вызова
print(get_math_func('+')(3, 4))

Аннотация вложенных функций:

from typing import Callable

def get_math_func(operation: str) -> Callable[[int, int], int]:
    ...

~={green}Замыкания=~

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

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}Декораторы=~

Декоратор - это функция, которая принимает исходную функцию и возвращает другую функцию, как правило, с дополненным поведением.

def decorator(func):
    def inner(*args, **kwargs):
        print('Стартуем декоратор')
        func(*args, **kwargs)
        print('Заканчиваем декоратор')
    return inner

my_fun = decorator(my_func)
# Декоратор с параметром
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=~, считается итератором.

    # создание итератора
    my_str = 'qwery'
    iterator = iter(my_str)
    

    next() - получает следующее значения итератора

    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}итератор=~.

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=~.

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.

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
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=~

Возвращает итератор

# Преобразование к списку
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 и несколько последовательностей=~

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=~

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)