About Closure and Decorator in Python with examples.
Closure Closure means function wraps function . When you want to save the state of a function but don’t want to write a decent class, use closure.
1 2 3 4 5 6 7 def new_counter (): total = 0 def count (): nonlocal total total += 1 return total return count
then you can use it to count student amount of different classes:
1 2 3 4 5 math = new_counter() tennis = new_counter() math(), math(), math(), math(), math(), math() tennis(), tennis()
When closure search a variables, Local -> enclosing -> global -> builtin. In the example, total
stores in enclosing level.
Decorator Decorator wraps a closure with extra function, but won’t change the inner function, it returns a closure.
Example: Logger 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def my_logger (func ): def wrapper (*args, **kwargs ): print (f"calling {func.__name__} " ) return func(*args, **kwargs) return wrapper @my_logger def get_total (ls ): return sum (ls) ls = [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ] get_total(ls)
But decorated function’s function will become pretty confused about its identity. get_total.__name__
will return 'wrapper'
. Use @functools.wraps
to preserve original function info.
1 2 3 4 5 6 7 8 9 10 11 12 13 import functoolsdef my_logger (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): print (f"calling {func.__name__} " ) return func(*args, **kwargs) return wrapper @my_logger def get_total (ls ): return sum (ls) get_total.__name__
Example: Fibonacci 1 2 3 4 5 6 7 8 9 10 11 12 13 class MyCache (object ): def __init__ (self, func ): self.func = func self.cache = {} def __call__ (self, *args ): if not args in self.cache: self.cache[args] = self.func(*args) return self.cache[args] @MyClass def fib (n ): return 1 if n <= 1 else fib(n - 1 ) + fib(n - 2 )
Example: Singleton 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def singleton (cls ): instances = {} def wrapper (*args, **kwargs ): if cls not in instances: instances[cls] = cls(*args, **kwargs) return instances[cls] return wrapper @singleton class MyClass : def __init__ (self, name ): self.name = name def submit (self ): return f"{self.name} Submitted!!!" c1 = MyClass('hello' ) c2 = MyClass('world' ) c1 == c2 c1.name, c2.name
So the next step, is refactoring my report & log modules in the next sprint.