I am just checking where is the limit of changing python using python (without modifying interpreter and/or C code).
I know that I can basically monkey patch every builtin function like this:
import builtins
int(1)
# 1
def new_int(number):
return number + 1
builtins.int = new_int
int(1)
# 2
I know I can turn python classes upside down using special methods like __new__, __get__ etc. and that I can overload any operator.
but is there any way to monkey patch parentheses? so that instead of creating list, python will do something else with elements between [...] like creating tuple.
# normal
old = [1,2,3,4]
print(old)
# [1, 2, 3, 4]
type(old)
# <class 'list'>
# some strange code that monkeypatches [] so that instead of list, it creates tuple
def monkey_patch_list(values):
return tuple(values)
[] = monkey_patch_list # of course it is wrong
# new
new = [1,2,3,4]
print(new)
# (1, 2, 3, 4)
type(old)
# <class 'tuple'>
Probably there is no way to do it just in python but maybe somewhere hidden in python code there is a definition of handling [] so that I can mess it up. If there is anyone crazy like me and knows how to do it I will appreciate help.
Disclaimer: Don't worry just for fun, thanks ;)
This can be done as a function decorator as long as the lists to be replaced as tuples are defined in a function.
To do that, use ast.NodeTransformer to replace any ast.List node with an equivalent ast.Tuple node in the function's AST:
import ast
import inspect
from textwrap import dedent
class ForceTuples(ast.NodeTransformer):
def visit_List(self, node):
return ast.Tuple(**vars(node))
# remove 'force_tuples' from the function's decorator list to avoid re-decorating during exec
def visit_FunctionDef(self, node):
node.decorator_list = [
decorator for decorator in node.decorator_list
if not isinstance(decorator, ast.Name) or decorator.id != 'force_tuples'
]
self.generic_visit(node)
return node
def force_tuples(func):
tree = ForceTuples().visit(ast.parse(dedent(inspect.getsource(func))))
ast.fix_missing_locations(tree)
scope = {}
exec(compile(tree, inspect.getfile(func), 'exec'), func.__globals__, scope)
return scope[func.__name__]
so that:
@force_tuples
def foo():
bar = [1, 2, 3, 4]
print(bar)
print(type(bar))
foo()
outputs:
(1, 2, 3, 4)
<class 'tuple'>
Demo: https://replit.com/@blhsing/MellowLinearSystemsanalysis
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With