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


84

일부 코드를 디버깅 중이며 특정 사전에 액세스하는시기를 알고 싶습니다. 글쎄, 실제로 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변경하고 수퍼 생성자에 대한 호출을 변경했습니다. 그래도 아무 일도 일어나지 않는 것 같습니다. 영리하다고 생각했지만 다른 방향으로 가야 할까.

도와 주셔서 감사합니다!


로그 대신 인쇄를 사용하려고 했습니까? 또한 로그를 생성 / 구성하는 방법을 설명해 주시겠습니까?
pajton 2010 년

2
dict.__init__가져 *args?
Tom Russell

4
데코레이터에 대한 좋은 후보처럼 보입니다.
Tom Russell

답변:


39

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

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


실제로 그것은 여분의 괄호가 아니지만, 주변에 누락 된 괄호가 없습니다(str(dict.get(self, 'name_label')), str(key), str(val)))
cobbal

3
진실. OP : 향후 참조를 위해 Python 문자열 형식화 연산자 대신 log.info ( '% s % s % s', a, b, c)를 수행 할 수 있습니다.
BrainCore 2010 년

로깅 수준이 문제가되었습니다. 나는 다른 사람의 코드를 디버깅하고 있으며 원래 다른 수준의 디버깅 세트를 다루는 다른 파일에서 테스트하고있었습니다. 감사!
Michael Mior 2010 년

73

서브 클래 싱의 또 다른 문제 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

9
파이썬 3를 사용하는 경우 즉, 그래서 당신은이 예제를 변경하려는 것 print은 IS print()기능과 update()방법을 사용하는 items()대신 iteritems().
Al Sweigart 2017 년

나는 당신의 졸을 시도하지만 그것은 단지 작동 것으로 보인다 단 하나의 색인의 레벨 (즉, DICT [키]가 아닌 DICT [KEY1] [키 2] ...) *
앤드류 기브

d [key1]은 사전과 같은 무언가를 반환합니다. 두 번째 키는이를 인덱싱합니다. 이 기술은 반환 된 것이 시계 동작을 지원하지 않는 한 작동하지 않습니다.
Matt Anderson

1
@AndrewNaguib : 왜 중첩 배열에서 작동해야합니까? 중첩 배열은 일반 파이썬 사전에서도 작동하지 않습니다 (직접 구현하지 않은 경우)
Igor Chubin

1
@AndrewNaguib은 : __getitem__테스트해야 val만 조건부 그렇게 - 예if isinstance(val, dict): ...
마티

14

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


9
참고 로 Python 3.6 의 문서 에는 "이 클래스의 필요성이 부분적으로 dict에서 직접 하위 클래스화할 수있는 기능으로 대체되었습니다. 그러나 기본 사전을 속성으로 액세스 할 수 있기 때문에이 클래스를 사용하는 것이 더 쉬울 수 있습니다."라고 말합니다.
Sean

@andrew 예제가 도움이 될 수 있습니다.
Vasantha Ganesh K


9

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

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

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

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


나는 dict[key]액세스 의 형태에 대해서만 걱정 하므로 문제가되지 않습니다.
Michael Mior 2010 년

1

당신이해야 할 일은

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에서만 테스트되었습니다.


0

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)작동합니다)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.