2011-03-11 3 views
63

Я пишу алгоритм поиска пространства состояний 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 выбрасывается по умолчанию.

Есть ли более быстрый способ проверить, не определена ли функция, кроме обнаружения исключения?

Я что-то думал о проверке присутствия в каталоге, но это не кажется правильным. hasattr реализуется путем вызова getattr и проверки, если он поднимается, чего я не хочу.

+0

Что-то звучит сломана, но я не могу достаточно положить палец на его ... –

+6

* "hasattr является реализуется путем вызова getattr и проверки, если он поднимается, чего я не хочу ». * Почему бы и нет? Почему вас волнует, что делает реализация? – detly

+3

'has_op = lambda obj, op: callable (getattr (obj, op, None))' – samplebias

ответ

106

Да, используйте 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

+2

Что делать, если функция не в этом классе, а в родительском классе ?. В этом случае я получаю True, даже когда дети никогда не реализуют эту функцию (используя hasattr). – darkgaze

17

Есть ли более быстрый способ проверить, не определена ли функция, кроме обнаружения исключения?

Почему вы против этого? В большинстве случаев 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) в основном осуществляется перехват исключения, тоже. Нет ничего плохого в том, что на земле Питона!

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.

+0

Это будет true, если метод вызывает какое-либо исключение. Вы также должны проверить, соответствует ли 'co_names'' ('NotImplementedError',) '. Однако я не уверен, что это делает его более или менее злым. – kindall

3

Мне нравится ответ Натана Остгарда, и я проголосовал за него. Но другим способом, который вы могли бы решить свою проблему, было бы использование memoizing decorator, в котором был бы кеш результатом вызова функции.Таким образом, вы можете идти вперед и иметь дорогостоящую функцию, которая что-то отличает, но тогда, когда вы вызываете ее снова и снова, последующие вызовы бывают быстрыми; memoized версия функции ищет аргументы в dict, находит результат в dict, когда фактическая функция вычисляет результат и сразу возвращает результат.

Рецепт создания памятного декоратора под названием «lru_cache» Раймонда Хеттингера. Версия этого стандарта теперь стандартная в модуле functools в Python 3.2.

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

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

18

Он работает как в 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

+0

хороший, вы можете указать на статью, хотя это не помешает :) –

+0

Это также возвращает True, если соединение имеет атрибут 'connection.invert_opt = 'foo''. –

3

ответы здесь, проверить, является ли строка имя атрибута объекта. Дополнительный шаг (с использованием вызываемого) необходим, чтобы проверить, является ли атрибут методом.

Таким образом, это сводится к следующему: что является самым быстрым способом проверить, связан ли атрибут объекта obj с атрибутом. Ответ

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

Время Comparision с 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) 

не что я поощряю этот подход, но, похоже, он работает.

[EDIT] Ускорьтесь еще выше, если имя метода не в данном классе:

>>%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) 
Смежные вопросы