将代码存储为功能块,以便稍后在该范围内执行

尤金·布尔金(Eugene Bulkin)

这似乎是不可能的,但是我正在尝试实现以下内容:

a = 0
with before():
  a += 1

do_thing(a) # does thing with a, whose value is now 1
do_thing(a) # does thing with a, whose value is now 2

因此,我希望可以在with语句中使用该块的东西,将该块保存在某个位置,并do_thing在使用该范围的同时在每次函数调用之前调用它。

另一个选择是这样的:

@before
def callback():
  a += 1

而不是with语句。我认为,虽然with语句是首选,但我认为这两种方法都可以。

我想在这里应该做一些我想做的事情,但是当我实际尝试时会遇到错误。

阿巴内特

您可以创建一个将函数存储在列表中的装饰器,该装饰器将由另一个装饰器附加到另一个函数。您似乎认为这是问题的难点,但这很简单:

before_funcs = []
def before(func):
    before_funcs.append(func)
    return func

def attach_befores(func):
    @functools.wraps(func)
    def newfunc(*args, **kwargs):
        for before_func in before_funcs:
            before_func()
        return func(*args, **kwargs)
    return newfunc

因此,现在您可以执行以下操作:

a = 0

@before
def callback():
    global a
    a += 1

@before
def another():
    global a
    a *= 2

@attach_befores
def do_thing(i):
    print(i)

请注意,您需要global a那里,因为该功能否则无效。


现在,您可以调用它:

do_thing(a)
do_thing(a)
do_thing(a)
do_thing(a)

但是,它不会为您提供所需的结果-特别是,更改全局值a不会更改传递给实do_thing函数的参数为什么?因为函数参数是在调用函数之前求值的。因此,a在对参数进行求值后重新绑定对您没有好处。当然,它仍然会更改传递给下一个调用的参数因此,输出将是:

0
2
6
14

如果您只想修改传递给该函数的参数,则不需要对globals进行所有这些处理。只需让before函数修改参数,并让装饰器-应用程序before先将参数传递给每个函数,然后再将其传递给实函数。

或者,或者,如果您想修改函数使用的全局变量,请让函数实际使用这些全局变量而不是获取参数。

或者,或者,如果您想就地改变值,则使它变得可变,如列表,并使before函数使值发生改变,而不仅仅是将全局变量重新绑定为其他值。

但是,您需要的是一个可以到达调用框架的装饰器,弄清楚对哪些表达式进行了求值以获取参数,并强制对其进行重新求值。那真是愚蠢。


如果您真的很想这样做,那么唯一的方法就是捕获并解释中的字节码sys._getframe(1).f_code

至少在CPython 2.7中,您将获得一些代码序列,这些代码序列将装饰后的函数推入堆栈(一个简单的LOAD_NAMELOAD_NAME在典型情况下,但不一定),然后是一系列代码,用于计算表达式,然后是一个CALL_FUNCTION/ CALL_FUNCTION_VAR/ etc 。因此,您可以向后走,模拟操作,直到找到将函数推入堆栈的函数为止。(我不确定如何以一种万无一失的方式执行此操作,但它应该可行。然后,构建一个新code对象,该对象只将函数推入aLOAD_CONST并在其后重复所有操作(然后返回值)。然后将其包装codefunction与调用方完全相同的环境中,然后调用该新函数并返回其值,而不是直接调用包装的函数。

这是一个例子:

def call_do_thing(b):
    global a
    b += a
    return do_thing(a * b)

伪字节码为:

LOAD_FAST b
LOAD_GLOBAL a
INPLACE_ADD
STORE_FAST b
LOAD_GLOBAL do_thing
LOAD_GLOBAL a
LOAD_FAST b
BINARY_MULTIPLY
CALL_FUNCTION 1
RETURN_VALUE

在这种情况下,查找函数调用很容易,因为它使用了LOAD_GLOBAL因此,我们只需要从那里获取所有操作,RETURN_VALUE然后将它们包装在一个新函数中即可调用,而不是调用我们提供的函数,并且a将在新的全局变量中对其进行重新评估。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何执行功能块?

将整数分布在一个范围内,以便它们合计为

评估Kotlin中的代码块(将变量隐藏在范围内)

铸造功能块

在站点范围内将站点ID存储为常量的AngularJS和Google Analytics(分析)

按执行顺序反应异步等待功能块 useDispatch

我如何停止lua功能块/调用中执行

以编程方式打开Simulink MATLAB功能块的代码

如何仅在功能块代码(Atom)中搜索/替换?

在本地范围内执行代码时异常导致硒

MIT Scratch功能块

该Perl 6 CATCH块是否应该能够在词法范围内更改变量?

将函数存储在data()中并稍后执行

将内存中的一个数据块保存到字节数组中,以便稍后从字节数组中恢复该块

使用for循环将范围内的文本粘贴为OLEObject

将时间范围内的值设置为零

整个功能块是否真的存储在RETAIN存储器中?

获取“ with”块范围内的文件对象

块范围内的thread_local

将 Promise 存储在 Map 中以便稍后解决/拒绝

将图像存储在 Word 文档中以便稍后插入

将输出存储在内存中,以便稍后写入磁盘

如何监视小于或大于(值)的数字列表,并在该值在范围内时执行操作?

需要一个额外的循环,以便如果范围为0的单元格执行此操作,否则,如果范围内的单元格具有数据,则继续

递归功能块的最后一条语句执行了多少次?

从MATLAB命令窗口中通过Simulink MATLAB功能块代码搜索匹配的字符串

Fullcalendar 功能不在范围内

计算功能范围内的特殊字符

指向功能范围内的对象的指针