程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

如何在 Python 中实现真正的通配符函数

发布于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;PyObjectstruct _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_funcobj8 个字节,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黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

5 0
收藏该文
已收藏

评论内容:(最多支持255个字符)