【问题标题】:Python - Function attributes or mutable default valuesPython - 函数属性或可变默认值
【发布时间】:2011-08-24 23:03:34
【问题描述】:

假设您有一个函数需要维护某种状态并根据该状态表现出不同的行为。我知道实现这一点的两种方法,其中状态完全由函数存储:

  1. 使用函数属性
  2. 使用可变默认值

使用稍微修改过的Felix Klings answer to another question 版本,这里有一个示例函数,可以在re.sub() 中使用,这样只会替换正则表达式的第三个匹配项:

功能属性:

def replace(match):
    replace.c = getattr(replace, "c", 0) + 1
    return repl if replace.c == 3 else match.group(0)

可变默认值:

def replace(match, c=[0]):
    c[0] += 1
    return repl if c[0] == 3 else match.group(0)

对我来说,第一个看起来更干净,但我看到第二个更常见。哪个更可取,为什么?

【问题讨论】:

  • 人们只是不考虑函数属性,因为它们并没有被初学者广泛使用,但是 everyone 考虑使用可变默认值,因为 every i> 初学者至少被它们咬过一次。

标签: python


【解决方案1】:

我改用闭包,没有副作用。

这里是例子(我刚刚修改了Felix Klings answer的原始例子):

def replaceNthWith(n, replacement):
    c = [0]
    def replace(match):
        c[0] += 1
        return replacement if c[0] == n else match.group(0)
    return replace

及用法:

 # reset state (in our case count, c=0) for each string manipulation
 re.sub(pattern, replaceNthWith(n, replacement), str1)
 re.sub(pattern, replaceNthWith(n, replacement), str2)
 #or persist state between calls
 replace = replaceNthWith(n, replacement)
 re.sub(pattern, replace, str1)
 re.sub(pattern, replace, str2)

对于可变的,如果有人调用 replace(match, c=[]) 会发生什么?

对于你打破封装的属性(是的,我知道 python 没有在类中实现差异原因......)

【讨论】:

  • +1 同意 - 没有意识到 original 问题的上下文是一个嵌套函数。
  • @JasonR.Coombs 我添加了一个示例
【解决方案2】:

这两种方式我都觉得很奇怪。第一个虽然好多了。但是当你这样想时:“具有可以对该状态和附加输入进行操作的状态的东西”,它听起来真的像一个普通的对象。当某物听起来像一个物体时,它应该是一个物体...... 所以,我的解决方案是使用带有__call__ 方法的简单对象:

class StatefulReplace(object):
    def __init__(self, initial_c=0):
        self.c = initial_c
    def __call__(self, match):
        self.c += 1
        return repl if self.c == 3 else match.group(0)

然后你可以在全局空间或者你的模块初始化中写:

replace = StatefulReplace(0)

【讨论】:

    【解决方案3】:

    怎么样:

    1. 使用类
    2. 使用全局变量

    没错,这些并不完全存储在函数中。我可能会使用一个类:

    class Replacer(object):
        c = 0
        @staticmethod # if you like
        def replace(match):
            replace.c += 1
            ...
    

    要回答您的实际问题,请使用getattr。这是一种非常清晰易读的方式来存储数据以备后用。对于阅读它的人来说,您正在尝试做什么应该很明显。

    可变的默认参数版本是常见编程错误的一个示例(假设您每次都会得到一个新列表)。仅出于这个原因,我会避免它。稍后阅读它的人可能会在没有完全理解后果的情况下决定这是一个好主意。即使在这种情况下,您的函数似乎也只能工作一次(您的 c 值永远不会重置为零)。

    【讨论】:

      【解决方案4】:

      对我来说,这两种方法看起来都很狡猾。问题是需要一个类实例。我们通常不会将函数视为在调用之间维护状态。这就是类的作用。

      也就是说,我以前在这类事情上使用过函数属性。特别是如果它是在其他代码中定义的一次性函数(即不可能在其他任何地方使用它),只需为其添加属性比定义一个全新的类并创建它的实例更简洁。

      我绝不会为此滥用默认值。理解上有很大的障碍,因为默认值的自然目的是提供参数的默认值,而不是维护调用之间的状态。另外,默认参数会邀请您提供非默认值,如果您使用滥用默认值来维护状态的函数这样做,您通常会得到非常奇怪的行为。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-18
        • 2011-12-16
        • 1970-01-01
        相关资源
        最近更新 更多