<jupyter_start><jupyter_text>functools module  partial<jupyter_code>import time

# func from some a library you don't have control of
def create_token(company, role, user):
    return '{company}:{secret}:{user}:{role}'.format(company=company, 
                                                     secret=int(time.time()),
                                                     role=role, 
                                                     user=user)

token = create_token('elife', 'admin', '<PASSWORD>')
print(token)
# imagine you always want the company set to `elife`
# you could use partial

from functools import partial

partial_create_token = partial(create_token, 'elife')

users = (('admin', 'Rick'), ('standard', 'Morty'), ('read-only', 'Jerry'))

for user in users:
    token = partial_create_token(user[0], user[1])
    print(token)<jupyter_output><empty_output><jupyter_text>lru_cache (least recently used cache)<jupyter_code>'''Decorator to wrap a function with a memoizing callable that saves up to the maxsize most recent calls. 

It can save time when an expensive or I/O bound function is periodically called with the same arguments.

Since a dictionary is used to cache results, the positional and keyword arguments to the function must be hashable.

If maxsize is set to None, the LRU feature is disabled and the cache can grow without bound. The LRU feature performs best when maxsize is a power-of-two.

If typed is set to true, function arguments of different types will be cached separately. For example, f(3) and f(3.0) will be treated as distinct calls with distinct results.

'''
# Note that it was added in Python 3.2.

from functools import lru_cache
import requests

from data_utils import ARTICLES_URL


@lru_cache(maxsize=24)
def get_data(url=''):
    response = requests.get(url=url)
    if response.status_code == 200:
        return response.json()
    return {}


def multiple_requests():
    for req in range(20):
        data = get_data(ARTICLES_URL)


# make multiple requests
multiple_requests()
print(get_data.cache_info())  # CacheInfo(hits=19, misses=1, maxsize=24, currsize=1)

# and some more....
multiple_requests()
print(get_data.cache_info())  # CacheInfo(hits=39, misses=1, maxsize=24, currsize=1)

# now invalidate the cache
get_data.cache_clear()

# make multiple requests again
multiple_requests()
print(get_data.cache_info())  # CacheInfo(hits=19, misses=1, maxsize=24, currsize=1)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09780844155844155, 'fraction_numerical': 0.012987012987012988, 'mean_word_length': 3.6774193548387095, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '158408', 'license': 'MIT', 'n_tokens_mistral': 747, 'n_tokens_neox': 717, 'n_words': 248, 'path': '5_functools_module.ipynb', 'repo_name': 'seanwiseman/Python-beyond-the-basics', 'size': '2458'}