Більше#
Наразі ми розглянули більшість різноманітних аспектів Python, які ви використовуватимете. У цьому розділі ми розглянемо ще деякі аспекти, які зроблять наші знання про Python більш повними.
Передача кортежів#
англійська: Passing tuples around
Ви коли-небудь хотіли, щоб функція повернула не один результат, а два? Це можливо. Все, що для цього потрібно, – використовувати кортеж.
код python (idle)
>>> def отримати_опис_помилки():
... return (2,"опис")
...
>>> номер_помилки,рядок_помилки = отримати_опис_помилки()
>>> номер_помилки
2
>>> рядок_помилки
'опис'
python code (idle)
>>> def get_error_details():
... return (2, 'details')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'details'
Зауважте, що використання a, b = <деякий вираз>
(англ.» a, b = <some expression>
») інтерпретує результат виразу як кортеж із двома значеннями.
Це також означає, що найшвидший спосіб поміняти дві змінні в Python можна наступним чином:
>>> a = 5; b = 8
>>> a, b
(5, 8)
>>> a, b = b, a
>>> a, b
(8, 5)
Спеціальні методи#
Існують певні методи, такі як __init__
і __del__
, які мають особливе значення в класах.
Для імітації певної поведінки вбудованих типів даних використовуються спеціальні методи. Наприклад, якщо ви хочете використовувати операцію індексування x[індекс]
(англ. x[key]
) для свого класу (так само, як ви використовуєте її для списків і кортежів), тоді все, що вам потрібно зробити, це реалізувати метод __getitem__()
і ваше завдання зроблено. До речі, саме цей метод Python використовує для класуlist
!
Деякі корисні спеціальні методи перераховані в наступній таблиці. Якщо ви хочете дізнатися про всі спеціальні методи, перегляньте посібник.
__init__(self, ...)
Цей метод викликається безпосередньо перед тим, як новостворений об’єкт повертається для використання.
__del__(self)
Викликається безпосередньо перед знищенням об’єкта (що має непередбачуваний час, тому уникайте цього)
__str__(self)
Викликається, коли ми використовуємо функцію
print
абоstr()
.
__lt__(self, other)
Викликається, коли використовується оператор менше ніж (<). Так само існують спеціальні методи для всіх операторів (+, > тощо)
__getitem__(self, key)
Викликається, коли використовується операція індексування
x[індекс]
.
__len__(self)
Викликається, коли для об’єкта послідовності використовується вбудована функція
len()
.
Блоки окремих операторів Блоки в один вираз#
англійська: Single Statement Blocks
Ми бачили, що кожен блок рядків коду відокремлений від решти власним рівнем відступу. Що ж, є одне застереження. Якщо ваш блок рядків коду містить лише один вираз, ви можете вказати його в одному рядку, скажімо, умовного оператора або оператора циклу. Наступний приклад повинен прояснити це:
>>> flag = True
>>> if flag: print('Yes')
...
Yes
Зверніть увагу, що єдиний оператор використовується в рядку, а не як окремий блок. Хоча ви можете використовувати це, щоб зробити свою програму меншою, я наполегливо рекомендую уникати цього скороченого методу, за винятком перевірки помилок, головним чином тому, що буде набагато легше додати додатковий оператор, якщо ви використовуєте належний відступ.
Лямбда-форми#
Ключове слово lambda
використовується для створення нових функціональних об’єктів (нових функцій та повернення їх значення у час виконання програми). По суті, “лямбда” приймає параметр, за яким слідує один вираз. Лямбда стає тілом функції. Значення цього виразу повертається новою функцією.
код python more_lambda_ukr.py
бали = [{'x': 2, 'y': 3},
{'x': 4, 'y': 1}]
бали.sort(key=lambda i: i['y'])
print(бали)
Висновок:
[{'x': 4, 'y': 1}, {'x': 2, 'y': 3}]
Як це працює
Зверніть увагу, що метод sort
класа list
може приймати параметр key
, який визначає спосіб сортування списку (зазвичай ми думаємо тільки про сортування за зростанням або за спаданням). У нашому випадку ми хочемо виконати спеціальне сортування, і для цього нам потрібно написати функцію. Замість написання окремого блоку def
для функції, яка використовуватиметься лише в цьому місці, ми використовуємо лямбда-вираз для створення нової функції.
Генератори списків#
англійська: List Comprehension
Генератори списків використовується для отримання нового списку з існуючого списку. Уявіть, що є список чисел, на основі якого потрібно отримати новий список, що складається з усіх чисел, помножених на 2, але тільки за умови, що саме число більше 2. Генератори списків ідеально підходить для таких ситуацій.
Приклад англійською (зберегти як more_list_comprehension.py
):
код python more_list_comprehension_ukr.py
список_перший = [2, 3, 4]
список_другий = [2*i for i in список_перший if i > 2]
print(список_другий)
Висновок:
$ python more_list_comprehension_ukr.py
[6, 8]
Як це працює
Тут ми отримуємо новий список, вказуючи маніпуляцію, яку потрібно виконати (2*i
), коли виконується певна умова (if i > 2
). Зауважте, що вихідний список залишається без змін.
Перевага використання генераторів списків полягає в тому, що воно зменшує кількість шаблонного коду, необхідного, коли ми використовуємо цикли для обробки кожного елемента списку та збереження його в новому списку.
Передача кортежів та словників у функції#
англійська: Receiving Tuples and Dictionaries in Functions
Існує спеціальний спосіб для отримання параметрів переданих функції, у вигляді кортежу або словника за допомогою префікса *
або **
відповідно. Це корисно, коли функція приймає змінну кількість аргументів.
код python (idle)
>>> def сума_зведення_у_ступінь(зведення_у_ступінь, *args):
... '''Повертає суму кожного аргументу, зведеного до вказаного степеня.'''
... результат = 0
... for i in args:
... результат += pow(i, зведення_у_ступінь)
... return результат
...
>>> сума_зведення_у_ступінь(2, 3, 4)
25
>>> сума_зведення_у_ступінь(2, 10)
100
python code (idle)
>>> def powersum(power, *args):
... '''Return the sum of each argument raised to the specified power.'''
... total = 0
... for i in args:
... total += pow(i, power)
... return total
...
>>> powersum(2, 3, 4)
25
>>> powersum(2, 10)
100
Оскільки ми маємо префікс *
у змінній args
, усі додаткові аргументи, передані функції, зберігаються в args
як кортеж. Якби замість нього використовувався префікс **
, додаткові параметри вважалися б парами ключ/значення словника.
Інструкція assert#
англійська: The assert statement
Інструкція assert
використовується для підтвердження того, чи є задана умова істинною чи ні. Якщо умова виконується, нічого не відбувається, але якщо вона не відповідає дійсності, інструкція assert
є ідеальним у цій ситуації.
Коли задана умова хибна, виникає помилка AssertionError.
Метод pop()
видаляє та повертає останній елемент зі списку.
код python (idle)
>>> мій_лист=["елемент"]
>>> assert len(мій_лист) >= 1
>>> мій_лист.pop()
'елемент'
>>> assert len(мій_лист) >= 1
Traceback (most recent call last):
File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
exec(code, self.locals)
File "<pyshell#4>", line 1, in <module>
AssertionError
python code (idle)
>>> mylist = ['item']
>>> assert len(mylist) >= 1
>>> mylist.pop()
'item'
>>> assert len(mylist) >= 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
Інструкцію assert
слід використовувати з обережністю. У більшості випадків краще перехоплювати винятки або вирішувати відповідну проблему автоматично, або відображати повідомлення про помилку користувачеві, а потім вийти із програми.
Декоратори#
Декоратори — це швидкий шлях до застосування функцій- обгорток (англ.“wrapper functions”). Це функція, яка дозволяє обгортнути іншу функцію для розширення її функціональності без безпосередньої зміни коду. «Обгорнути» функціональність тим самим кодом знову і знову- це корисно. Наприклад, я створив для себе “retry decorator”, який я можу просто застосувати до будь-якої функції, і якщо під час запуску викидається(виникає) будь-який виняток, він повторюється ще раз, максимум 5 разів із затримкою між кожною повторною спробою. Це особливо корисно в ситуаціях, коли ви намагаєтеся здійснити мережеве з’єднання з віддаленим комп’ютером:
код python decorators_en.py
from time import sleep
from functools import wraps
import logging
logging.basicConfig()
log = logging.getLogger("retry")
def retry(f):
@wraps(f)
def wrapper_function(*args, **kwargs):
MAX_ATTEMPTS = 5
for attempt in range(1, MAX_ATTEMPTS + 1):
try:
return f(*args, **kwargs)
except Exception:
log.exception("Attempt %s/%s failed : %s",
attempt,
MAX_ATTEMPTS,
(args, kwargs))
sleep(10 * attempt)
log.critical("All %s attempts failed : %s",
MAX_ATTEMPTS,
(args, kwargs))
return wrapper_function
counter = 0
@retry
def save_to_database(arg):
print("Write to a database or make a network call or etc.")
print("This will be automatically retried if exception is thrown.")
global counter
counter += 1
# This will throw an exception in the first call
# And will work fine in the second call (i.e. a retry)
if counter < 2:
raise ValueError(arg)
if __name__ == '__main__':
save_to_database("Some bad value")
Output:
$ python decorators_en.py
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})
Traceback (most recent call last):
File "more_decorator.py", line 14, in wrapper_function
return f(*args, **kwargs)
File "more_decorator.py", line 39, in save_to_database
raise ValueError(arg)
ValueError: Some bad value
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
Як це працює
Подивитися:
Відео: Декоратори Python робляться легко (англ.“Video : Python Decorators Made Easy”)
http://www.ibm.com/developerworks/linux/library/l-cpdecor.html
http://toumorokoshi.github.io/dry-principles-through-python-decorators.html
Відмінності між Python 2 і Python 3#
Подивитися:
Резюме#
У цій главі ми розглянули ще деякі функції Python, але не всі можливості Python. Однак на цьому етапі ми розглянули більшість того, що ви збираєтеся використовувати на практиці. Цього достатньо, щоб почати роботу з будь-якою програмою, яку ви збираєтеся створювати.
Далі ми обговоримо, як далі досліджувати Python.