2012-08-08 43 views
11

有没有pythonic维护状态的方法(例如为了优化目的),而没有完全面向对象?如何在没有类的情况下在Python中维护状态?

为了说明我的问题比较好,这是我在JavaScript中经常使用模式的一个例子:

var someFunc = (function() { 
    var foo = some_expensive_initialization_operation(); 
    return someFunc (bar) { 
     // do something with foo and bar 
    } 
}()); 

外部讲,这仅仅是一个函数像任何其他,而无需初始化对象之类的东西,但闭包允许计算单个时间的值,然后我将其作为常量使用。

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) 

,或者通过创建类:

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() 

前一种方式是广告-hoc,并且它不是很清楚该函数和它上面声明的变量是相互关联的。它也污染了模块的命名空间,我不太满意。

后一种方法在样板上看起来冗长而有点沉重。

我能想到的唯一方法就是将所有这些功能都分解成单独的文件(模块)并只导入函数,这样一切都很干净。

有关如何处理此问题的更多经验丰富的Pythoners的任何建议?或者你只是不担心它,继续解决问题?

+1

我个人认为你的班级创作没有问题。详细程度很好。 “'显式比隐式更好。” – tMC 2012-08-08 14:18:50

+0

一种至少使用该类的方法不会太冗长,那就是将'try_match'重命名为'__call__',它可以让你像构造函数一样使用它(构造之后)。但是,正如在@ glglgl的回答中,您的JavaScript代码实际上直接转换为Python。 – lvc 2012-08-08 14:27:03

+0

是否在第一个例子中调用someFunc?还是应该是函数定义? – soulcheck 2012-08-08 14:46:50

回答

10

您还可以使用默认参数实现这一点:

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'方法。在繁忙的功能或紧密的循环中,那些'。'决议可以加起来。

+0

感谢您的回答。我知道模块缓存,但即使在脚本中,我在某些紧密循环中使用单个正则表达式,但通过使用“re.compile”而不是依赖内置缓存,性能显着提高。 – Cera 2012-08-13 13:37:43

5

什么

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") 

但是在大多数情况下,类更好,更干净。

+1

+1”但在大多数情况下,班级更好,更干净。“ – tMC 2012-08-08 14:19:45

12

您可以使用在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

只需添加:Python 3引入了'nonlocal',它可以用来给封闭的变量提供显式的写入权限。 [(PEP 3104)](http://www.python.org/dev/peps/pep-3104/) – rwos 2012-08-08 14:30:43

+1

更新了答案 – Imran 2012-08-08 14:33:49

+0

我对这个有点困惑。为什么每次运行'get_matcher()'都不会被评估?或者是为了做一些像'try_match = get_matcher()'这样的目的? – Cera 2012-08-13 13:36:49

1

一个经常使用的惯例是先于私有模块级全局以下划线指示它们不是模块的输出API的一部分:

# mymodule.py 

_MATCHER = compile_my_regex() 

def try_match(m): 
    return _MATCHER.match(m) 

您不应该因此而气馁 - 在函数闭包中最好隐藏一个变量。

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上使用过它。

相关问题