Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ## Description: Decorator that enforces purity on a function. Think of it as declaring a const function in C++.
- ## Why: Pure functions ensure that certain inputs always result in certain outputs, under the same transformations.
- ## Working with pure functions can ease your development because you never have to stop and worry and check
- ## whether using a certain function will implicitly mutate something. Things change only when YOU want them
- ## explicitly to change, via: `new_thing = transformation(old_thing)`.
- ## Usage: See examples below. You can run the small tests with: `python pure_function_decorator.py`.
- ## Python version: 3.6+ (you can adapt for previous versions by not using the f'' format strings and using python2 prints)
- ## Author: Michele Piccolini
- from functools import wraps
- def pure(func):
- @wraps(func) # maintain func's docstring
- def wrapped(*args, **kwargs):
- # run function on copy of arguments, so that they are not mutated even if func turns out to be impure
- import copy
- proxy_args = copy.deepcopy(args)
- proxy_kwargs = copy.deepcopy(kwargs)
- result = func(*proxy_args, **proxy_kwargs)
- # check purity
- for proxy_arg, arg, idx in zip(proxy_args, args, range(len(args))):
- if proxy_arg != arg:
- raise Exception(f"Function '{func.__name__}' is not pure, at parameter n. {idx}.")
- # if not all([proxy_arg == arg for proxy_arg, arg in zip(proxy_args, args)]):
- # raise Exception(f"Function {func.__name__} is not pure.")
- for arg_name in kwargs.keys():
- if proxy_kwargs[arg_name] != kwargs[arg_name]:
- raise Exception(f"Function '{func.__name__}' is not pure, at named parameter '{arg_name}'.")
- return result
- return wrapped
- # test
- if __name__ == '__main__':
- @pure
- def purefunc(a):
- """A pure function - does not modify its inputs"""
- print(a)
- print("\n>Testing a pure function...")
- mystr = 'hello'
- try: purefunc(mystr)
- except Exception as e: print(e)
- else: print(">OK")
- @pure
- def falseimpurefunc(b):
- """A function that could seem impure but it is not,
- since numbers are immutable in python (i.e. when you
- modify them you are always working on copies, as in C)"""
- b += 2
- print("\n>Testing a pure function (it would be impure if it weren't acting on immutable variables)...")
- mynum = 0
- try: falseimpurefunc(mynum)
- except Exception as e: print(e)
- else: print(">OK")
- @pure
- def impurefunc(L):
- """An impure function"""
- L.append(0)
- print("\n>Testing an impure function...")
- mylist = []
- try: impurefunc(mylist)
- except Exception as e:
- print(e)
- print(f"Argument before was: {[]}, now is: {mylist}.\n"
- f"The @pure decorator prevented it from being mutated!")
- else: print(">OK")
- @pure
- def impurefunc2(a, L=[]):
- print(a)
- L.append(0)
- print("\n>Testing an impure function (impure on named argument)...")
- try: impurefunc2(mystr, L=mylist)
- except Exception as e:
- print(e)
- print(f"Named argument before was: L={[]}, now is: {mylist}.\n"
- f"The @pure decorator prevented it from being mutated!")
- else: print("OK")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement