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

时间:2011-03-11 02:41:58

标签: python

我正在写一个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并检查它是否会引发来实现的,这不是我想要的。

8 个答案:

答案 0 :(得分:162)

是的,使用getattr()获取属性,使用callable()验证它是一种方法:

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

请注意,getattr()通常在属性不存在时抛出异常。但是,如果您指定默认值(在这种情况下为None),则会返回该值。

答案 1 :(得分:34)

它适用于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

答案 2 :(得分:18)

  

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

你为什么反对?在大多数Pythonic案例中,最好是请求宽恕而不是许可。 ; - )

  

hasattr是通过调用getattr并检查它是否会引发来实现的,这不是我想要的。

再次,为什么?以下是Pythonic:

    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领域没有任何问题!

答案 3 :(得分: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

答案 4 :(得分:3)

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

所以归结为:检查对象obj是否具有属性属性的最快方法是什么。答案是

'attrib' in obj.__dict__

这是因为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

答案 5 :(得分: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

答案 6 :(得分:1)

您也可以遍历课程:

import inspect


def get_methods(cls_):
    methods = inspect.getmembers(cls_, inspect.isfunction)
    return dict(methods)

# Example
class A(object):
    pass

class B(object):
    def foo():
        print('B')


# If you only have an object, you can use `cls_ = obj.__class__`
if 'foo' in get_methods(A):
    print('A has foo')

if 'foo' in get_methods(B):
    print('B has foo')

答案 7 :(得分:0)

虽然检查__dict__属性中的属性非常快,但您不能将其用于方法,因为它们不会出现在__dict__哈希中。然而,如果表现非常重要,你可以在课堂上采用虚假的解决方法:

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)