关于python:如何在调用函数时更改函数中的字符串?

How to change a string in a function when calling a function?

我不确定这是否可行,但是否有方法在从另一个函数调用函数时更改函数打印的字符串?我想这样做:

1
2
3
4
5
6
7
8
9
def string():
    print ("This cat was scared.")

def main():
    for words in string():
        str.replace("cat","dog")
        # Print"The do was scared."

main()


根据流行的需求(好吧,一个人的好奇心…),下面是如何在调用函数之前更改函数中的字符串。

你不应该在实践中这样做。有一些使用代码对象的用例,但这并不是其中之一。另外,如果你做了一些不那么简单的事情,你应该使用像bytecodebyteplay这样的库,而不是手工操作。另外,不用说,并非所有的Python实现都使用cpython风格的代码对象。但不管怎样,这里是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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()

如果这对您来说还不够糟糕:我提到代码对象是不可变的。当然,弦也是。但是足够深的覆盖,它们只是指向一些C数据的指针,对吗?再说一次,只有我们在使用cpython,但是如果我们……

首先,从Github上获取我的superhackyinternals项目。(故意不安装pip,因为您不应该使用它,除非尝试使用您的本地解释器构建等。)然后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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()


作为猜测:

  • 您希望string()返回调用方可以使用的值,而不是将某些内容打印到屏幕上。所以你需要一个return语句而不是print调用。
  • 您希望循环返回字符串中的所有单词,而不是所有字符,因此需要在字符串上调用split()
  • 你要替换每个单词中的内容,而不是文字"cat"中的内容。所以,你需要在word上给replace打电话,而不是在str上。另外,replace实际上并没有改变单词,它返回一个新的单词,你必须记住。
  • 你想把这些字都打印出来。
  • 小精灵

    如果是这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def string():
        return"This cat was scared."

    def main():
        for word in string().split():
            word = word.replace("cat","dog")
            print(word, end=' ')
        print()

    main()

    这解决了你所有的问题。但是,它可以简化,因为您在这里并不真正需要word.replace。你要换掉整个单词,所以你可以这样做:

    1
    2
    3
    4
    5
    def main():
        for word in string().split():
            if word =="cat": word ="dog"
            print(word, end=' ')
        print()

    但是,更简单地说,您可以在整个字符串上调用replace,根本不需要循环:

    1
    2
    def main():
        print(string().replace("cat","dog"))


    我认为您可能正在寻找的是使用默认参数调用函数的能力:

    1
    2
    def string(animal='cat'):
        print("This {} was scared.".format(animal))
    1
    2
    >>> string()
    This cat was scared.

    1
    2
    >>> string('dog')
    This dog was scared.

    如果不向string传递任何内容,则假定为默认值。否则,将使用显式传递的子字符串打印字符串。