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

时间:2021-10-06 01:55:22

I'm writing an AI state space search algorithm, and I have a generic class which can be used to quickly implement a search algorithm. A subclass would define the necessary operations, and the algorithm does the rest.

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

Here is where I get stuck: I want to avoid regenerating the parent state over and over again, so I have the following function, which returns the operations that can be legally applied to any state:

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

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

And the invert_op function throws by default.

并且invert_op函数默认抛出。

Is there a faster way to check to see if the function is not defined than catching an exception?

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

I was thinking something on the lines of checking for present in dir, but that doesn't seem right. hasattr is implemented by calling getattr and checking if it raises, which is not what I want.

我正在考虑在dir中检查当前的内容,但这似乎不对。 hasattr是通过调用getattr并检查它是否会引发来实现的,这不是我想要的。

7 个解决方案

#1


119  

Yes, use getattr() to get the attribute, and callable() to verify it is a method:

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

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

Note that getattr() normally throws exception when the attribute doesn't exist. However, if you specify a default value (None, in this case), it will return that instead.

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

#2


18  

It works in both Python 2 and Python 3

它适用于Python 2和Python 3

hasattr(connection, 'invert_opt')

hasattr returns True if connection object has a function invert_opt defined. Here is the documentation for you to graze

如果连接对象定义了函数invert_opt,则hasattr返回True。这是您放牧的文档

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

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

#3


17  

Is there a faster way to check to see if the function is not defined than catching an exception?

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

Why are you against that? In most Pythonic cases, it's better to ask forgiveness than permission. ;-)

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

hasattr is implemented by calling getattr and checking if it raises, which is not what I want.

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

Again, why is that? The following is quite Pythonic:

再说一遍,为什么呢?以下是相当Pythonic:

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

Or,

要么,

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

Note, however, that getattr(obj, attr, default) is basically implemented by catching an exception, too. There is nothing wrong with that in Python land!

但是请注意,getattr(obj,attr,default)基本上也是通过捕获异常来实现的。在Python领域没有任何问题!

#4


3  

I like Nathan Ostgard's answer and I up-voted it. But another way you could solve your problem would be to use a memoizing decorator, which would cache the result of the function call. So you can go ahead and have an expensive function that figures something out, but then when you call it over and over the subsequent calls are fast; the memoized version of the function looks up the arguments in a dict, finds the result in the dict from when the actual function computed the result, and returns the result right away.

我喜欢Nathan Ostgard的答案,我对其进行了投票。但另一种解决问题的方法是使用memoizing装饰器,它会缓存函数调用的结果。所以你可以继续使用昂贵的功能来解决问题,但是当你一遍又一遍地调用它时,后续的通话很快;函数的memoized版本在dict中查找参数,在实际函数计算结果时从dict中查找结果,并立即返回结果。

Here is a recipe for a memoizing decorator called "lru_cache" by Raymond Hettinger. A version of this is now standard in the functools module in Python 3.2.

这是Raymond Hettinger的一个名为“lru_cache”的memoizing装饰器的配方。现在,它的一个版本在Python 3.2的functools模块中是标准的。

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

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

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

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

#5


3  

The responses herein check if a string is the name of an attribute of the object. An extra step (using callable) is needed to check if the attribute is a method.

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

So it boils down to: what is the fastest way to check if an object obj has an attribute attrib. The answer is

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

'attrib' in obj.__dict__

This is so because a dict hashes its keys so checking for the key's existence is fast.

这是因为一个字典散列其键,因此检查键的存在是快速的。

See timing comparisons below.

请参阅下面的时间比较。

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

#6


2  

Like anything in Python, if you try hard enough, you can get at the guts and do something really nasty. Now, here's the nasty part:

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

Please do us a favor, just keep doing what you have in your question and DON'T ever use this unless you are on the PyPy team hacking into the Python interpreter. What you have up there is Pythonic, what I have here is pure EVIL.

请帮我们一个忙,继续做你在问题中所做的事情,除非你是在PyPy团队攻击Python解释器,否则不要使用它。你有什么Pythonic,我在这里是纯粹的EVIL。

#7


0  

While checking for attributes in __dict__ property is really fast, you cannot use this for methods, since they do not appear in __dict__ hash. You could however resort to hackish workaround in your class, if performance is that critical:

虽然检查__dict__属性中的属性非常快,但您不能将其用于方法,因为它们不会出现在__dict__哈希中。但是,如果性能至关重要,您可以在课堂上使用hackish workaround:

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

    def custom_method(self):
        pass

Then check for method as:

然后检查方法为:

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

Time comparision with getattr:

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

Not that I'm encouraging this approach, but it seems to work.

并不是说我鼓励这种方法,但似乎有效。

[EDIT] Performance boost is even higher when method name is not in given class:

[编辑]当方法名称不在给定类中时,性能提升甚至更高:

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

#1


119  

Yes, use getattr() to get the attribute, and callable() to verify it is a method:

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

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

Note that getattr() normally throws exception when the attribute doesn't exist. However, if you specify a default value (None, in this case), it will return that instead.

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

#2


18  

It works in both Python 2 and Python 3

它适用于Python 2和Python 3

hasattr(connection, 'invert_opt')

hasattr returns True if connection object has a function invert_opt defined. Here is the documentation for you to graze

如果连接对象定义了函数invert_opt,则hasattr返回True。这是您放牧的文档

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

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

#3


17  

Is there a faster way to check to see if the function is not defined than catching an exception?

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

Why are you against that? In most Pythonic cases, it's better to ask forgiveness than permission. ;-)

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

hasattr is implemented by calling getattr and checking if it raises, which is not what I want.

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

Again, why is that? The following is quite Pythonic:

再说一遍,为什么呢?以下是相当Pythonic:

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

Or,

要么,

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

Note, however, that getattr(obj, attr, default) is basically implemented by catching an exception, too. There is nothing wrong with that in Python land!

但是请注意,getattr(obj,attr,default)基本上也是通过捕获异常来实现的。在Python领域没有任何问题!

#4


3  

I like Nathan Ostgard's answer and I up-voted it. But another way you could solve your problem would be to use a memoizing decorator, which would cache the result of the function call. So you can go ahead and have an expensive function that figures something out, but then when you call it over and over the subsequent calls are fast; the memoized version of the function looks up the arguments in a dict, finds the result in the dict from when the actual function computed the result, and returns the result right away.

我喜欢Nathan Ostgard的答案,我对其进行了投票。但另一种解决问题的方法是使用memoizing装饰器,它会缓存函数调用的结果。所以你可以继续使用昂贵的功能来解决问题,但是当你一遍又一遍地调用它时,后续的通话很快;函数的memoized版本在dict中查找参数,在实际函数计算结果时从dict中查找结果,并立即返回结果。

Here is a recipe for a memoizing decorator called "lru_cache" by Raymond Hettinger. A version of this is now standard in the functools module in Python 3.2.

这是Raymond Hettinger的一个名为“lru_cache”的memoizing装饰器的配方。现在,它的一个版本在Python 3.2的functools模块中是标准的。

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

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

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

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

#5


3  

The responses herein check if a string is the name of an attribute of the object. An extra step (using callable) is needed to check if the attribute is a method.

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

So it boils down to: what is the fastest way to check if an object obj has an attribute attrib. The answer is

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

'attrib' in obj.__dict__

This is so because a dict hashes its keys so checking for the key's existence is fast.

这是因为一个字典散列其键,因此检查键的存在是快速的。

See timing comparisons below.

请参阅下面的时间比较。

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

#6


2  

Like anything in Python, if you try hard enough, you can get at the guts and do something really nasty. Now, here's the nasty part:

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

Please do us a favor, just keep doing what you have in your question and DON'T ever use this unless you are on the PyPy team hacking into the Python interpreter. What you have up there is Pythonic, what I have here is pure EVIL.

请帮我们一个忙,继续做你在问题中所做的事情,除非你是在PyPy团队攻击Python解释器,否则不要使用它。你有什么Pythonic,我在这里是纯粹的EVIL。

#7


0  

While checking for attributes in __dict__ property is really fast, you cannot use this for methods, since they do not appear in __dict__ hash. You could however resort to hackish workaround in your class, if performance is that critical:

虽然检查__dict__属性中的属性非常快,但您不能将其用于方法,因为它们不会出现在__dict__哈希中。但是,如果性能至关重要,您可以在课堂上使用hackish workaround:

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

    def custom_method(self):
        pass

Then check for method as:

然后检查方法为:

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

Time comparision with getattr:

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

Not that I'm encouraging this approach, but it seems to work.

并不是说我鼓励这种方法,但似乎有效。

[EDIT] Performance boost is even higher when method name is not in given class:

[编辑]当方法名称不在给定类中时,性能提升甚至更高:

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