Last active
October 9, 2018 06:39
-
-
Save KaiserKatze/cc9aee8c348b608bbac23ffd2a80221a to your computer and use it in GitHub Desktop.
Python decorator examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| import functools | |
| import inspect | |
| import os.path | |
| def checkcaller(caller: str = 'package'): | |
| # 限制函数作用域 | |
| def gcp_package(): | |
| currentFrame = inspect.currentframe() | |
| outerFrames = inspect.getouterframes(currentFrame) | |
| # `outerFrames[0]` 就是被调用函数,在被调用时,所在的帧 `currentFrame` | |
| callerFrame = outerFrames[2] | |
| file = callerFrame.filename | |
| path = os.path.abspath(file) | |
| return path | |
| get_current_path = { | |
| 'package': gcp_package, | |
| }.get(caller) | |
| if get_current_path is None: | |
| raise RuntimeError(f'Invalid argument: `(caller={caller})` is unsupported!') | |
| def decorator_checkcaller(func): | |
| # 获取“声明路径” | |
| declare_path = get_current_path() | |
| print('Declare Path:', declare_path) | |
| @functools.wraps(func) | |
| def wrapper_checkcaller(*args, **kwargs): | |
| # 获取“调用路径” | |
| invoker_path = get_current_path() | |
| print('Invoker Path:', invoker_path) | |
| if invoker_path == declare_path: | |
| result = func(*args, **kwargs) | |
| return result | |
| else: | |
| raise AssertionError(f'Private function {func.__name__!r} invoked outside its declaring context!') | |
| return wrapper_checkcaller | |
| return decorator_checkcaller | |
| package = checkcaller('package') | |
| checkcaller = checktype(caller = str)(checkcaller) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| import functools | |
| import inspect | |
| def checktype(**kwargs): | |
| rules = kwargs | |
| # Validate rule entries | |
| for paramType in rules.values(): | |
| if not isinstance(paramType, type): | |
| raise TypeError('Invalid decorator arguments! Subclass of `type` is needed.') | |
| def decorator_checktype(func): | |
| @functools.wraps(func) | |
| def wrapper_checktype(*args, **kwargs): | |
| # Get parameter name list | |
| paramNames = inspect.getargspec(func)[0] | |
| for paramName in rules: | |
| paramType = rules[paramName] | |
| paramValue = kwargs.get(paramName) | |
| if not paramValue and paramName in paramNames: | |
| paramIndex = paramNames.index(paramName) | |
| paramValue = args[paramIndex] | |
| if isinstance(paramValue, paramType): | |
| continue | |
| raise AssertionError(f'Type of argument `{paramName}` is `{paramValue}`, violating the expectation of its being instance of `{paramType}`!') | |
| result = func(*args, **kwargs) | |
| return result | |
| return wrapper_checktype | |
| return decorator_checktype | |
| if __name__ == '__main__': | |
| msg = 'Decorator `checktype` fails to fullfilled runtime type checking.' | |
| try: | |
| assert checktype(param = int), msg # 0 | |
| assert checktype(apple = str), msg # 1 | |
| @checktype(x = int, y = int) | |
| def add(x, y): | |
| return x + y | |
| assert add(1, 2) == 3, msg # 2 | |
| @checktype(x = int, y = int) | |
| def mul(x, y): | |
| return x * y | |
| assert add(5, 6) == 11, msg # 3 | |
| except: | |
| result = False | |
| else: | |
| result = True | |
| finally: | |
| assert result, msg # 4 | |
| try: | |
| add('anything', None) | |
| checktype(x = 'hello') | |
| except: | |
| result = True | |
| else: | |
| result = False | |
| finally: | |
| assert result, msg # 5 | |
| print('All tests passed.') |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| # -*- coding: utf-8 -*- | |
| ''' | |
| Examples taken from [Primer on Python Decorators – Real Python](https://realpython.com/primer-on-python-decorators/) | |
| ''' | |
| import functools | |
| import time | |
| def timer(func): | |
| """Print the runtime of the decorated function""" | |
| @functools.wraps(func) | |
| def wrapper_timer(*args, **kwargs): | |
| start_time = time.perf_counter() # 1 | |
| value = func(*args, **kwargs) | |
| end_time = time.perf_counter() # 2 | |
| run_time = end_time - start_time # 3 | |
| print(f"Finished {func.__name__!r} in {run_time:.4f} secs") | |
| return value | |
| return wrapper_timer | |
| @timer | |
| def waste_some_time(num_times): | |
| for _ in range(num_times): | |
| sum([i**2 for i in range(10000)]) | |
| def debug(func): | |
| """Print the function signature and return value""" | |
| @functools.wraps(func) | |
| def wrapper_debug(*args, **kwargs): | |
| args_repr = [repr(a) for a in args] # 1 | |
| kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] # 2 | |
| signature = ", ".join(args_repr + kwargs_repr) # 3 | |
| print(f"Calling {func.__name__}({signature})") | |
| value = func(*args, **kwargs) | |
| print(f"{func.__name__!r} returned {value!r}") # 4 | |
| return value | |
| return wrapper_debug |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment