, 가장 빠른 옵션이 자신을 monkeypatch하는 것입니다 초기화시 :
def __init__(self, wrappee):
for name, value in inspect.getmembers(wrappee, callable):
if not hasattr(self, name):
setattr(self, name, value)
이 정상적인 데이터 값을 가진 Wrappee
방법을 결합하는 특성을 당신의 Wrapper
인스턴스를 제공 할 것입니다. 그것은 엄청나게 빠르다. 그렇지?
class WrapperA(object):
def __init__(self, wrappee):
self.wrappee = wrappee
for name, value in inspect.getmembers(wrappee, callable):
if not hasattr(self, name):
setattr(self, name, value)
class WrapperB(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def __getattr__(self, name):
return getattr(self.wrappee, name)
In [1]: %run wrapper
In [2]: o2 = Wrappee()
In [3]: o1a = WrapperA(o2)
In [4]: o1b = WrapperB(o2)
In [5]: %timeit o2.bar()
10000000 loops, best of 3: 154 ns per loop
In [6]: %timeit o1a.bar()
10000000 loops, best of 3: 159 ns per loop
In [7]: %timeit o1b.bar()
1000000 loops, best of 3: 879 ns per loop
In [8]: %timeit o1b.wrapper.bar()
1000000 loops, best of 3: 220 ns per loop
따라서 바인딩 된 메서드를 복사하는 데 3 %의 비용이 듭니다. 이보다 더 동적 인 것이면 최소 66 %의 오버 헤드가있는 self.wrapper
에서 속성을 가져와야합니다. 평소 __getattr__
솔루션은 471 %의 오버 헤드를 가지고 있습니다 (그리고 불필요한 여분의 물건을 추가하면 느려질 수 있습니다).
그렇다면 바운드 방식의 해킹에 대한 개방적이고 단호한 승리라고 할 수 있습니다. 맞습니까?
반드시 그렇지는 않습니다. 471 %의 오버 헤드는 여전히 700 나노초입니다. 실제로 코드에서 차이를 만들 예정입니까? 아마도 딱딱한 루프 안에서 사용되지 않는 한 아마 그렇지 않을 것입니다.이 경우 어쨌든 메소드를 로컬 변수에 복사하려고 할 것입니다.
그리고이 해킹의 많은 단점이 있습니다. 그것은 "그것을하는 한 가지 분명한 방법"이 아닙니다. 인스턴스 dict에서 조회되지 않는 특수 메소드에는 작동하지 않습니다. 정적으로 애트리뷰트를 o2
에서 꺼내므로 나중에 새로운 애셋을 만들면 o1
이 프록시가되지 않습니다. 이렇게하면 프록시의 동적 체인을 만들 수 있습니다. 프록시가 많은 경우 많은 메모리를 낭비합니다. __getattr__
은 2.3에서 현재까지 똑같이 유지되고있는 반면에 (그리고 만약 당신이 inspect
에 의존한다면 2.x와 3.x 시리즈 내에서조차도) 파이썬 2.x와 3.x에서 약간 다릅니다 다른 파이썬 구현에서). 등등.
속도가 정말로 필요한 경우 프록시 방법을 캐시하는 하이브리드 : __getattr__
메서드를 고려할 수 있습니다. 한 번 호출되는 무언가, 클래스 속성에 언 바운드 메소드를 캐시하고 즉시 바인딩 할 수 있습니다. 그런 다음 반복적으로 호출되는 경우 바인딩 된 메서드를 인스턴스 특성에 캐시합니다.
왜 상속되지 않습니까? –
두 개체를 서로 다른 상황에서 격리하여 사용하기 때문에. – orange