[python] dict를 올바르게 하위 클래스로 만들고 __getitem__ 및 __setitem__을 재정의하는 방법

일부 코드를 디버깅 중이며 특정 사전에 액세스하는시기를 알고 싶습니다. 글쎄, 실제로 dict는 몇 가지 추가 기능 을 하위 클래스로 만들고 구현하는 클래스입니다 . 어쨌든, 내가하고 싶은 것은 dict직접 서브 클래스 를 만들고 재정의를 추가 __getitem__하고 __setitem__디버깅 출력을 생성하는 것입니다. 지금은

class DictWatch(dict):
    def __init__(self, *args):
        dict.__init__(self, args)

    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        log.info("GET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val)))
        return val

    def __setitem__(self, key, val):
        log.info("SET %s['%s'] = %s" % str(dict.get(self, 'name_label')), str(key), str(val)))
        dict.__setitem__(self, key, val)

name_label'는 결국 출력을 식별하는 데 사용할 키입니다. 그런 다음 계측중인 클래스를 DictWatch대신 하위 클래스로 dict변경하고 수퍼 생성자에 대한 호출을 변경했습니다. 그래도 아무 일도 일어나지 않는 것 같습니다. 영리하다고 생각했지만 다른 방향으로 가야 할까.

도와 주셔서 감사합니다!



답변

당신이하는 일은 절대적으로 효과가 있어야합니다. 나는 당신의 수업을 테스트했으며 로그 문에 여는 괄호가 누락 된 것을 제외하고는 잘 작동합니다. 생각할 수있는 것은 두 가지뿐입니다. 첫째, 로그 문의 출력이 올바르게 설정 되었습니까? logging.basicConfig(level=logging.DEBUG)스크립트 맨 위에 를 넣어야 할 수도 있습니다 .

둘째, __getitem____setitem__동안에 만이라고 []액세스. 그래서 반드시 전용 액세스 확인 DictWatch을 통해를 d[key]보다는 d.get()d.set()


답변

서브 클래 싱의 또 다른 문제 dict는 내장이를 __init__호출하지 않고 update내장이를 update호출하지 않는다는 것 __setitem__입니다. 따라서 모든 setitem 작업이 __setitem__함수를 통과 하도록하려면 직접 호출되는지 확인해야합니다.

class DictWatch(dict):
    def __init__(self, *args, **kwargs):
        self.update(*args, **kwargs)

    def __getitem__(self, key):
        val = dict.__getitem__(self, key)
        print 'GET', key
        return val

    def __setitem__(self, key, val):
        print 'SET', key, val
        dict.__setitem__(self, key, val)

    def __repr__(self):
        dictrepr = dict.__repr__(self)
        return '%s(%s)' % (type(self).__name__, dictrepr)

    def update(self, *args, **kwargs):
        print 'update', args, kwargs
        for k, v in dict(*args, **kwargs).iteritems():
            self[k] = v


답변

서브 클래 싱 UserDict또는 UserList. 이러한 클래스는 일반 반면 서브 클래 싱하기위한 것입니다 dictlist아니며, 최적화가 포함되어 있습니다.


답변

그것은 결과를 실제로 변경해서는 안됩니다 (좋은 로깅 임계 값을 위해 작동해야 함) : init 는 다음과 같아야합니다.

def __init__(self,*args,**kwargs) : dict.__init__(self,*args,**kwargs)

대신 DictWatch ([(1,2), (2,3)]) 또는 DictWatch (a = 1, b = 2)로 메서드를 호출하면 실패하기 때문입니다.

(또는 더 나은, 이것에 대한 생성자를 정의하지 마십시오)


답변

당신이해야 할 일은

class BatchCollection(dict):
    def __init__(self, inpt={}):
        super(BatchCollection, self).__init__(inpt)

내 개인적인 사용을위한 샘플 사용

### EXAMPLE
class BatchCollection(dict):
    def __init__(self, inpt={}):
        super(BatchCollection, self).__init__(inpt)

    def __setitem__(self, key, item):
        if (isinstance(key, tuple) and len(key) == 2
                and isinstance(item, collections.Iterable)):
            # self.__dict__[key] = item
            super(BatchCollection, self).__setitem__(key, item)
        else:
            raise Exception(
                "Valid key should be a tuple (database_name, table_name) "
                "and value should be iterable")

참고 : python3에서만 테스트되었습니다.


답변

Andrew Pate 답변을 완성하기 위해 다음 dict과 같은 차이점을 보여주는 예가 있습니다 UserDict.

dict를 올바르게 덮어 쓰는 것은 까다 롭습니다.

class MyDict(dict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Bad! MyDict.__setitem__ not called
d.update(c=3)  # Bad! MyDict.__setitem__ not called
d['d'] = 4  # Good!
print(d)  # {'a': 1, 'b': 2, 'c': 3, 'd': 40}

UserDict에서 상속 collections.abc.MutableMapping하므로 사용자 정의하기가 훨씬 쉽습니다.

class MyDict(collections.UserDict):

  def __setitem__(self, key, value):
    super().__setitem__(key, value * 10)


d = MyDict(a=1, b=2)  # Good: MyDict.__setitem__ correctly called
d.update(c=3)  # Good: MyDict.__setitem__ correctly called
d['d'] = 4  # Good
print(d)  # {'a': 10, 'b': 20, 'c': 30, 'd': 40}

마찬가지로, 당신은 단지 구현해야 __getitem__자동으로와 호환되도록 key in my_dict, my_dict.get

참고 : UserDict의 서브 클래스가 아닌 dict, 그래서 isinstance(UserDict(), dict)실패 할 것이다 (하지만 isinstance(UserDict(), collections.abc.MutableMapping)작동합니다)


답변