Scribbling

Python: Decorator & Closure 본문

Computer Science/Python

Python: Decorator & Closure

focalpoint 2022. 3. 29. 11:49

 

Decorator is a callable that takes another function as its parameter. Decorator is executed during 'import time'. 

 

The key idea about closure you must understand is that it can access to 'nonlocal' variables outside the function.

Take a look at the below exmaple.

def make_averager():
    series = []
    def averager(new_val):
        series.append(new_val)
        return sum(series) / len(series)
    return averager

avg = make_averager()
avg(10)
avg(11)
print(avg(12))

 

Binding between freevars and closures are kept until the end.

print(avg.__code__.co_freevars)
print(avg.__closure__)

 

"Nonlocal" keyword is to define free variable. In the previous example, "series" was automatically interpreted as free variable as it is mutable data type. On the other hand, nonlocal keyword should be used in closure when dealing with immutable data types.

def make_averager():
    count = 0
    total = 0
    def averager(new_val):
        nonlocal count, total
        count += 1
        total += new_val
        return total / count
    return averager

avg = make_averager()
avg(10)
avg(11)
print(avg(12))

 

Now, back to decorator. Usually, we call a function in decorator. Below is the typical code format.

import time
import functools

def clock(func):
    @functools.wraps(func)
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        print(f"{func.__name__} took {elapsed:1.3f} seconds and returned {result!r}")
        return result
    return clocked

@clock
def iamfunc(iamparam:str)->str:
    '''
    iamfunc
    '''
    return iamparam + 'was passed'

iamfunc('what happend?')

 

@functools.wraps(func)

From above, what is this for? This is to maintain original function's properties as below.

print(iamfunc.__name__)
print(iamfunc.__doc__)

Without it, iamfunc.__name__ and iamfunc.__doc__ would have been replaced with clocked function's properties.

import time

def clock(func):
    def clocked(*args, **kwargs):
        t0 = time.time()
        result = func(*args, **kwargs)
        elapsed = time.time() - t0
        print(f"{func.__name__} took {elapsed:1.3f} seconds and returned {result!r}")
        return result
    return clocked

 

Decorators in STL

These three decorators are for class. For details, refer to below link.

@property       # for setter / getter method
@classmethod    # to define class method
@staticmethod   # to define static method

https://focalpoint.tistory.com/283

 

Python Grammar: Things that I forget often - by Shiba

1. Use deep copy list function copy() can be troublesome when the list has mutable objects. from copy import deepcopy l1 = [1, 2, [3, 4]] l2 = l1.copy() l3 = deepcopy(l1) l1[1] = 3 l1[2][0] = 4 prin..

focalpoint.tistory.com

 

functools.lru_cache(maxsize, typed) is for memoization.

- if typed is set to True, seperate result will be stored for different data types. (i.e. 1 & 1.0)

- all params should be hashable.

import functools

@functools.lru_cache()
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))

 

Python doesn't provide function overloading. As a result, it feels more complicated to deal with different data types. functools.singleddispath() is to specialize a certain function. 

from functools import singledispatch
from collections import abc

import numbers

@singledispatch
def iamfunc(iamparam):
    print(f"Not specialized Function Says: {iamparam!r} has data type of {type(iamparam)}.")

@iamfunc.register(str)
def _(string):
    print(f"Specialized Function Says: {string!r} has data type of str.")

@iamfunc.register(numbers.Integral)
def _(num):
    print(f"Specialized Function Says: {num!r} has data type of int.")

@iamfunc.register(abc.MutableSequence)
@iamfunc.register(tuple)
def _(seq):
    print(f"Specialized Function Says: {seq!r} has data type of seq.")

iamfunc([1, 2, 3])
iamfunc((4, 5))
iamfunc(3)
iamfunc(3.3)
iamfunc('hello world')

Notes:

- numbers.Inegral is abstract base class for int type.

- abc.MutableSequence is abstract base class for mutable sequence types.

 

For multiple decorators, they work like this.

@d1
@d2
def f():
    print('f')
    
f = d1(d2(f))

 

Now, a little bit more trickier part: decorators with params. Here we call echo as decorator factory.

import functools

def ehco(times):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            result = func(*args, **kwargs)
            return result * times
        return wrapper
    return decorator

@ehco(5)
def printer(text):
    return text

print(printer('What?'))
print(printer.__name__)

 

'Computer Science > Python' 카테고리의 다른 글

Python: Pythonic Object  (0) 2022.04.05
Python: Object References  (0) 2022.04.04
Python: Design Patterns  (0) 2022.03.25
Python: Memoryview function  (0) 2022.03.23
Python Immutable Dictionary, Set  (0) 2022.03.22