Skip to content

Functions

Functions - Функції

Що таке функція у Python

Summary

Функція - це блок коду, який виконується лише тоді, коли його викликають. Функції використовуються для організації коду та повторного використовуваним. Функцію оголошують за допомогою ключового слова def, за яким слідує ім'я функції та параметри у круглих дужках.

Тіло функції - це блок коду, який виконується при виклику функції. Він може містити різні операції та вказівки. Функція може повертати значення за допомогою ключового слова return. Це значення може бути використане у коді, який викликав функцію.

Для виклику функції використовується її ім'я та передаються аргументи у круглих дужках. Параметри - це значення, які функція отримує при виклику. Аргументи - це конкретні значення, передані функції при її виклику.

Параметри передаються - по значенню - immutable створюються копії - по посиланню - по reference - mutable - створюється посилання

def greet(name):
    return "Hello, " + name

message = greet("Alice")
print(message)  # Hello, Alice

В Python функція - це об'єкт. Все що є за межами функцій і класів - глобальні змінні.

Що таке args, kwargs і в яких випадках вони потрібні

Вирази *args і **kwargs оголошуються в сигнатурі функції. Вони означають, що всередині функції будуть доступні змінні з іменами args і kwargs (без зірочок). Можна використовувати інші імена, але це вважається поганим стилем.

args - це кортеж, який накопичує позиційні аргументи. kwargs - словник іменованих аргументів, де ключ - це ім'я параметра, а значення - значення параметра.

Якщо в функцію не передано жодних параметрів, змінні будуть відповідно пустим кортежем і пустим словником, а не None.

def func(*args, **kwargs):
    print(f"args: {args}, kwargs: {kwargs}")

func()  # args: (), kwargs: {}

Чому використання змінних об'єктів як параметрів за замовчуванням погана практика

Функція створюється один раз під час завантаження модуля. Іменовані параметри і їх значення за замовчуванням також створюються один раз і зберігаються в одному з полів об'єкта-функції (__defaults__). Це стосується списків, множин і словників.

У даному прикладі bar має значення порожнього списку. Список - це змінний об'єкт, тому значення bar може змінюватися від виклику до виклику.

def foo(bar=[]):
    bar.append(1)
    return bar

foo()
>>> [1]
foo()
[1, 1]
foo()
>>> [1, 1, 1]
foo.__defaults__
>>> ([1, 1, 1],)

Хорошим тоном вважається вказувати для параметра пусте незмінне значення, наприклад 0, None, '', False. В тілі функції перевіряти на False/None і створювати нову колекцію:

def foo(bar=None):
    if bar is None:
        bar = []
    bar.append(1)
    return bar
foo()
>>> [1]
foo()
>>> [1]
foo()
>>> [1]
foo.__defaults__
>>> (None,)

Чи можна передавати функцію як аргумент іншій функції

Так, в Пайтоні функція є об'єктом першого класу: можна присвоювати, передавати в функцію, видаляти.

Чи можна оголошувати функцію всередині іншої функції. Де вона буде видима

Так, можна. Така функція буде видимою лише всередині першої функції.

Що таке лямбди. Які їх особливості

Лямбда (lambda) - це анонімна функція, яка не резервує ім'я в просторі імен.

Лямбда може приймати необмежену кількість аргументів та мати будь-яку кількість параметрів. Однак лямбда-функція може мати лише один вираз або інструкцію, і вона завжди повертає результат цього виразу.

Лямбди використовується в ситуаціях, коли потрібна анонімна функція на короткий період часу. Наприклад передати в map, reduce, filter, або для сортування.

У Python лямбди можуть складатися лише з одного виразу. Використовуючи синтаксис дужок, можна оформити тіло лямбди у декілька рядків.

Не можна використовувати крапку з комою для розділення операторів.

a = lambda x, y: x + y
print(a(5, 6))  # 11

students = [{"name": "Alice", "age": 25}, {"name": "Bob", "age": 20}]
sorted(students, key=lambda x: x["age"])  # [{"name": "Bob", "age": 20}, {"name": "Alice", "age": 25}]

Наступні вирази при завантаженні модуля викличуть SyntaxError. Тіло лямбди може містити лише вирази. pass і raise є операторами.

nope = lambda: pass
riser = lambda x: raise Exception(x)

Як передаються значення аргументів у функцію або метод (by value or reference)?

Summary - Незмінні (імутабельні) об'єкти передаються "по значенню" (цілі числа, рядки). - Змінювані об'єкти такі, як списки, словники, передаються у вигляді посилань на об'єкти.

У мовах програмування, таких як C++, існують змінні, які зберігаються у стеку та динамічній пам'яті. При виклику функції ми поміщаємо всі аргументи у стек, після чого передаємо керування функції. Функція знає розміри та зміщення змінних у стеку та може вірно їх інтерпретувати.

При цьому у нас є два варіанти: скопіювати пам'ять змінної на стек або помістити посилання на об'єкт у динамічній пам'яті (або на більш високому рівні стеку). Очевидно, що при зміні значень на стеці функції, значення в динамічній пам'яті не зміняться, а при зміні області пам'яті за посиланням, ми змінюємо спільну пам'ять, відповідно всі посилання на цю ж область пам'яті "побачать" нове значення.

У Python відмовилися від подібного механізму, заміною є механізм присвоєння (assignment) імені змінної до об'єкту, наприклад, при створенні змінної: var = "john" Інтерпретатор створює об'єкт "john" та "ім'я" var, а потім зв'язує об'єкт з даним іменем. При виклику функції, нових об'єктів не створюється, замість цього в її області видимості створюється ім'я, яке зв'язується з існуючим об'єктом.

Проте в Python є змінні і незмінні типи даних. До других, наприклад, належать числа: при арифметичних операціях існуючі об'єкти не змінюються, а створюється новий об'єкт, з яким потім зв'язується існуюче ім'я. Якщо після цього зі старим об'єктом не зв'язано жодного імені, воно буде видалено за допомогою механізму підрахунку посилань. Якщо ж ім'я зв'язано зі змінною змінного типу, то при операціях з нею змінюється пам'ять об'єкта, відповідно всі імена, зв'язані з даною областю пам'яті, "побачать" зміни.

def inc(a):
    a += 1
    return a

a = 5
print(inc(a))
print(a) # -> 5

Links

Що таке замикання (Closure)

Summary

Замикання (closure) – це вкладена функція, яка дозволяє отримати доступ до змінних зовнішньої функції навіть після завершення виконання зовнішньої функції.

Замикання — це конструкція, яка дозволяє функції «запам'ятовувати» значення змінних з її зовнішнього оточення, навіть після того, як ці змінні вийшли з області видимості. Це досягається завдяки вкладеним функціям, які можуть мати доступ до змінних з оточуючої функції.

Синтаксично це виглядає як функція, що повністю знаходиться у тілі іншої функції. При цьому вкладена внутрішня функція містить посилання на локальні змінні зовнішньої функції. Коли зовнішня функція завершує виконання, контекст її виконання не зникає повністю. Замість цього, він зберігається у вигляді замикання.

def counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        print(f"Current count: {count}")

    return increment

counter1 = counter()
counter1()  # "Current count: 1"
counter1()  # "Current count: 2"

print(closure_func.__closure__)  # return tuple with cell objects

Під капотом використовується атрибут функції, який називається __closure__, який повертає кортеж об'єктів cell, що зберігають значення з області видимості функції, в якій була створена вкладена функція. Кожен елемент кортежу представляє собою значення однієї із змінних, що використовуються в замиканні.