我不确定这是否可行,但是有没有办法在从另一个函数调用函数时更改函数打印的字符串?我想做这样的事情:
def string():
print ("This cat was scared.")
def main():
for words in string():
str.replace("cat", "dog")
# Print "The do was scared."
main()
请您参考如下方法:
根据大众需求(好吧,一个人的好奇心……),下面是您实际上可以在调用该函数之前更改该函数中的字符串的方法。
你永远不应该在实践中这样做。有一些使用代码对象的用例,但这真的不是其中之一。另外,如果你做任何不那么琐碎的事情,你应该使用像 bytecode 这样的库或 byteplay而不是手动进行。此外,不用说,并非所有 Python 实现都使用 CPython 样式的代码对象。但无论如何,这里是:
import types
def string():
print ("This cat was scared.")
def main():
# A function object is a wrapper around a code object, with
# a bit of extra stuff like default values and closure cells.
# See inspect module docs for more details.
co = string.__code__
# A code object is a wrapper around a string of bytecode, with a
# whole bunch of extra stuff, including a list of constants used
# by that bytecode. Again see inspect module docs. Anyway, inside
# the bytecode for string (which you can read by typing
# dis.dis(string) in your REPL), there's going to be an
# instruction like LOAD_CONST 1 to load the string literal onto
# the stack to pass to the print function, and that works by just
# reading co.co_consts[1]. So, that's what we want to change.
consts = tuple(c.replace("cat", "dog") if isinstance(c, str) else c
for c in co.co_consts)
# Unfortunately, code objects are immutable, so we have to create
# a new one, copying over everything except for co_consts, which
# we'll replace. And the initializer has a zillion parameters.
# Try help(types.CodeType) at the REPL to see the whole list.
co = types.CodeType(
co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
co.co_stacksize, co.co_flags, co.co_code,
consts, co.co_names, co.co_varnames, co.co_filename,
co.co_name, co.co_firstlineno, co.co_lnotab,
co.co_freevars, co.co_cellvars)
string.__code__ = co
string()
main()
如果这对您来说还不够hacky:我提到过代码对象是不可变的。当然,字符串也是如此。但在幕后足够深,它们只是指向一些 C 数据的指针,对吧?同样,只有当我们使用 CPython 时,但如果我们……
首先,捕获我的 superhackyinternals GitHub 上的项目。 (它有意不能通过 pip 安装,因为你真的不应该使用它,除非你要试验你本地构建的解释器等等。)然后:
import ctypes
import internals
def string():
print ("This cat was scared.")
def main():
for c in string.__code__.co_consts:
if isinstance(c, str):
idx = c.find('cat')
if idx != -1:
# Too much to explain here; see superhackyinternals
# and of course the C API docs and C source.
p = internals.PyUnicodeObject.from_address(id(c))
assert p.compact and p.ascii
length = p.length
addr = id(c) + internals.PyUnicodeObject.utf8_length.offset
buf = (ctypes.c_int8 * 3).from_address(addr + idx)
buf[:3] = b'dog'
string()
main()
