发布于2024-11-26 22:17 阅读(970) 评论(0) 点赞(5) 收藏(5)
对于 python 类,可以定义__getattr__()
方法以便能够使用该类调用任何函数。
前任:
# testclass/__init__.py
def x(*args, **kwargs):
print("hello")
def __getattr__(name):
return x
可以像这样使用:
# main.py
import testclass
testclass.thismethodshouldntexist() # -> prints "hello"
是否可以修改 Python 脚本的全局范围,以便能够实现类似的结果,而无需通过自定义类?我尝试过修改sys.modules[__name__]
,但似乎无法让它做任何事情。我猜这sys.modules
只代表了表面之下实际存在的东西的反映,并且它不会返回 Python 解释器使用的实际对象。
要明确的是,我想要实现的是:
# main.py
def x(*args, **kwargs):
pass
# do some magic stuff
y() # runs x instead as y is not specifically defined
我知道这可能是一个非常糟糕的想法,不适合用于任何形式的生产代码,我只是想更多地了解 Python 的工作原理,而摆弄内部结构通常是一种很好的学习方法
使用 CPython,编译期间在任何静态作用域中都找不到的名称的值将与LOAD_NAME
字节码一起加载,字节码在运行时按顺序在本地、全局和内置的命名空间中查找名称。
因此,如果您想使用默认值自定义名称查找,您会希望在最后一次查找尝试失败后在内置命名空间中发生默认行为,如果可以使用通过返回默认值__getitem__
处理的方法定制内置字典,则可以KeyError
做到这一点,这意味着我们需要用具有自定义行为的字典替换当前框架的内置字典。
这里的问题是,我们通常对当前框架的内置字典的唯一访问权限是通过frame.f_builtins
只读属性,因此无法通过标准 Python API 替换它。
但是为了像您的问题所希望的那样“摆弄内部结构”,我们可以通过ctypes
模块直接内存访问来修改它,给出对内置字典的引用与框架对象的偏移量,存储为类型PyFrameObject
,定义为类型struct _frame
,其定义为:
struct _frame {
PyObject_HEAD
PyFrameObject *f_back; /* previous frame, or NULL */
struct _PyInterpreterFrame *f_frame; /* points to the frame data */
...
};
其中是定义为 的PyObject_HEAD
宏, 是定义为 的类型,剥离宏后定义为,如下所示:PyObject ob_base;
PyObject
struct _object
struct _object {
union {
Py_ssize_t ob_refcnt;
PY_UINT32_T ob_refcnt_split[2];
};
PyTypeObject *ob_type;
};
定义为:struct _PyInterpreterFrame
typedef struct _PyInterpreterFrame {
PyObject *f_executable; /* Strong reference (code object or None) */
struct _PyInterpreterFrame *previous;
PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */
PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */
PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */
...
} _PyInterpreterFrame;
f_frame
因此,我们可以计算出64 位平台上框架对象的偏移量,为 8 个字节ob_refcnt
,为 8 个字节ob_type
,为 8 个字节,f_back
总共 24 个字节:
f_frame = ctypes.c_void_p.from_address(id(sys._getframe(0)) + 24)
f_builtins
并计算到的偏移量f_frame
,为 8 个字节f_executable
, 为 8 个字节previous
, 为 8 个字节, 为f_funcobj
8 个字节,f_globals
总共 32 个字节:
f_builtins = ctypes.py_object.from_address(f_frame.value + 32)
f_builtins
然后我们可以用默认为指定值的代理映射替换的值:
import sys
import ctypes
from collections import UserDict
def default_dict(value):
class _DefaultDict(UserDict):
def __getitem__(self, name):
try:
return super().__getitem__(name)
except KeyError:
return value
return _DefaultDict
def x(*args, **kwargs):
print("hello")
f_frame = ctypes.c_void_p.from_address(id(sys._getframe(0)) + 24)
f_builtins = ctypes.py_object.from_address(f_frame.value + 32)
f_builtins.value = default_dict(x)(f_builtins.value)
y()
输出:
hello
点击此处演示
作者:黑洞官方问答小能手
链接:https://www.pythonheidong.com/blog/article/2046117/0460e3fdd6dadfb31130/
来源:python黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 python黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-1
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!