在遗留代码评估的格式字符串中执行计算

Perform calculation in format string that is evaluated by legacy code

我正在使用一个函数 foo(来自我无法修改的外部包)接受、填补空白并输出一个字符串。我想通过对该字符串中允许的空格填充符进行简单的算术运算来操纵输出。

更明确地说:

我目前打电话给 foo('Finished {n} out of {N}'),我得到的是 Finished 3 out of 5 jobs

我想做 foo({N-n} left) 以获得 2 jobs left

目前这不起作用。我怀疑 foo 只是在传递给它的字符串上调用 format,显然 N-n 甚至不是有效的 Python 变量名。

问题:有没有办法欺骗 format 进行我想要的计算?欢迎丑陋的黑客。

编辑:这是给 Python 3 EDIT2:我无权访问变量 Nn。它们在 foo

内部

EDIT3:什么是可能的,什么不是的最小例子,假设 foo 实际上只是对传递给它的字符串执行 .format

import sys
def foo(s):#This function cannot be changed
 N=5
 n=3
 sys.stderr.write(s.format(N=N,n=n))
foo('Finished {n} out of {N}')
foo('{N-n} left') # Error

在 Python 3.6+ 中,您可以使用格式化字符串文字 (PEP 498),也称为 f 字符串。

n = 3
N = 5

x = f'Finished {n} out of {N}'
y = f'{N-n} jobs left'

结果:

print(x, y, sep='\n')

Finished 3 out of 5
2 jobs left

如果你愿意,你可以简单地将它包装在一个函数中。

尝试:

foo('{} left'.format(N-n))

这应该可以完成工作。这是 Python 字符串格式化功能的一个非常简单的示例。

根据您修改后的问题定义,这是一个建议的方法来做您想要的,但它绝对是 非平凡的。它基于为函数 foo.

修改 Python 抽象语法树

需要执行以下步骤:

  • 仅当您只有 foo 函数的字节码时才需要此步骤。

    • 查找将生成 Python 源代码的第三方反编译器
    • 生成 foo 函数的源代码。
  • ast.parse()解析源码。

  • 在函数中找到 Nn 可靠可用的位置。
  • 如果已经有一个您可以覆盖的打印语句,请将其替换为您的代码,否则将您的打印语句插入到树中。
  • 使用 ast.compile() 编译 AST。
  • 使用生成的函数对象代替原始函数。

您将需要一个非常强大的用例,因为这需要大量的学习和工作。没有实际的 foo 功能,我能提供的就很少了。

我相信这可以通过 Python 的属性查找进行调和,就像 lazify 包所做的那样,然后在堆栈中搜索来电者,但我放弃了这种方法,因为事情很快就会变得一团糟。

假设 foo 与我们目前认为的一样,为什么不子类化 Python 的 string 功能用于此特定案例?

class FooString(str):

    def format(self, **kwargs):
        if "n" not in kwargs or "N" not in kwargs:
            raise ValueError("Perhaps we're mistaken about foo?")

        return "Finished {n} out of {N}\n{left} left".format(
            left=kwargs["N"] - kwargs["n"], **kwargs)

然后,调用 foo(FooString()) 应该会产生所需的输出,

>>>print(FooString().format(n=1, N=10))

<<<Finished 1 out of 10
   9 left