繁体   English   中英

Python - 函数属性或可变的默认值

[英]Python - Function attributes or mutable default values

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

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

使用稍微修改过的Felix Klings版本回答另一个问题 ,这里是一个可以在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)

对我来说,第一个看起来更干净,但我更常见的是第二个。 哪个更好,为什么?

我用封闭代替,没有副作用。

这是一个例子(我刚刚修改了Felix Klings答案的原始例子):

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没有在类中实现差异原因......)

这两种方式对我来说都很奇怪。 第一个虽然好多了。 但是当你以这种方式思考它时:“具有可以对该状态进行操作的状态和额外输入的东西”,它听起来真的像普通对象。 当某些东西听起来像是一个对象时,它应该是一个对象...所以,我的解决方案是使用一个带有__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)

然后你可以在全局空间或模块init中编写:

replace = StatefulReplace(0)

怎么样:

  1. 使用课程
  2. 使用全局变量

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

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

要回答您的实际问题,请使用getattr 这是一种非常清晰易读的方式来存储数据以供日后使用。 对于那些阅读它的人来说,你应该做的事情应该是非常明显的。

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

对我来说,这两种方法看起来都很狡猾。 问题是为一个类实例而哭泣。 我们通常不认为函数是在调用之间保持状态; 这就是类

也就是说,之前我已经使用过函数属性来处理这类事情。 特别是如果它是在其他代码中定义的一次性函数(即不可能从其他任何地方使用它),那么仅仅定义属性就比定义一个全新的类并创建它的实例更简洁。

我永远不会滥用默认值。 理解存在很大障碍,因为默认值的自然目的是提供参数的默认值,而不是维持调用之间的状态。 另外,默认参数会邀请您提供非默认值,如果使用滥用默认值来维护状态的函数,则通常会出现非常奇怪的行为。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM