2016-06-30 3 views
4

고장 나는이 (강하게 간체) 예와 유사한 래퍼 클래스를 (대신 정의의) 지정 이 함수를 호출하여 최적화하고 하나의 함수 호출을 제거 할 수 있다고 생각했습니다.__getitem__ 마법 방법은 색인

class wrap(object): 
    def __init__(self): 
     self._data = range(10) 
     self.__getitem__ = self._data.__getitem__ 

그러나 후자의 버전으로 print w[2] 라인

TypeError: 'wrap' object does not support indexing

.

방법에 대한 직접 호출, 즉, print w.__getitem__(2)가, 두 경우 모두에서 작동 ...

왜 할당 버전은 색인을 허용하지 않습니다

?

+0

프로 팁 : 왜 이런 문제가 발생했는지 모르는 경우 최적화를 시도해서는 안됩니다. 그것의 간접비는 아마 당신이 생각하는 것만 큼 중요하지 않습니다. – MisterMiyagi

+1

하지만 시도하지 않았다면 어떻게 작동하는지 배울 수 없었을 것입니다. OTOH, 내가 깨닫다 간 알면, 나는 그것을 시도하지 않았을 것입니다 ... 또한, 아마 당신은 내가 얼마나 중요하다고 생각했는지 모릅니다. – NichtJens

+0

모든 유형에 대해 새 클래스를 작성하면 실제로이 문제를 해결할 수 있습니다. 기본적으로 이것을 위해 팩토리 함수가 필요합니다. '__new__'을 통해 투명하게 구현할 수 있습니다. 그것은 약간의 흑 마법입니다 ... 물론 나는 당신이 그것을 얼마나 중요하다고 생각하는지 추측 할 수 있습니다. 참조 용으로 약 0.1 usec의 통화 당 안전 할 수 있습니다. – MisterMiyagi

답변

4

특수 메서드 (본질적으로 양쪽 끝에 밑줄이 두 개인 경우)를 클래스에 정의해야합니다. 특수 메서드에 대한 internal lookup procedure은 인스턴스 dict를 완전히 건너 뜁니다. 무엇보다도, 이것은 당신이

class Foo(object): 
    def __repr__(self): 
     return 'Foo()' 

사용자가 정의한 __repr__ 방법은 repr(Foo)에 대한 Foo의 경우에 사용되지 않고 할 경우에 그렇다.

+0

아! 내가 참조.이 문서에 대한 링크를 줄 수 있습니까? 그리고, 내 질문에 대한 답변을 귀하의 첫 번째 단락 사이의 연결을 실제로 볼 수 없습니다 ... – NichtJens

+0

@ NichtJens : 특별한 메서드 조회 인스턴스 dict 보았다면'repr (Foo)'가 나타납니다 정의한'Foo .__ repr__' 메소드를 사용하십시오. 그 방법은'Foo' 자체가 아니라'Foo' 인스턴스 만 처리하기 때문에 당신이 원하는 것은 아닙니다. – user2357112

+0

@NichtJens : 비슷한 예를 사용하는 설명서는 [여기] (https://docs.python.org/3/reference/datamodel.html#special-lookup) 문서에 나와 있습니다. – user2357112

1

모든 유형에 대해 새 클래스를 만들어 실제로이를 해결할 수 있습니다. 이 기능을 투명하게 사용하려면 __new__이 필요합니다.

import weakref 


class BigWrap(object): 
    def __new__(cls, wrapped): 
     wrapped_type = type(wrapped) 
     print('Wrapping %s (%s)' % (wrapped, wrapped_type)) 
     # creates a new class, aka a new type 
     wrapper_class = type( # new_class = type(class name, base classes, class dict) 
      '%s_%s_%d' % (cls.__name__, wrapped_type.__name__, id(wrapped)), # dynamic class name 
      (
       cls, # inherit from wrap to have all new methods 
       wrapped_type, # inherit from wrap_type to have all its old methods 
      ), 
      { 
       '__getitem__': wrapped.__getitem__, # overwrite __getitem__ based on wrapped *instance* 
       '__new__': wrapped_type.__new__, # need to use wrapped_type.__new__ as cls.__new__ is this function 
      }) 
     cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use 
     return cls._wrappers[wrapped_type](wrapped) 

    # self is already an instance of wrap_<type(wrapped)> 
    def __init__(self, wrapped): 
     self.__wrapped__ = wrapped 

초기 "솔루션"

import weakref 
class wrap(object): 
    _wrappers = weakref.WeakValueDictionary() # cache wrapper classes so we don't recreate them 

    def __new__(cls, wrapped): 
    wrapped_type = type(wrapped) 
    print('Wrapping %s (%s)' % (wrapped, wrapped_type)) 
    try: 
     return object.__new__(cls._wrappers[wrapped_type]) # need to use object.__new__ as cls.__new__ is this function 
    except KeyError: 
     print('Creating Wrapper %s (%s)' % (wrapped, wrapped_type)) 
    # creates a new class, aka a new type 
    wrapper_class = type( # class name, base classes, class dict 
     '%s_%s' % (cls.__name__, wrapped_type.__name__), # dynamic class name 
     (cls,), # inherit from wrap to have all its method 
     {'__getitem__': wrapped_type.__getitem__}) # overwrite __getitem__ based on wrapped class 
    cls._wrappers[wrapped_type] = wrapper_class # store wrapper for repeated use 
    return cls._wrappers[wrapped_type](wrapped) 

    # self is already an instance of wrap_<type(wrapped)> 
    def __init__(self, wrapped): 
    self._data = wrapped 

것은 그러나 조심! 이렇게하면 래핑 된 클래스 '__getitem__을 사용하여 원하는대로 할 수 있습니다. 그러나 이것이 항상 의미있는 것은 아닙니다! 예를 들어, list.__getitem__은 실제로 CPython의 CAPI에 내장되며 다른 유형에는 적용 할 수 없습니다.

foo = wrap([1,2,3]) 
print(type(foo)) # __main__.wrap_list 
foo[2] 
--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 
<ipython-input-31-82791be7104b> in <module>() 
----> 1 foo[2] 

TypeError: descriptor '__getitem__' for 'list' objects doesn't apply to 'wrap_list' object 
+1

나는 본다. 따라서 우리는 "특수 조회"절차를 감싸서 '__getitem__'을 실제로 고려해 볼 수는 있지만 일관되게 작동하지 않습니다. 그건 좀 어설픈 이야기입니다 ... 그 점을 제외하고는, 예를 감싸는 메소드를 인수로 취하여 데코레이터로 다시 작성 될 수 있다고 생각합니다. 그러나 위의 불일치 때문에 아마도 그럴 가치가 없습니다. – NichtJens

+0

@NichtJens 그래, 적어도 직접적으로 그 방법을 사용하는 것은 가치가없는 것 같다. 동일한 접근법을 사용하여 래퍼의 상속을 동적으로 변경할 수 있습니다. 즉 래핑 된 클래스의 실제 하위 클래스로 만들 수 있습니다. – MisterMiyagi

관련 문제