2008-10-15 61 views
23

我經常想檢查一個對象是否有成員。一個例子是在函數中創建一個單例。爲了這個目的,你可以使用hasattr這樣的:在Python中檢查成員存在

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

然後sFoo類型的對象中,相同的各一次。而且,通常這種方法會被多次調用。

+0

您能否舉一個您正在嘗試撥打的電話的例子以及該電話的預期回報? – Baltimark 2008-10-15 11:45:11

回答

21

這是兩個不同的方法:№1是LBYL(三思而後行)和№2是EAFP(更容易請求寬恕而不是許可)。

Pythonistas通常認爲EAFP較好,在「假設一個進程創建您測試它的時候,你試着自己創建它的時間之間的文件嗎?」類型的參數。這種說法在這裏並不適用,但它的總體思路。例外不應被視爲例外。

性能明智的,你的情況 - 由於設置例外經理(該try關鍵字)是CPython的很便宜,同時創造爲異常(raise關鍵字和內部異常創建)是什麼,是相對expensive-使用方法№2的異常只會引發一次;之後,您只需使用該屬性。

5

這取決於哪種情況是「典型的」,因爲例外應該模擬非常非典型的情況。因此,如果典型情況是instance屬性應該存在,則使用第二種代碼樣式。如果沒有instanceinstance一樣典型,則使用第一種樣式。

在創建單例的具體情況中,我傾向於使用第一種樣式,因爲創建單例的初始時間是典型的用例。 :-)

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 

看來,嘗試/除了速度更快。這對我來說似乎也更易讀,無論如何取決於案例,這個測試非常簡單,也許你需要更復雜的測試。

+0

我不知道這些速度結果是否有些誤導(但仍然非常有趣!),因爲hasattr在後臺使用異常來確定attr是否存在 – seans 2013-02-14 17:06:24

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) 
+0

線自`.dict __ ['_ Singleton__instance'] = Singleton .__ instance`?這應該等同於self .__ instance = Singleton .__實例,不是嗎? – PierreBdR 2008-10-15 13:58:48

1

使用它的方式有點偏離主題。單身人士高估了,而「共享狀態」的方法是有效的,而且大多在蟒蛇很乾淨,例如:

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

將有相同的信息,或者是相同的實例。