2011-03-11 51 views
63

我正在寫一個AI狀態空間搜索算法,並且我有一個可以用來快速實現搜索算法的泛型類。一個子類將定義必要的操作,算法完成剩下的操作。檢查一個類是否定義了函數的最快方法是什麼?

這裏是我卡住:我想避免一遍又一遍再生雙親狀態,所以我有下面的函數,它返回可依法適用於任何國家的行動:

def get_operations(self, include_parent=True): 
    ops = self._get_operations() 
    if not include_parent and self.path.parent_op: 
     try: 
      parent_inverse = self.invert_op(self.path.parent_op) 
      ops.remove(parent_inverse) 
     except NotImplementedError: 
      pass 
    return ops 

默認情況下會拋出invert_op函數。

是否有更快的方法來檢查函數是否未定義而不是捕獲異常?

我在考慮目前在dir中的檢查內容,但這似乎並不正確。 hasattr通過調用getattr並檢查它是否引發來實現,這不是我想要的。

+0

東西破碎的聲音,但我不能完全把我的手指上... – 2011-03-11 02:43:10

+6

*「hasattr是通過調用getattr並檢查是否引發,這不是我想要的。「*爲什麼不呢?你爲什麼關心實現的功能? – detly 2011-03-11 03:30:55

+3

'has_op = lambda obj,op:callable(getattr(obj,op,None))' – samplebias 2011-03-11 04:03:11

回答

106

是,使用getattr()獲取屬性,並callable()以驗證它是一種方法:

invert_op = getattr(self, "invert_op", None) 
if callable(invert_op): 
    invert_op(self.path.parent_op) 

注意getattr()通常拋出異常時,該屬性不存在。但是,如果您指定一個默認值(在本例中爲None),它將返回該值。

+2

還要注意,在這種情況下'getattr'的實現靜靜地捕獲一個異常並且返回默認值,就像'hasattr'一樣,這是OP出於某種原因反對的。 – Santa 2011-03-11 17:28:21

+2

如果該函數不在該類中,但是在父類中,該怎麼辦?在這種情況下,我會得到一個True,即使孩子從未實現該功能(使用hasattr) – darkgaze 2016-09-05 14:33:33

17

是否有更快的方法來檢查函數是否未定義,而不是捕獲異常?

你爲什麼反對?在大多數Pythonic案件中,最好請求寬恕而不是允許。 ;-)

hasattr通過調用getattr並檢查是否引發,這不是我想要的。

再次,爲什麼?以下是相當Python化:

try: 
     invert_op = self.invert_op 
    except AttributeError: 
     pass 
    else: 
     parent_inverse = invert_op(self.path.parent_op) 
     ops.remove(parent_inverse) 

或者,

# if you supply the optional `default` parameter, no exception is thrown 
    invert_op = getattr(self, 'invert_op', None) 
    if invert_op is not None: 
     parent_inverse = invert_op(self.path.parent_op) 
     ops.remove(parent_inverse) 

但是請注意,這getattr(obj, attr, default)基本上是通過捕獲異常,也實施。在Python的土地上沒有任何問題!

2

就像Python中的任何東西一樣,如果你足夠努力,你可以得到膽量並做一些真正令人討厭的事情。現在,這裏的討厭的部分:

def invert_op(self, op): 
    raise NotImplementedError 

def is_invert_op_implemented(self): 
    # Only works in CPython 2.x of course 
    return self.invert_op.__code__.co_code == 't\x00\x00\x82\x01\x00d\x00\x00S' 

請幫一下忙,只是不停地在做什麼,你有你的問題,不要曾經使用過這一點,除非你在PyPy隊攻入了Python解釋器。你在那裏有Pythonic,我在這裏是純粹的EVIL

+0

如果該方法引發任何異常,則這將成立。你還應該檢查'co_names'是否等於'('NotImplementedError',)'。不過,我不確定這是否會讓它變得更加邪惡。 – kindall 2011-03-11 18:25:21

3

我喜歡Nathan Ostgard的回答,我投了票。但另一種解決問題的方法是使用memoizing裝飾器,它可以緩存函數調用的結果。所以你可以繼續,並有一個昂貴的函數來計算出某些東西,但是當你通過後續調用快速調用它時會很快;該函數的memoized版本在dict中查找參數,從實際函數計算結果時的dict中查找結果,並立即返回結果。

這是Raymond Hettinger稱爲「lru_cache」的記憶裝飾者的食譜。這個版本現在是Python 3.2中functools模塊的標準版本。

http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/

http://docs.python.org/release/3.2/library/functools.html

18

它工作在兩個Python 2和Python 3的

hasattr(connection, 'invert_opt') 

hasattr返回True如果連接對象具有定義的函數invert_opt。這裏是文檔,你吃草

https://docs.python.org/2/library/functions.html#hasattr https://docs.python.org/3/library/functions.html#hasattr

+3

雖然代碼是讚賞,它應該總是有一個附帶的解釋。這不需要很長時間,但它是預期的。 – peterh 2015-04-28 09:02:44

+0

不錯的一個,你可以指向一篇文章,雖然它不會傷害:) – 2016-11-01 13:22:55

+0

如果連接有一個屬性'connection.invert_opt ='foo'',這也返回True。 – 2017-11-13 12:47:44

3

這裏的響應檢查一個字符串對象的屬性的名稱。需要額外的步驟(使用可調用的)來檢查屬性是否是方法。

所以歸結爲:檢查對象obj是否具有attrib屬性的最快方法是什麼?答案是

'attrib' in obj.__dict__ 

這是因爲字典散列其密鑰,所以檢查密鑰的存在是很快的。

查看下面的時間比較。

>>> class SomeClass(): 
...   pass 
... 
>>> obj = SomeClass() 
>>> 
>>> getattr(obj, "invert_op", None) 
>>> 
>>> %timeit getattr(obj, "invert_op", None) 
1000000 loops, best of 3: 723 ns per loop 
>>> %timeit hasattr(obj, "invert_op") 
The slowest run took 4.60 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 674 ns per loop 
>>> %timeit "invert_op" in obj.__dict__ 
The slowest run took 12.19 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000000 loops, best of 3: 176 ns per loop 
0

在檢查中__dict__財產的屬性實在是快,你不能用這個方法,因爲他們不__dict__哈希出現。但是,您可以訴諸hackish的解決辦法在你的類,如果性能是至關重要的:

class Test(): 
    def __init__(): 
     # redefine your method as attribute 
     self.custom_method = self.custom_method 

    def custom_method(self): 
     pass 

然後檢查方法:

t = Test() 
'custom_method' in t.__dict__ 

時間比較有getattr

>>%timeit 'custom_method' in t.__dict__ 
55.9 ns ± 0.626 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

>>%timeit getattr(t, 'custom_method', None) 
116 ns ± 0.765 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

不我鼓勵這種方法,但它似乎工作。

[編輯]性能提升甚至更高時,方法名是不是在給定的類:

>>%timeit 'rubbish' in t.__dict__ 
65.5 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

>>%timeit getattr(t, 'rubbish', None) 
385 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 
相關問題