2014-04-27 2 views
2

라이브러리에서 사용하는 일부 변수에 "감시자"를 추가하고 싶습니다. 유형은 collections.deque, dict, weakref.WeakSet, 및 list입니다.컬렉션 개체를 래핑하여 상태 변경을 로그하는 일반 개체 "감시자"프록시 (추가/삭제)

언제든지 항목이 추가/추가되거나 터지지 않을 때 로그 메시지 을 생성하고 싶습니다.

내가 원래 코드의 변경을 최소화하고자

:

  • 나는 변수에 액세스하고 수정하는 모든 장소에서 로그 메시지를 추가하지 않습니다.

  • 내가 원래 클래스에서 상속 내 자신의 클래스를 만들려하지 않는 대신 (예 : class InstrumentedDeque(collections.deque): ...)

, 이것이 문제이다, 그것은 가능 하나 제네릭을 만드는 것입니다 위의 모든 컬렉션 오브젝트에 대해 작동하는 클래스 (또는 래퍼/데코레이터)가 필요하므로 변경이 필요한 유일한 곳은 오브젝트가 처음 작성된 곳입니다. self.scheduledself.ready ... 다음

def __init__(self): 
    self.scheduled = [] 
    self.ready = collections.deque() 

같은 일 것입니다 필요한 변경 사항 만 ...

def __init__(self): 
    self.scheduled = MyLogger([], var_name='scheduled') 
    self.ready = MyLogger(collections.deque(), var_name='ready') 

장비없이 :이 "볼"2 개 바르 원래 코드 인 경우 계측 후

test = Test() 

test.add(1) 
test.add(2) 
test.pop() 

변경
test = Test() 

test.add(1) 
***instrumentation output. scheduled: [1] 
***instrumentation output. ready: deque([1]) 

test.add(2) 
***instrumentation output. scheduled: [1, 2] 
***instrumentation output. ready = deque([1, 2]) 

test.pop() 
***instrumentation output. scheduled: [2] 
***instrumentation output. ready: deque([2]) 

add()pop()는 다음과 같이 보일 것이다 0 ...

def add(self, val): 
     heapq.heappush(self.scheduled, val) 
     self.ready.append(val) 

    def pop(self): 
     heapq.heappop(self.scheduled) 
     self.ready.popleft() 

나는 "래퍼"클래스를 만드는 시도하고 __new__, __init__, __getattr__ 연주하지만하지 않은 것 이 기능을 사용하려면 이 필요합니다. 이게 뭔가 ...

class MyLooger: 
    def __new__(cls): 
     # what to do? 

    def __init__(self): 
     # what to do? 

    def __getattr__(self, name): 
     # what to do? 

어떤 도움을 주시면 감사하겠습니다.

답변

3

참고 : 다음과 같은 추상화 는하지 않습니다 랩 된 개체의 낮은 수준의 내부로 직접 이동 C 확장 코드를 사용하여 작업 (예 : heapq.heappush로, CPython의뿐만 아니라 PyPy에 모두); 파이썬 수준에서이를 완화 할 수있는 방법은 없습니다. C 레벨에서 "누수 패치"를 할 수 있는지 확인한 다음 C 및 Python 확장 프로그램을 작성하여 손을 더러워야합니다.

해결책 :__new__까지 갈 필요가 없습니다.다음은 모든 객체에서 일반적으로 작동합니다. 또한 래핑 된 객체에서 호출 된 것처럼 래퍼에서 isinstance이 작동합니다.

from functools import wraps 

class Logged(object): 
    def __init__(self, obj, obj_name): 
     self.obj = obj 
     self.obj_name = obj_name 

    def __getattribute__(self, attr_name): 
     obj = object.__getattribute__(self, 'obj') 
     obj_name = object.__getattribute__(self, 'obj_name') 

     attr = getattr(obj, attr_name) 
     # this is not 100% generic, mathematically speaking, 
     # but covers all methods and the `__class__` attribute: 
     if not callable(attr) or isinstance(attr, type): 
      return attr 

     @wraps(attr) 
     def fn(*args, **kwargs): 
      print "%s called on %s with: %s and %s" % (attr_name, obj_name, args, kwargs) 
      return attr(*args, **kwargs) 

     return fn 

    def __repr__(self): 
     return repr(object.__getattribute__(self, 'obj')) 

그리고 단지 :

내가 생각
>>> scheduled = Logged([], obj_name="scheduled") 

>>> scheduled.append 
<function append> 

>>> scheduled.append(3) 
append called on scheduled with: (3,) and {} 

>>> scheduled.extend([1,2]) 
extend called on scheduled with: ([1, 2],) and {} 

>>> isinstance(scheduled, list) 
True 

>>> scheduled 
[3, 1, 2] 
+0

, 당신은 '잊은는 –

+0

어디 .add'? 나는 그 대답을 몇 번 편집했다. –

+0

@jd. 일반적으로 작동하도록 코드를 업데이트했습니다. –