检查一个类是否定义了函数的最快方法是什么?

检查一个类是否定义了函数的最快方法是什么?

问题描述:

我正在写一个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

是,使用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

是否有更快的方法来检查函数是否未定义,而不是捕获异常?

你为什么反对?在大多数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的土地上没有任何问题!

就像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

我喜欢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

它工作在两个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

这里的响应检查一个字符串对象的属性的名称。需要额外的步骤(使用可调用的)来检查属性是否是方法。

所以归结为:检查对象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 

在检查中__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)