如何在没有类的情况下在Python中维护状态?

Cer*_*era 13 python closures state

pythonic没有完全面向对象的方法来维护状态(例如,为了优化的目的)?

为了更好地说明我的问题,这里是我在JavaScript中经常使用的模式的示例:

var someFunc = (function () {
    var foo = some_expensive_initialization_operation();
    return someFunc (bar) {
        // do something with foo and bar
    }
}());
Run Code Online (Sandbox Code Playgroud)

在外部,这只是一个像其他任何函数一样的函数,不需要初始化对象或类似的东西,但闭包允许计算值一次,然后我基本上用作常量.

Python中的一个例子是优化正则表达式 - 使用re.compile和存储编译版本matchsearch操作很有用.

我知道在Python中执行此操作的唯一方法是在模块范围中设置变量:

compiled_regex = compile_my_regex()

def try_match(m): # In reality I wouldn't wrap it as pointlessly as this
    return compiled_regex.match(m)
Run Code Online (Sandbox Code Playgroud)

或者通过创建一个类:

class MatcherContainer(object):
    def __init__(self):
        self.compiled_regex = compile_my_regex()
    def try_match(self, m):
        self.compiled_regex.match(m)

my_matcher = MatcherContainer()
Run Code Online (Sandbox Code Playgroud)

前一种方法是临时的,并且不清楚上面声明的函数和变量是否相互关联.它也会污染模块的命名空间,我对此并不满意.

后一种方法似乎冗长而且在样板上有点沉重.

我能想到的另一种处理方法是将这样的任何函数分解为单独的文件(模块),然后导入函数,以便一切都干净.

来自更有经验的Pythoners的任何建议如何处理这个?或者你只是不担心它并继续解决问题?

Imr*_*ran 14

您可以像在JavaScript中定义闭包一样在Python中定义闭包.

def get_matcher():
    compiled_regex = compile_my_regex()

    def try_match(m)
        return compiled_regex.match(m)

    return try_match
Run Code Online (Sandbox Code Playgroud)

但是,在Python 2.x中,闭包是只读的(compiled_regex对于上面的示例,您无法重新分配到内部函数调用).如果封闭变量是一个可变的数据结构(例如list,dict,set),你可以修改它的函数调用里面虽然.

def get_matcher():
    compiled_regex = compile_my_regex()
    match_cache = {}

    def try_match(m):
        if m not in match_cache:
           match_cache[m] = compiled_regex.match(m)

        return match_cache[m]

    return try_match
Run Code Online (Sandbox Code Playgroud)

在Python 3.x中,您可以使用nonlocal关键字在函数调用中重新分配给闭包变量.(PEP-3104)

另请参阅以下有关Python封闭的问题:


Pau*_*McG 11

您也可以使用默认参数完成此操作:

def try_match(m, re_match=re.compile(r'sldkjlsdjf').match):
    return re_match(m)
Run Code Online (Sandbox Code Playgroud)

因为默认参数仅在模块导入时评估一次.

甚至更简单:

try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m)
Run Code Online (Sandbox Code Playgroud)

或者最简单的:

try_match = re.compile(r'sldkjlsdjf').match
Run Code Online (Sandbox Code Playgroud)

这不仅节省了重新编译时间(实际上在re模块内部实际缓存),而且还节省了'.match'方法的查找.在忙碌的功能或紧凑的循环中,那些'.' 分辨率可以加起来.


glg*_*lgl 6

关于什么

def create_matcher(re):
    compiled_regex = compile_my_regex()
    def try_match(m):
        return compiled_regex.match(m)
    return try_match

matcher = create_matcher(r'(.*)-(.*)')
print matcher("1-2")
Run Code Online (Sandbox Code Playgroud)

但在大多数情况下,课程更好,更清洁.


Mar*_*nen 5

您可以在任何函数中存储属性。由于函数名称是全局的,您可以在其他函数中检索它。例如:

def memorize(t):
    memorize.value = t

def get():
    return memorize.value

memorize(5)
print get()
Run Code Online (Sandbox Code Playgroud)

输出:

5
Run Code Online (Sandbox Code Playgroud)

您可以使用它在单个函数中存储状态:

def memory(t = None):
    if t:
        memory.value = t
    return memory.value

print memory(5)
print memory()
print memory()
print memory(7)
print memory()
print memory()
Run Code Online (Sandbox Code Playgroud)

输出:

5
5
5
7
7
7
Run Code Online (Sandbox Code Playgroud)

当然,它的用处是有限的。在这个问题中,我只在 SO 上使用过它。