是否有pythonic
方法来维护状态(例如,为了优化目的)而不是完全面向对象?
为了更好地说明我的问题,这是我在JavaScript中经常使用的模式示例:
var someFunc = (function () {
var foo = some_expensive_initialization_operation();
return someFunc (bar) {
// do something with foo and bar
}
}());
在外部,这只是一个像其他任何函数一样的函数,不需要初始化对象或类似的东西,但闭包允许计算值一次,然后我基本上用作常量。
Python中的一个示例是优化正则表达式时 - 使用re.compile
并将编译版本存储为match
和search
操作非常有用。
我知道在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)
或者通过创建一个类:
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()
前一种方法是临时的,并且不清楚上面声明的函数和变量是否相互关联。它也会污染模块的命名空间,我对此并不满意。
后一种方法在样板上看起来很冗长,而且有点沉重。
我能想到的另一种处理方法是将这样的任何函数分解为单独的文件(模块),然后导入函数,以便一切都干净。
来自更有经验的Pythoners的任何建议如何处理这个问题?或者你只是不担心它并继续解决问题?
答案 0 :(得分:14)
您可以像在JavaScript中定义闭包一样在Python中定义闭包。
def get_matcher():
compiled_regex = compile_my_regex()
def try_match(m)
return compiled_regex.match(m)
return try_match
但是,在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
在Python 3.x中,您可以使用nonlocal
关键字在函数调用中重新分配给闭包变量。 (PEP-3104)
另请参阅以下有关Python封闭的问题:
答案 1 :(得分:11)
您也可以使用默认参数完成此操作:
def try_match(m, re_match=re.compile(r'sldkjlsdjf').match):
return re_match(m)
因为默认参数仅在模块导入时计算一次。
甚至更简单:
try_match = lambda m, re_match=re.compile(r'sldkjlsdjf').match: re_match(m)
或最简单:
try_match = re.compile(r'sldkjlsdjf').match
这不仅节省了重新编译时间(无论如何都在re模块内部实际缓存),而且还查找了' .match'方法。在繁忙的功能或紧凑的循环中,那些'。'分辨率可以加起来。
答案 2 :(得分: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")
但在大多数情况下,课程更好,更清洁。
答案 3 :(得分:2)
您可以在任何函数中存储属性。由于函数名称是全局的,因此您可以在其他函数中检索它。例如:
def memorize(t):
memorize.value = t
def get():
return memorize.value
memorize(5)
print get()
输出:
5
您可以使用它在单个函数中存储状态:
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()
输出:
5
5
5
7
7
7
当然,它的用处是有限的。我只在this question中使用它。
答案 4 :(得分:1)
一个经常使用的约定是在私有模块级全局变量之前加上下划线,表示它们不是模块导出API的一部分:
# mymodule.py
_MATCHER = compile_my_regex()
def try_match(m):
return _MATCHER.match(m)
不应该劝阻你不要这样做 - 它比函数闭包中的隐藏变量更好。
答案 5 :(得分:0)
您可以使用generator.send();它可能不适用于此特定情况,但对于维护没有类的状态很有用。调用'.send(x)'可以设置yield调用后的值。如果调用“ next”,则possible_vals为none。 Related Send Question。
def try_match(regex = '', target = ''):
cache = {}
while True:
if regex not in cache:
cache[regex] = re.compile(regex).match
possible_vals = (yield cache[regex](target))
if possible_vals is not None:
(regex, target) = possible_vals
m = try_match(r'(.*)-(.*)', '1-2')
print(next(m))
m.send((r'(.*)-(.*)', '3-4'))
print(next(m))
#Note that you have to call yield before you can send it more values
n = try_match()
n.send((r'(.*)-(.*)', '5-6'))
print(next(n))