【问题标题】:Change string value by function [duplicate]按函数更改字符串值[重复]
【发布时间】:2011-09-17 13:52:30
【问题描述】:

我注意到我的代码中有很多这样的语句:

var = "some_string"
var = some_func(var)
var = another_func(var)
print(var)  # outputs "modified_string"

这真的让我很烦,它看起来很糟糕(与整个 Python 相反)。 如何避免使用它并以这样的方式开始使用它:

var = "some_string"
modify(var, some_func)
modify(var, another_func)
print(var) # outputs "modified_string"

【问题讨论】:

  • 根据我的经验,最好避免这样的副作用。相反,返回值(如第一个示例中所示)。如果有多个值,它们可能会像元组一样被包装复合类型(然后由调用者分解)。或者,也许问题是“重复任务太多”?考虑:print another_func(some_func("some_string"))
  • x = func(x) 看起来比modify(x, func) 差多少?我 100% 清楚第一个示例应该做什么,而 0% 清楚第二个示例应该做什么。
  • @Chris Lutz,我不确定确定这是重复的。字面上的问题是“我如何通过引用传递一个变量”,但真正的问题是“我如何避免重复地为同一个变量名分配一个新值”。这实际上是一个有趣且有价值的问题。
  • @senderle - 我想像x = chain_funcs(func1, func2, ..., x) 这样的东西还可以。不过,这会使调用的顺序有点模棱两可。
  • 我完全同意 pst - 一般来说,最好坚持只做一件事的功能,并且把它们做好。如果您希望经常重复相同的函数序列并且不希望有重复的代码,请考虑将常见的函数序列包装在它们自己的高级函数中,其唯一目的是调用这些低级函数。从长远来看,这种做法会对你有所帮助,我保证。

标签: python string


【解决方案1】:

这可能不是最“pythonic”的事情,但你可以“包装”你的字符串在一个列表中,因为列表 em> 在 Python 中是可变的。 例如:

var = "string"
var_wrapper = [var]

现在您可以将该列表传递给函数并访问其唯一元素。更改后,它将在函数外部可见:

def change_str(lst):
    lst[0] = lst[0] + " changed!"

你会得到:

>>> change_str(var_wrapper)
>>> var_wrapper[0]
"string changed!"

为了使内容更具可读性,您可以更进一步,创建一个 "wrapper" 类:

class my_str:
    def __init__(self, my_string):
        self._str = my_string

    def change_str(self, new_str):
        self._str = new_str

    def __repr__(self):
        return self._str

现在让我们运行相同的示例:

>>> var = my_str("string")
>>> var
string
>>> var.change_str("new string!")
>>> var
new string!

* 感谢@Error-SyntacticalRemorse 提出上课的意见。

【讨论】:

  • 不错,尽管这也可能暗示您可能想要实现一个类。 (仅供未来阅读此问题和答案的人使用)。
  • @Error-SyntacticalRemorse 你完全正确!我想知道是否在编辑中添加它。现在我想我会...
  • 实际上并不需要完整的实现,但最后的小提示(或简短示例)可能会有用。
【解决方案2】:

事实上,已经有at least one attemptfunctools 添加了compose 函数。我想我明白他们为什么不这样做了……但是,这并不意味着我们不能自己制造:

def compose(f1, f2):
    def composition(*args, **kwargs):
        return f1(f2(*args, **kwargs))
    return composition

def compose_many(*funcs):
    if len(funcs) == 1:
        return funcs[0]
    if len(funcs) == 2:
        return compose(funcs[0], funcs[1])
    else:
        return compose(funcs[0], compose_many(*funcs[1:]))

测试:

>>> def append_foo(s):
...     return s + ' foo'
... 
>>> def append_bar(s):
...     return s + ' bar'
... 
>>> append_bar(append_foo('my'))
'my foo bar'
>>> compose(append_bar, append_foo)('my')
'my foo bar'
>>> def append_baz(s):
...     return s + ' baz'
... 
>>> compose_many(append_baz, append_bar, append_foo)('my')
'my foo bar baz'

想一想,这可能不是解决您问题的最佳方法。但是写起来很有趣。

【讨论】:

  • 哇!我真的过度设计了那个。除了有一个更简单的递归解决方案之外,甚至还有一个单线! lambda lst: reduce(lambda x, y: lambda *args, **kwargs: x(y(*args, **kwargs)), lst)。它不漂亮,但它有效;请参阅 here 以获取使用 functional 的无 lambda 版本。
【解决方案3】:

问题是strintfloatlong 也是,如果你在 Py 2.x 中(True 和 False 真的是 ints,所以它们也是))是你在 Python 中所说的“不可变类型”。这意味着您不能修改它们的内部状态:对str(或intfloat)的所有操作都将导致str“新” 实例(或其他),而旧值将保留在 Python 的缓存中,直到下一个垃圾回收周期。

基本上,您无能为力。对不起。

【讨论】:

    【解决方案4】:

    其他人已经解释了为什么这是不可能的,但你可以:

    for modify in some_func, other_func, yet_another_func:
     var = modify(var)
    

    或如 pst 所说:

    var = yet_another_func(other_func(some_func(var)))
    

    【讨论】:

      【解决方案5】:

      字符串在 python 中是不可变的,所以你的第二个例子不能工作。在第一个示例中,您将名称 var 绑定到每行上的一个全新对象。

      通常对一个名称进行多次赋值是一种代码异味。也许如果您在这里发布更大的代码示例,有人可以向您展示更好的方法?

      【讨论】:

        【解决方案6】:

        有一种方法可以修改不可变变量,通过在本地符号表中重写它,但是,我认为这不是很好,应该尽可能避免。

        def updatevar(obj, value, callingLocals=locals()):
            callingLocals[next(k for k, o in callingLocals.items() if o is obj)] = value
        

        另一种方式,甚至更少 Pythonic,是将 exec 与格式化指令一起使用。由于this solution,它以字符串形式获取变量名:

        def getname(obj, callingLocals=locals()):
            """
            a quick function to print the name of input and value.
            If not for the default-Valued callingLocals, the function would     always
            get the name as "obj", which is not what I want.
            """
            return next(k for k, v in callingLocals.items() if v is obj)
        
        def updatevar2(k, v, callingLocals=locals()):
            n = getname(k, callingLocals)
            exec('global {};{}={}'.format(n, n, repr(v)))
        

        结果如预期:

        var = "some_string"
        updatevar(var, "modified_string")
        print(var) # outputs "modified_string"
        updatevar2(var, var + '2')
        print(var) # outputs "modified_string2"
        

        【讨论】:

          【解决方案7】:

          我只是把它放在这里(因为似乎还没有答案解决它)

          如果您经常重复相同的函数序列,请考虑将它们包装在更高级别的函数中:

          def metafunc(var):
              var = somefunc(var)
              var = otherfunc(var)
              var = thirdfunc(var)
              return lastfunc(var)
          

          然后,当您调用函数metafunc 时,您确切地知道您的var 发生了什么:什么都没有。你从函数调用中得到的只是metafunc 返回的任何东西。 此外,您可以确定您忘记的程序部分没有发生任何事情。这一点非常重要,尤其是在脚本语言中,通常会有很多你知道/记得的幕后发生的事情。

          这有利有弊,理论讨论属于纯函数式编程范畴。一些现实世界的交互(例如 i/o 操作)需要非纯函数,因为它们需要超出代码执行范围的现实世界影响。 这里简要定义其背后的原理:

          http://en.wikipedia.org/wiki/Functional_programming#Pure_functions

          【讨论】:

            猜你喜欢
            • 2021-09-07
            • 2017-12-23
            • 1970-01-01
            • 2015-05-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-02-19
            • 1970-01-01
            相关资源
            最近更新 更多