在Python模块中强制方法顺序
在处理一个需要按特定顺序调用方法的模块时,最“Pythonic”的方式是什么呢?
举个例子,我有一个XML配置文件,这个文件必须在其他操作之前读取,因为这个配置会影响程序的行为。
首先必须调用parse_config()
,并且要提供配置文件。其他辅助方法,比如query_data()
,在parse_config()
被调用之前是无法工作的。
我最开始是把这个实现成一个单例模式,这样可以确保在初始化时传入配置文件名,但我发现其实模块本身就是单例的。它不再是一个类,而只是一个普通的模块。
那么,怎样才能确保在模块中首先调用parse_config
呢?
值得注意的是,这个函数实际上是parse_config(configfile)
。
6 个回答
正如Cat Plus Plus所说,简单来说,就是把功能或行为放到一个类里,然后把所有需要的设置放在__init__
方法中。
你可能会觉得这些函数看起来并不太像是自然归在一个对象里,所以这可能不是一个好的面向对象设计。如果是这样的话,可以把你的类或对象看作是一种命名空间。这样比起强行规定函数调用顺序或者使用单例模式要干净和灵活得多。
我使用的模型是,后续的函数只能作为前一个函数返回值的方法来使用,像这样:
class Second(object):
def two(self):
print "two"
return Third()
class Third(object):
def three(self):
print "three"
def one():
print "one"
return Second()
one().two().three()
如果设计得当,这种风格(我承认这并不是特别符合Python的风格,但)可以让库的使用变得流畅,特别是在处理复杂的流程操作时,后面的步骤需要前面计算的结果和调用函数的新输入。
一个有趣的结果是错误处理。我发现,处理流程步骤中那些容易理解的错误的最好方法是有一个空的错误类,这个类理论上可以处理流程中的每个函数(除了第一个),但这些函数(除了可能是终点的函数)只返回self
:
class Error(object):
def two(self, *args):
print "two not done because of earlier errors"
return self
def three(self, *args):
print "three not done because of earlier errors"
class Second(object):
def two(self, arg):
if arg == 2:
print "two"
return Third()
else:
print "two cannot be done"
return Error()
class Third(object):
def three(self):
print "three"
def one(arg):
if arg == 1:
print "one"
return Second()
else:
print "one cannot be done"
return Error()
one(1).two(-1).three()
在你的例子中,你会有一个Parser类,这个类几乎只有一个configure
函数,它返回一个ConfiguredParser类的实例,这个实例可以做只有经过正确配置的解析器才能做的事情。这让你可以访问多个配置和处理配置失败的尝试。
如果这个对象在被调用之前不合法,那就应该在__init__
方法里调用那个方法(或者使用一个工厂函数)。你完全不需要那些复杂的单例模式,绝对没必要。