【问题标题】:How to change a string in a function when calling a function?调用函数时如何更改函数中的字符串?
【发布时间】:2018-08-22 14:17:42
【问题描述】:

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

def string():
    print ("This cat was scared.")

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

main()

【问题讨论】:

  • 为什么不传递参数?
  • 这个问题的假设副本是在发布后一小时被问到的。人们至少可以在标记之前检查日期吗?
  • 正确,这个帖子是导致另一个帖子被发布的原因。
  • @MichaelSmith 它已关闭,因为这里投票最多的答案正是那里的答案。而且由于 OP 没有就他们真正想要的是什么一句话,我只能假设他们真的在问同样的事情。
  • 另外,如果问题相同并且新问题比旧问题“更具信息性”(主观),将旧问题作为新问题的副本关闭并没有错。

标签: python string printing


【解决方案1】:

根据大众的需求(好吧,一个人的好奇心……),以下是在调用函数之前实际更改函数中字符串的方法。

在实践中你不应该这样做。有一些使用代码对象的用例,但这确实不是其中之一。另外,如果你做的事情不那么琐碎,你应该使用像bytecodebyteplay 这样的库,而不是手动进行。此外,不言而喻,并非所有 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 时,但如果我们……

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

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()

【讨论】:

  • 这可能不属于这里,特别是因为现在有a separate question specifically about it,我发布了几乎相同的答案。我应该删除这个吗?
  • Tbh 在此处包含其信息后,我将删除另一个答案。然后,您可以编辑此答案以指向对更骇人听闻的方法感兴趣的任何人的单独问题。
  • 你能把它留在这里以备将来参考吗?我不会为此使用它,但我觉得它很有趣。
【解决方案2】:

猜测:

  • 您希望string()返回调用者可以使用的值,而不是仅仅在屏幕上打印一些内容。所以你需要一个return 语句而不是一个print 调用。
  • 您想要遍历返回的字符串中的所有单词,而不是所有字符,因此您需要在字符串上调用split()
  • 您想替换每个单词中的内容,而不是文字 "cat"。因此,您需要在word 上调用replace,而不是在str 类上。此外,replace 实际上并没有改变这个词,它返回一个 新的,你必须记住它。
  • 您想打印出每个单词。

如果是这样:

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。你正在换掉整个单词,所以你可以这样做:

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

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

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

【讨论】:

  • print() 在做什么?
  • @Vicrobot 它打印一个换行符。由于我们在循环中使用end=' ' 打印每个单词,它们都在一行上,而光标仍在同一行上,所以我们想移动到下一行。
  • 那为什么我们需要给end参数赋予其他值呢?
  • @Vicrobot 因为否则,每个单词都会打印在自己的单独行上,它们之间有换行符。这样,它们都打印在同一行,它们之间有空格(然后我们在末尾打印一个换行符)。
【解决方案3】:

我认为您可能真正想要的是使用默认参数调用您的函数的能力:

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

>>> string()
This cat was scared.

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

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

【讨论】:

  • 确定这就是他想要的,但这看起来是一个很好的猜测,如果是这样,这是一个很好的解释。
  • @COLDSPEED 他可能正在寻找这个,但如果我不能改变string()function 的定义,它就是这样。有没有办法替换我正在调用的函数的打印函数中传递的字符串?
  • @moghya 并非没有一些隐秘的诡计,这超出了我的专业知识,也超出了我会推荐某人做的事情......
  • @moghya 有几种方法可以做到这一点,但它们都非常丑陋,永远不应该这样做。最不难看的方法可能是将函数内的code 对象替换为具有不同co_consts 列表的对象。接下来可能是进入 C API 以访问 str 的内部缓冲区。如果您想要更多解释,请创建一个新问题 - 但同样,您永远不想真正这样做;如果你真的想深入了解 Python,这很有趣。
  • @abarnert 哇...如果你被要求编写代码,你能做到吗?!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
  • 2011-09-18
  • 2021-02-14
  • 1970-01-01
  • 2012-10-09
  • 2022-01-21
  • 1970-01-01
相关资源
最近更新 更多