在Python中检查成员存在性
我经常想检查一个对象里是否有某个成员。比如说,在一个函数里创建一个单例(也就是只创建一个实例)。为此,你可以用 hasattr
这样做:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
不过你也可以这样做:
class Foo(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Foo()
return self.instance
这两种方法哪个更好呢?
补充:我加了 @classmethod
... 但请注意,这个问题 不是 关于如何创建单例,而是如何检查一个对象里是否有某个成员。
补充:在这个例子中,典型的用法是:
s = Foo.singleton()
这样 s
就是一个类型为 Foo
的对象,每次都是同一个。而且,通常这个方法会被调用很多次。
5 个回答
5
这要看什么情况是“典型”的,因为异常情况应该用来处理不寻常的情况。所以,如果通常情况下instance
属性应该存在,那就用第二种代码风格。如果没有instance
和有instance
一样常见,那就用第一种风格。
在创建单例的具体情况下,我倾向于使用第一种风格,因为第一次创建单例是一个典型的用例。:-)
11
我刚刚试着测量了一下时间:
class Foo(object):
@classmethod
def singleton(self):
if not hasattr(self, 'instance'):
self.instance = Foo()
return self.instance
class Bar(object):
@classmethod
def singleton(self):
try:
return self.instance
except AttributeError:
self.instance = Bar()
return self.instance
from time import time
n = 1000000
foo = [Foo() for i in xrange(0,n)]
bar = [Bar() for i in xrange(0,n)]
print "Objs created."
print
for times in xrange(1,4):
t = time()
for d in foo: d.singleton()
print "#%d Foo pass in %f" % (times, time()-t)
t = time()
for d in bar: d.singleton()
print "#%d Bar pass in %f" % (times, time()-t)
print
在我的电脑上:
Objs created.
#1 Foo pass in 1.719000
#1 Bar pass in 1.140000
#2 Foo pass in 1.750000
#2 Bar pass in 1.187000
#3 Foo pass in 1.797000
#3 Bar pass in 1.203000
看起来使用try/except会更快。对我来说,这种写法也更容易理解。不过这要看具体情况,这个测试很简单,可能你需要一个更复杂的测试。
22
这两种方法是不同的:第一种是 LBYL(先看再跳),第二种是 EAFP(问原谅比问许可更简单)。
在 Python 的圈子里,大家通常认为 EAFP 更好,理由是“如果在你检查文件是否存在和你自己尝试创建文件之间,有其他程序创建了这个文件怎么办?”虽然这个理由在这里不太适用,但大致意思就是:异常情况不应该被看得太过特别。
从性能的角度来看——因为在 CPython 中设置异常管理器(使用 try
关键字)是非常便宜的,而创建异常(使用 raise
关键字和内部异常创建)相对来说是比较昂贵的——使用第二种方法时,异常只会被触发一次;之后你就可以直接使用这个属性了。