En primer lugar, quiero mencionar que sé que esta es una idea horrible y que no debería hacerse. Mi intención es principalmente la curiosidad y aprender las entrañas de Python, y cómo 'hackearlas'.
Me preguntaba si es posible cambiar lo que sucede cuando, por ejemplo, usamos [] para crear una lista. ¿Hay alguna manera de modificar el comportamiento del analizador para, por ejemplo, hacer que ["hello world"] llame a print("hello world") en lugar de crear una lista con un elemento?
Intenté encontrar documentación o publicaciones sobre esto, pero no pude hacerlo.
A continuación se muestra un ejemplo de cómo reemplazar el dictado incorporado para usar una clase personalizada:
from __future__ import annotations from typing import List, Any import builtins class Dict(dict): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__dict__ = self def subset(self, keys: List[Any]) -> Dict: return Dict({key: self[key] for key in keys}) builtins.dict = Dict Cuando se importa este módulo, reemplaza el dict incorporado con la clase Dict . Sin embargo, esto solo funciona cuando llamamos directamente a dict() . Si intentamos usar {} , se recurrirá a la implementación integrada del dict base:
import new_dict a = dict({'a': 5, 'b': 8}) b = {'a': 5, 'b': 8} print(type(a)) print(type(b))Rendimientos:
<class 'py_extensions.new_dict.Dict'> <class 'dict'>[] y {} se compilan en códigos de operación específicos que devuelven específicamente una list o un dict , respectivamente. Por otro lado list() y dict() se compilan en bytecodes que buscan variables globales para list y dict y luego las llaman como funciones:
import dis dis.dis(lambda:[]) dis.dis(lambda:{}) dis.dis(lambda:list()) dis.dis(lambda:dict())devuelve (con algunas nuevas líneas adicionales para mayor claridad):
3 0 BUILD_LIST 0 2 RETURN_VALUE 5 0 BUILD_MAP 0 2 RETURN_VALUE 7 0 LOAD_GLOBAL 0 (list) 2 CALL_FUNCTION 0 4 RETURN_VALUE 9 0 LOAD_GLOBAL 0 (dict) 2 CALL_FUNCTION 0 4 RETURN_VALUE Por lo tanto, puede sobrescribir lo que devuelve dict() simplemente sobrescribiendo el dict global, pero no puede sobrescribir lo que devuelve {} .
Estos códigos de operación están documentados aquí . Si se ejecuta el código de operación dict , obtienes un dictado, no hay forma de evitarlo. Como ejemplo, aquí está la implementación de BUILD_MAP en CPython, que llama a la función _PyDict_FromItems . No analiza ningún tipo de clases definidas por el usuario, sino que crea específicamente una estructura C que representa un dict de Python.
Es posible en al menos algunos casos manipular el código de bytes de python en tiempo de ejecución. Si realmente desea hacer que {} devuelva una clase personalizada, supongo que podría escribir algún código para buscar el código de operación BUILD_MAP y reemplazarlo con los códigos de operación apropiados. Aunque esos códigos de operación no son del mismo tamaño, probablemente haya que hacer algunos cambios adicionales.
El módulo ast es una interfaz para el árbol de sintaxis abstracta de Python que se crea después de analizar el código de Python.
Es posible reemplazar el dict literal ( {} ) con una llamada de dict modificando el árbol de sintaxis abstracta del código de Python.
import ast import new_dict a = dict({"a": 5, "b": 8}) b = {"a": 5, "b": 8} print(type(a)) print(type(b)) print(type({"a": 5, "b": 8})) src = """ a = dict({"a": 5, "b": 8}) b = {"a": 5, "b": 8} print(type(a)) print(type(b)) print(type({"a": 5, "b": 8})) """ class RewriteDict(ast.NodeTransformer): def visit_Dict(self, node): # don't replace `dict({"a": 1})` if isinstance(node.parent, ast.Call) and node.parent.func.id == "dict": return node # replace `{"a": 1} with `dict({"a": 1}) new_node = ast.Call( func=ast.Name(id="dict", ctx=ast.Load()), args=[node], keywords=[], type_comment=None, ) return ast.fix_missing_locations(new_node) tree = ast.parse(src) # set parent to every node for node in ast.walk(tree): for child in ast.iter_child_nodes(node): child.parent = node RewriteDict().visit(tree) exec(compile(tree, "ast", "exec"))producción;
<class 'new_dict.Dict'> <class 'dict'> <class 'dict'> <class 'new_dict.Dict'> <class 'new_dict.Dict'> <class 'new_dict.Dict'>