27

我经常想检查一个对象是否有成员。一个例子是在函数中创建单例。为此,您可以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每次都一样。而且,通常,该方法会被多次调用。

4

5 回答 5

22

这是两种不同的方法:№1 是 LBYL(在你跳跃之前先看),№2 是 EAFP(请求宽恕比许可更容易)。

Pythonistas 通常建议 EAFP 更好,其参数风格为“如果一个进程在您测试它的时间和您尝试自己创建它的时间之间创建文件怎么办?”。这个论点在这里并不适用,但它是一般的想法。异常不应被视为异常。

在您的情况下性能方面 - 因为在 CPython 中设置异常管理器(try关键字)非常便宜,而创建异常(raise关键字和内部异常创建)相对昂贵 - 使用方法 №2 只会引发一次异常; 之后,您只需使用该属性。

于 2008-10-15T12:28:11.233 回答
10

我只是试图测量时间:

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 更快。它对我来说似乎也更具可读性,无论如何取决于具体情况,这个测试非常简单,也许你需要一个更复杂的测试。

于 2008-10-15T12:15:54.300 回答
5

这取决于哪种情况是“典型的”,因为异常应该模拟非典型情况。因此,如果典型情况是该instance属性应该存在,则使用第二种代码样式。如果没有instance像有一样典型instance,那么使用第一种样式。

在创建单例的特定情况下,我倾向于使用第一种样式,因为在初始时创建单例是一个典型的用例。:-)

于 2008-10-15T10:59:22.283 回答
1

使用它的方式有点离题。单例被高估了,“共享状态”方法在 python 中同样有效,而且大多数情况下非常干净,例如:

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state
    # and whatever else you want in your class -- that's all!

现在每次你这样做:

obj = Borg()

它将具有相同的信息,或者,在某种程度上是相同的实例。

于 2008-10-15T12:45:28.327 回答
0

我必须同意克里斯。请记住,在您真正需要之前不要进行优化。我真的怀疑检查存在是否会成为任何合理程序的瓶颈。

我确实也将http://code.activestate.com/recipes/52558/视为一种方法。该代码的未注释副本(“垃圾邮件”只是类接口具有的随机方法):

class Singleton:
    class __impl:
        def spam(self):
            return id(self)
    __instance = None
    def __init__(self):
        if Singleton.__instance is None:
            Singleton.__instance = Singleton.__impl()
        self.__dict__['_Singleton__instance'] = Singleton.__instance
    def __getattr__(self, attr):
        return getattr(self.__instance, attr)
    def __setattr__(self, attr, value):
        return setattr(self.__instance, attr, value)
于 2008-10-15T12:27:14.807 回答