클래스에 함수가 정의되어 있는지 확인하는 가장 빠른 방법은 무엇입니까?


132

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 함수는 기본적으로 throw됩니다.

예외를 잡는 것보다 함수가 정의되어 있지 않은지 확인하는 더 빠른 방법이 있습니까?

나는 dir에 존재하는 것을 확인하는 줄에 무언가를 생각하고 있었지만 그것이 옳지 않은 것 같습니다. hasattr은 getattr을 호출하고 그것이 발생하는지 확인하여 구현됩니다.


8
"hasattr은 getattr을 호출하고 그것이 발생하는지 확인함으로써 구현됩니다. 이것이 내가 원하는 것이 아닙니다." 왜 안돼? 구현이 무엇을 신경 쓰는가?
detly

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

1
시도하십시오 : hasattr(connection, 'invert_opt').
kenorb

답변:


204

예, getattr()속성을 가져 와서 callable()메소드인지 확인하는 데 사용하십시오.

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

getattr()속성이 존재하지 않는 경우 일반적으로 예외가 발생합니다. 그러나 기본값 ( None이 경우) 을 지정하면 대신 해당 값을 반환합니다.


3
getattr이 경우 의 구현은 예외를 자동으로 포착 hasattr하고 OP 와 같은 이유로 OP 와 달리 기본값을 대신 반환합니다 .
Santa

3
함수가 해당 클래스에 있지 않고 부모 클래스에 있으면 어떻게됩니까? 이 경우에 나는 아이들이 그 기능을 구현하지 않더라도 (hasattr을 사용하여) True를 얻는다
darkgaze

46

파이썬 2와 파이썬 3 모두에서 작동합니다.

hasattr(connection, 'invert_opt')

hasattrTrue연결 객체에 함수가 invert_opt정의 되어 있으면를 반환 합니다 . 여기 당신이 방목하는 문서가 있습니다

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


5
코드는 이해되지만 항상 함께 설명해야합니다. 오래 걸리지는 않지만 예상됩니다.
peterh-Reinstate Monica

이 :) 해치지 않을 것이지만 좋은, 당신은 기사를 가리킬 수 있습니다
비탈리 테르 지에프을

5
연결에 속성이있는 경우에도 True를 반환합니다 connection.invert_opt = 'foo'.
Robert Hönig

20

예외를 잡는 것보다 함수가 정의되어 있지 않은지 확인하는 더 빠른 방법이 있습니까?

왜 그런 거에요? 대부분의 Pythonic 경우 허가보다 용서를 구하는 것이 좋습니다. ;-)

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)기본적으로 예외를 포착하여 구현됩니다. 파이썬 땅에는 아무런 문제가 없습니다!


4

여기의 응답은 문자열이 객체의 속성 이름인지 확인합니다. 속성이 메소드인지 확인하려면 추가 단계 (호출 가능 사용)가 필요합니다.

따라서 객체 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

이 클래스를 사용하는 클래스에서는 실패합니다 __slots__. __slots__속성 액세스 속도를 ~ 10 % 향상시킵니다. stackoverflow.com/a/14119024/1459669
noɥʇʎԀʎzɐɹƆ 2009 년

3

나는 Nathan Ostgard의 답변이 마음에 들었고 투표했습니다. 그러나 문제를 해결할 수있는 또 다른 방법은 함수 데코레이터를 사용하여 함수 호출 결과를 캐시하는 것입니다. 따라서 무언가를 알아낼 수있는 고가의 기능을 사용할 수 있지만, 계속해서 호출하면 후속 호출이 빠릅니다. 메모 된 버전의 함수는 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


2

파이썬의 어떤 것과 마찬가지로, 열심히 노력하면 용기를 얻고 정말 불쾌한 것을 할 수 있습니다. 이제 불쾌한 부분이 있습니다.

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 인터프리터를 해킹하지 않는 한 우리에게 호의를 베푸십시오. 귀하의 질문에 대해 계속하고 PyPy 팀에 속하지 않는 한 이것을 사용 하지 마십시오 . 당신이 가진 것은 Pythonic이고, 내가 가진 것은 순수한 EVIL 입니다.


메소드가 예외를 발생시키는 경우에 해당됩니다. 또한 co_names같은지 확인해야 합니다 ('NotImplementedError',). 그러나 이것이 다소 악한 지 확실하지 않습니다.
kindall

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

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)

1
__dict__재정의 될 수 있습니다. 신뢰할 수 없습니다.
Xiao
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.