Python 利用sys._getframe() 实现虚构代码运行,控制函数的执行时机

sys._getframe()是用来获取当前函数的调用句柄的方法,返回一个FrameType的对象

class FrameType:
    f_back: Optional[FrameType]
    f_builtins: Dict[str, Any]
    f_code: CodeType
    f_globals: Dict[str, Any]
    f_lasti: int
    f_lineno: int
    f_locals: Dict[str, Any]
    f_trace: Optional[Callable[[FrameType, str, Any], Any]]
    if sys.version_info >= (3, 7):
        f_trace_lines: bool
        f_trace_opcodes: bool
    def clear(self) -> None: ...

f_back:链路中的上一个函数对象

f_locals: 函数携带的请求参数

f_code: 当前code信息,包括函数名代码位置等信息

class CodeType:
    """Create a code object.  Not for the faint of heart."""

    co_argcount: int
    if sys.version_info >= (3, 8):
        co_posonlyargcount: int
    co_kwonlyargcount: int
    co_nlocals: int
    co_stacksize: int
    co_flags: int
    co_code: bytes
    co_consts: Tuple[Any, ...]
    co_names: Tuple[str, ...]
    co_varnames: Tuple[str, ...]
    co_filename: str
    co_name: str
    co_firstlineno: int
    co_lnotab: bytes
    co_freevars: Tuple[str, ...]
    co_cellvars: Tuple[str, ...]

利用以上方法可以获取到整个调用链路的函数,一个简单的例子:

def frame(a, b):
    return sys._getframe()


if __name__ == '__main__':
    f = frame(2,3)
    print(f.f_locals)
    print(f.f_code)
    print(f.f_code.co_name)
output:
{'a': 2, 'b': 3}
<code object frame at 0x7f9865e0cea0, file "hooks.py", line 25>
frame

可以查到frame函数的名字和请求参数,进而扩展到类中

class Hook:
    """
    hook 方法,实例化后,获取调用链路上一个函数名及参数信息,call后执行函数
    """

    def __init__(self, obj: object()):
        _ancestors = obj.__class__.mro()
        _f_back = sys._getframe().f_back

        self.cls_name = _f_back.f_code.co_name
        self.kwargs = _f_back.f_locals
        self.kwargs.pop('self')

        if len(_ancestors) > 2:  # 超过一层继承抛出异常
            raise MethodUnimplemented(f'method: {self.cls_name} unimplemented')

    def __call__(self, obj: object()):
        return getattr(obj, self.cls_name)(**self.kwargs)


class Base:
    def add(self, num):
        return Hook(self)


class A(Base):
    def __init__(self, result):
        self.result = result

    def add(self, num):
        return self.result + num


if __name__ == '__main__':
    b = Base()
    h = b.add(23)
    print(h.cls_name, h.kwargs)
    a = A(10)
    print(h(a))
output:
add {'num': 23}
33

Base类定义了一个add方法,在A类中对add方法进行了实现,正常情况下使用A类的add方法如a.add(23),add方法是立刻执行的,这里可以使用Base方法先虚构一个add事件,代码中return Hook(self)返回的Hook对象包含了方法名和参数信息,当在后面需要执行add事件的时候只需要h(a)使用Hook携带的call方法即可通过反射执行add方法。

通常的应用场景有:

1.不希望立即执行当前函数

2.批量操作

3.多个继承类对同一个方法参数进行调用invoke

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值