Ця стаття була опублікована як частина Блогатон науки про дані
Вступ
Стандартна бібліотека Python має багато чудових модулів, які допоможуть у збереженні чистоти коду, його простоти та функціональності безумовно, один з
кешування
Почнемо з деяких з найпростіших, але потужних функцій функціональних модулів модуля. Почнемо з функцій кешування (а також декораторів) - lru_cache
,cache
та cached_property
. Перший з них - lru_cache
надає кеш останніх результатів виконання функцій, або іншими словами, запам'ятовує результат їх роботи:
from functools import lru_cache
import requests @lru_cache(maxsize=32)
def get_with_cache(url): try: r = requests.get(url) return r.text except: return "Not Found" for url in ["https://google.com/", "https://reddit.com/", "https://google.com/", "https://google.com/"]: get_with_cache(url) print(get_with_cache.cache_info())
# CacheInfo(hits=2, misses=4, maxsize=32, currsize=4)
print(get_with_cache.cache_parameters())
# {'maxsize': 32, 'typed': False}
У цьому прикладі ми робимо запити GET і кешуємо їх результати (до 32 результатів) за допомогою декоратора @lru_cache
. Щоб перевірити, чи справді працює кешування, можна перевірити інформацію кешу функцій за допомогою методу cache_info
що показує кількість звернень кешу та звернень. Декоратор також надає методи clear_cache
та cache_parameters
для скасування кешованих результатів та параметрів тестування відповідно.
Якщо вам потрібно більш детальне кешування, ви можете включити додатковий аргумент typed = true, що дозволяє кешувати різні типи аргументів окремо.
Ще один декоратор для кешування у функціональних інструментах - це функція, яка називається просто cache
. Це проста обгортка lru_cache
що опускає аргумент max_size, зменшуючи його, і не видаляє старі значення.
Ще один декоратор, який можна використовувати для кешування cached_property
. Як випливає з назви, він використовується для кешування результатів атрибутів класу. Ця механіка дуже корисна, якщо у вас є властивість, яку дорого обчислити, але вона залишається незмінною.
from functools import cached_property class Page: @cached_property def render(self, value): # Do something with supplied value... # Long computation that renders HTML page... return html
Цей простий приклад показує. Як можна використовувати, властивість кешування, наприклад, для кешування відображеної сторінки HTML, яку потрібно показувати користувачеві знову і знову. Те саме можна зробити для певних запитів до бази даних або тривалих математичних розрахунків.
Ще одна красуня cached_property
полягає в тому, що він працює лише під час пошуку, тому дозволяє нам змінювати значення атрибута. Після зміни атрибута раніше кешоване значення не зміниться, натомість нове значення буде обчислено та кешоване. Ви також можете очистити кеш, і все, що вам потрібно зробити, це видалити атрибут.
Я хочу закінчити цей розділ застереженням щодо всіх вищезазначених декораторів - не використовуйте їх, якщо ваша функція має деякі побічні ефекти або якщо вона створює змінні об’єкти кожного разу, коли вона викликається, оскільки це явно не ті функції, які ви хочете кешувати .
Порівняння та упорядкування
Напевно, ви вже знаєте, що в Python можна реалізувати оператори порівняння, такі як <
, >=
or ==
, С lt
, gt
or eq
. Однак усвідомлення кожного з них може бути досить неприємним eq
, lt
, le
, gt
or ge
. На щастя, у функціональних інструментів є декоратор @total_ordering
що може допомогти нам у цьому, тому що все, що нам потрібно - це реалізувати eq
один із решти методів, а решта декоратора буде створено автоматично.
from functools import total_ordering @total_ordering
class Number: def __init__(self, value): self.value = value def __lt__(self, other): return self.value Number(3))
# True
print(Number(1) = Number(15))
# True
print(Number(10) <= Number(2))
# False
Таким чином, ми можемо реалізувати всі розширені операції порівняння, незважаючи на наявність тільки еквалайзера та вручну lt. Найбільш очевидною перевагою є зручність, яка полягає в тому, що вам не потрібно писати всі ці додаткові магічні методи, але, ймовірно, важливіше зменшити кількість коду та його кращу читаність.
Перевантаження
Напевно, всіх нас вчили, що в Python немає перевантаження, але насправді існує простий спосіб реалізувати це за допомогою двох функцій із функтуолів, а саме методу єдиного диспетчеризації та/або єдиного диспетчеризації. Ці функції допомагають нам реалізувати алгоритм множинної відправки, що дозволяє динамічно набраним мовам програмування, таким як Python, розрізняти типи під час виконання.
Частковий
Ми всі працюємо з різними зовнішніми бібліотеками або фреймворками, багато з яких забезпечують функції та інтерфейси, які вимагають від нас передачі зворотного виклику, наприклад, для асинхронних операцій або прослуховування подій. Це нічого нового, але що, якщо нам також потрібно передати деякі аргументи разом із зворотним викликом. Тут він стане в нагоді у функціональних інструментах.partial
. Може бути використаний partial
заморозити деякі (або всі) аргументи функції шляхом створення нового об’єкта зі спрощеним підписом функції. Збентежений? Давайте розглянемо деякі практичні приклади:
def output_result(result, log=None): if log is not None: log.debug(f"Result is: {result}") def concat(a, b): return a + b import logging
from multiprocessing import Pool
from functools import partial logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("default") p = Pool()
p.apply_async(concat, ("Hello ", "World"), callback=partial(output_result, log=logger))
p.close()
p.join()
Наведений вище код показує, як ви можете ним користуватися partial
передати функцію ( output_result
) разом з аргументом ( log=logger
) як зворотний дзвінок. У цьому випадку ми будемо використовувати multiprocessing.apply_async, який асинхронно обчислює результат функції ( concat
) і повертає результат зворотного дзвінка. Однак, apply_async
він завжди передаватиме результат як перший аргумент, і якщо ми хочемо включити будь -які додаткові аргументи, як у випадку з log = logger, нам потрібно використовувати частковий.
Ми розглянули досить розширений варіант використання, і більш простим прикладом може бути звичайне створення функції, яка пише в stderr замість stdout:
import sys
from functools import partial print_stderr = partial(print, file=sys.stderr)
print_stderr("This goes to standard error output")
За допомогою цього простого трюку ми створили нову функцію, що викликається, яка завжди пройде file=sys.stderr
як іменований аргумент для виведення, що дозволяє нам спростити наш код і не потрібно щоразу вказувати значення іменованого аргументу.
І останній хороший приклад. Ми можемо використовувати partial
у поєднанні з маловідомою функцією iter
створити ітератор, передавши об’єкт, що викликається, та sentinel
in iter
, які можна застосувати так:
from functools import partial RECORD_SIZE = 64 # Read binary file...
with open("file.data", "rb") as file: records = iter(partial(file.read, RECORD_SIZE), b'') for r in records: # Do something with the record...
Зазвичай під час читання файлу ми хочемо перебирати рядки, але у випадку двійкових даних нам може знадобитися перебирати записи фіксованого розміру. Ви можете зробити це, створивши об'єкт, що викликається, за допомогою partial
який зчитує зазначену частину даних і передає її iter
для створення ітератора. Потім цей ітератор викликає функцію читання до тих пір, поки вона не досягне кінця файлу, завжди приймаючи лише вказаний розмір шматка ( RECORD_SIZE
). Нарешті, коли кінець файлу досягається, значення sentinel
(б ») повертається і ітерація припиняється.
декоратори
Ми вже говорили про деяких декораторів у попередніх розділах, але не про декораторів, щоб створити більше декораторів. Одним з таких декораторів є функціональні інструменти.wraps
. Щоб зрозуміти, навіщо це потрібно, давайте просто розглянемо приклад:
def decorator(func): def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") greet("Martin")
# Before Calling greet
# Hello, Martin!
# After Calling greet
У цьому прикладі показано, як можна реалізувати простий декоратор. Ми обгортаємо функцію, яка виконує певне завдання ( actual_func
) із зовнішнім декоратором, і він стає самим декоратором, який потім може бути застосований до інших функцій, наприклад, як у випадку greet
. Коли ви викликаєте функцію, greet
ви побачите, що він роздруковує повідомлення як з actual_func
і самостійно. Виглядає нормально, чи не так? Але що станеться, якщо ми зробимо це:
print(greet.__name__)
# actual_func
print(greet.__doc__)
# Inner function within decorator, which does the actual work
Коли ми називаємо назву та документацію прикрашеної функції, ми розуміємо, що вони замінені значеннями з функції декоратора. Це погано, оскільки ми не можемо переписати всі назви функцій та документацію, коли ми використовуємо якийсь декоратор. Як можна вирішити цю проблему? Звичайно, з функціональними інструментами.wraps
:
from functools import wraps def decorator(func): @wraps(func) def actual_func(*args, **kwargs): """Inner function within decorator, which does the actual work""" print(f"Before Calling {func.__name__}") func(*args, **kwargs) print(f"After Calling {func.__name__}") return actual_func @decorator
def greet(name): """Says hello to somebody""" print(f"Hello, {name}!") print(greet.__name__)
# greet
print(greet.__doc__)
# Says hello to somebody
Єдине призначення функції wraps
полягає в тому, щоб скопіювати ім'я, документацію, список аргументів тощо, щоб запобігти перезапису. Враховуючи це wraps
це також декоратор, ви можете просто додати його до нашої фактичної_функції, і проблема вирішена!
Зменшити
І останнє, але не менш важливе у функціональних модулях модуля - це це зменшення. Можливо, з інших мов ви знаєте це як fold
(Хаскелл). Ця функція бере ітерабельність і складає (додає) всі свої значення в одне. Для цього існує безліч програм, наприклад:
from functools import reduce
import operator def product(iterable): return reduce(operator.mul, iterable, 1) def factorial(n): return reduce(operator.mul, range(1, n)) def sum(numbers): # Use `sum` function from standard library instead return reduce(operator.add, numbers, 1) def reverse(iterable): return reduce(lambda x, y: y+x, iterable) print(product([1, 2, 3]))
# 6
print(factorial(5))
# 24
print(sum([2, 6, 8, 3]))
# 20
print(reverse("hello"))
# olleh
Як видно з коду, reduce
може спростити або згустити код в один рядок, що в іншому випадку було б набагато довшим. Враховуючи це, зазвичай поганою ідеєю є зловживання цією функцією лише заради зменшення коду, роблячи його «розумнішим», оскільки він швидко стає страшним і нечитабельним. З цієї причини, на мою думку, його слід використовувати помірно.
І якщо ви це пам’ятаєте reduce
часто скорочує все до одного рядка, з ним можна ідеально поєднувати partial
:
product = partial(reduce, operator.mul) print(product([1, 2, 3]))
# 6
І, нарешті, якщо вам потрібно більше, ніж просто остаточний «згорнутий» результат, ви можете скористатися accumulate
- з іншого чудового модуля itertools
. Для розрахунку максимуму його можна використати таким чином:
from itertools import accumulate data = [3, 4, 1, 3, 5, 6, 9, 0, 1] print(list(accumulate(data, max)))
# [3, 4, 4, 4, 5, 6, 9, 9, 9]
Висновок
Як ви бачите, функціональні інструменти мають багато корисних функцій та декораторів, які можуть полегшити ваше життя, але це лише вершина айсберга. Як я вже говорив на початку, у стандартній бібліотеці Python є багато функцій, які допомагають писати кращий код, тому на додаток до функцій, які ми розглянули тут, можна звернути увагу на інші модулі, такі як operator
or itertool.
Для будь -яких запитів ви можете натиснути вікно моїх коментарів. Я постараюся вирішити ваші внески і сподіваюся дати вам бажані результати. Ви також можете зв’язатися зі мною у LinkedIn:-
https://www.linkedin.com/in/shivani-sharma-aba6141b6/
Носії, показані в цій статті, не належать Analytics Vidhya і використовуються на розсуд автора.
споріднений
- '
- "
- 9
- Додатковий
- алгоритм
- ВСІ
- аналітика
- застосування
- аргументація
- стаття
- Краса
- КРАЩЕ
- Box
- call
- зміна
- код
- обчислення
- створення
- дані
- Database
- Відправка
- і т.д.
- Події
- виконання
- в кінці кінців
- Перший
- Заморожувати
- функція
- добре
- великий
- мобільний
- тут
- Як
- HTTPS
- ідея
- інформація
- IT
- зберігання
- мови
- бібліотека
- Лінія
- список
- Прослуховування
- Довго
- пошук
- Робить
- математики
- Медіа
- а саме
- Імена
- номера
- добре
- операції
- Думка
- Інше
- Платити
- басейн
- влада
- Програмування
- мови програмування
- власність
- Python
- читання
- облік
- зменшити
- REST
- результати
- Умови повернення
- наука
- простий
- Розмір
- So
- ВИРІШИТИ
- старт
- тест
- час
- us
- значення
- в
- слова
- Work
- працює
- світ
- X