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

我正在写一个人工智能状态空间搜索算法,我有一个通用类,可以用来快速实现一个搜索算法。子类定义必要的操作,算法完成其余的操作。

这里我陷入了困境: 我想避免一次又一次地重新生成父状态,所以我有以下函数,它返回可以合法应用于任何状态的操作:

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

函数默认情况下抛出。

有没有比捕获异常更快的检查函数是否定义的方法?

我在想要不要做点什么来检查一下目录里有没有礼物,但是看起来不太对劲。Hasattr 是通过调用 getattr 并检查它是否引发来实现的,这不是我想要的。

140575 次浏览

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

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

请注意,当属性不存在时,getattr()通常会引发异常。但是,如果指定了默认值(在本例中为 None) ,它将返回该值。

有没有比捕获异常更快的检查函数是否定义的方法?

你为什么反对这种做法? 在大多数 Python 案例中,请求原谅比请求许可更好。 ; -)

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

再问一次,为什么会这样呢? 下面这段话非常简短:

    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 中的任何东西一样,如果你足够努力,你可以抓住内核,做一些非常肮脏的事情。最糟糕的是:

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 解释器。上面是 Python,这里是纯 邪恶

我喜欢 Nathan Ostgard 的回答,我投了赞成票。但是另一种解决问题的方法是使用制表修饰符,它将缓存函数调用的结果。因此,你可以继续使用一个昂贵的函数来计算结果,但是当你一遍又一遍地调用它时,这个函数的运行速度非常快; 这个函数的制表版本会查找一个 dict 中的参数,从实际的函数计算结果的时候在 dict 中找到结果,然后立即返回结果。

下面是 Raymond Hettinger 的一个名为“ lru _ cache”的记忆装饰器的配方。现在 Python 3.2中的 function tools 模块中有一个标准版本。

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

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

它在 Python2和 Python3中都可以工作

hasattr(connection, 'invert_opt')

如果连接对象定义了函数 invert_opt,则 hasattr返回 True

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

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

因此,它归结为: 检查对象 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

虽然在 _ _ dict _ _ property 中检查属性非常快,但是不能将其用于方法,因为它们不会出现在 _ _ dict _ _ hash 中。然而,如果性能如此关键,你可以在课堂上使用黑客工作区:

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)

你也可以复习一下课程:

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