Functions
Functions - Функції⚑
Що таке функція у Python⚑
Summary
Функція - це блок коду, який виконується лише тоді, коли його викликають. Функції використовуються для організації коду та повторного використовуваним. Функцію оголошують за допомогою ключового слова
def
, за яким слідує ім'я функції та параметри у круглих дужках.
Тіло функції - це блок коду, який виконується при виклику функції. Він може містити різні операції та вказівки. Функція може повертати значення за допомогою ключового слова return
. Це значення може бути використане у коді, який викликав функцію.
Для виклику функції використовується її ім'я та передаються аргументи у круглих дужках. Параметри - це значення, які функція отримує при виклику. Аргументи - це конкретні значення, передані функції при її виклику.
Параметри передаються - по значенню - immutable створюються копії - по посиланню - по reference - mutable - створюється посилання
В Python функція - це об'єкт. Все що є за межами функцій і класів - глобальні змінні.
Що таке args
, kwargs
і в яких випадках вони потрібні⚑
Вирази *args
і **kwargs
оголошуються в сигнатурі функції. Вони означають, що всередині функції будуть доступні змінні з іменами args
і kwargs
(без зірочок). Можна використовувати інші імена, але це вважається поганим стилем.
args
- це кортеж, який накопичує позиційні аргументи. kwargs
- словник іменованих аргументів, де ключ - це ім'я параметра, а значення - значення параметра.
Якщо в функцію не передано жодних параметрів, змінні будуть відповідно пустим кортежем і пустим словником, а не None
.
Чому використання змінних об'єктів як параметрів за замовчуванням погана практика⚑
Функція створюється один раз під час завантаження модуля. Іменовані параметри і їх значення за замовчуванням також створюються один раз і зберігаються в одному з полів об'єкта-функції (__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
є операторами.
Як передаються значення аргументів у функцію або метод (by value or reference)?⚑
Summary - Незмінні (імутабельні) об'єкти передаються "по значенню" (цілі числа, рядки). - Змінювані об'єкти такі, як списки, словники, передаються у вигляді посилань на об'єкти.
У мовах програмування, таких як C++, існують змінні, які зберігаються у стеку та динамічній пам'яті. При виклику функції ми поміщаємо всі аргументи у стек, після чого передаємо керування функції. Функція знає розміри та зміщення змінних у стеку та може вірно їх інтерпретувати.
При цьому у нас є два варіанти: скопіювати пам'ять змінної на стек або помістити посилання на об'єкт у динамічній пам'яті (або на більш високому рівні стеку). Очевидно, що при зміні значень на стеці функції, значення в динамічній пам'яті не зміняться, а при зміні області пам'яті за посиланням, ми змінюємо спільну пам'ять, відповідно всі посилання на цю ж область пам'яті "побачать" нове значення.
У Python відмовилися від подібного механізму, заміною є механізм присвоєння (assignment) імені змінної до об'єкту, наприклад, при створенні змінної: var = "john"
Інтерпретатор створює об'єкт "john" та "ім'я" var
, а потім зв'язує об'єкт з даним іменем. При виклику функції, нових об'єктів не створюється, замість цього в її області видимості створюється ім'я, яке зв'язується з існуючим об'єктом.
Проте в Python є змінні і незмінні типи даних. До других, наприклад, належать числа: при арифметичних операціях існуючі об'єкти не змінюються, а створюється новий об'єкт, з яким потім зв'язується існуюче ім'я. Якщо після цього зі старим об'єктом не зв'язано жодного імені, воно буде видалено за допомогою механізму підрахунку посилань. Якщо ж ім'я зв'язано зі змінною змінного типу, то при операціях з нею змінюється пам'ять об'єкта, відповідно всі імена, зв'язані з даною областю пам'яті, "побачать" зміни.
Links
- How do I write a function with output parameters (call by reference)?
- Интересности и полезности python. Часть 3
Що таке замикання (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
, що зберігають значення з області видимості функції, в якій була створена вкладена функція. Кожен елемент кортежу представляє собою значення однієї із змінних, що використовуються в замиканні.