I'm writing an AI state space search algorithm, and I have a generic class which can be used to qui开发者_开发知识库ckly implement a search algorithm. A subclass would define the necessary operations, and the algorithm does the rest.
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.
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.
Yes, use getattr()
to get the attribute, and callable()
to verify it is a method:
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.
It works in both Python 2 and 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
https://docs.python.org/2/library/functions.html#hasattr https://docs.python.org/3/library/functions.html#hasattr
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. ;-)
hasattr is implemented by calling getattr and checking if it raises, which is not what I want.
Again, why is that? The following is quite 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!
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:
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.
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
'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
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.
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.
http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/
http://docs.python.org/release/3.2/library/functools.html
You can also go over the class:
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')
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:
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
:
>>%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)
精彩评论