dict처럼 작동하는 파이썬 클래스


113

나는 동작합니다 좋아하는 사용자 정의 클래스 작성하려면 dict, 그래서 내가로부터 상속 오전 - dict.

하지만 내 질문은 : dict__init__()메서드 에서 비공개 멤버 를 만들어야 합니까?입니다. 나는 dict단순히에서 상속하면 이미 동작 이 있기 때문에 이것의 요점을 보지 못합니다 dict.

대부분의 상속 스 니펫이 아래와 같은 이유를 누구든지 지적 할 수 있습니까?

class CustomDictOne(dict):
   def __init__(self):
      self._mydict = {} 

   # other methods follow

더 간단한 대신 ...

class CustomDictTwo(dict):
   def __init__(self):
      # initialize my other stuff here ...

   # other methods follow

사실 저는 사용자가 사전에 직접 액세스 할 수 없도록하는 질문에 대한 대답이라고 생각합니다 (즉, 사용자가 제공 한 액세스 방법을 사용해야 함).

그러나 배열 액세스 연산자는 []어떻습니까? 어떻게 구현할까요? 지금까지 []연산자 를 재정의하는 방법을 보여주는 예를 보지 못했습니다 .

따라서 []사용자 정의 클래스에 액세스 기능이 제공되지 않으면 상속 된 기본 메서드가 다른 사전에서 작동합니까?

Python 상속에 대한 이해를 테스트하기 위해 다음 스 니펫을 시도했습니다.

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(self, id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)
print md[id]

다음과 같은 오류가 발생했습니다.

KeyError : <내장 함수 ID>

위의 코드에 어떤 문제가 있습니까?

myDict이와 같은 코드를 작성할 수 있도록 클래스를 수정하려면 어떻게해야 합니까?

md = myDict()
md['id'] = 123

[편집하다]

책상에서 멀어지기 전에 만든 어리석은 오류를 없애기 위해 위의 코드 샘플을 편집했습니다. 오타였습니다 (오류 메시지에서 발견 했어야합니다).

답변:



105
class Mapping(dict):

    def __setitem__(self, key, item):
        self.__dict__[key] = item

    def __getitem__(self, key):
        return self.__dict__[key]

    def __repr__(self):
        return repr(self.__dict__)

    def __len__(self):
        return len(self.__dict__)

    def __delitem__(self, key):
        del self.__dict__[key]

    def clear(self):
        return self.__dict__.clear()

    def copy(self):
        return self.__dict__.copy()

    def has_key(self, k):
        return k in self.__dict__

    def update(self, *args, **kwargs):
        return self.__dict__.update(*args, **kwargs)

    def keys(self):
        return self.__dict__.keys()

    def values(self):
        return self.__dict__.values()

    def items(self):
        return self.__dict__.items()

    def pop(self, *args):
        return self.__dict__.pop(*args)

    def __cmp__(self, dict_):
        return self.__cmp__(self.__dict__, dict_)

    def __contains__(self, item):
        return item in self.__dict__

    def __iter__(self):
        return iter(self.__dict__)

    def __unicode__(self):
        return unicode(repr(self.__dict__))


o = Mapping()
o.foo = "bar"
o['lumberjack'] = 'foo'
o.update({'a': 'b'}, c=44)
print 'lumberjack' in o
print o

In [187]: run mapping.py
True
{'a': 'b', 'lumberjack': 'foo', 'foo': 'bar', 'c': 44}

37
subclass 를하려는 경우 단순히 인스턴스에 위임하는 대신 dict객체 자체 (사용 super)를 사용해야합니다. __dict__이는 본질적으로 모든 인스턴스에 대해 두 개의 딕셔너리를 생성한다는 것을 의미합니다.
Aaron Hall

8
self .__ dict__는 실제 사전 내용과 동일 하지 않습니다 . 모든 파이썬 객체는 유형에 관계없이 _dict__모든 객체 속성 (메서드, 필드 등)을 포함하는가 있습니다. 자신을 수정하는 코드를 작성하지 않는 한이 문제를 엉망으로 만들고 싶지 않습니다 .
Raik

85

이렇게

class CustomDictOne(dict):
   def __init__(self,*arg,**kw):
      super(CustomDictOne, self).__init__(*arg, **kw)

이제 dict.get()as 와 같은 내장 함수를 사용할 수 있습니다 self.get().

숨겨진을 감쌀 필요가 없습니다 self._dict. 귀하의 클래스는 이미 입니다 사전인가.


3
이. dict생성자를 먼저 호출하지 않고 는 상속 할 필요 가 없습니다 .
sykora

1
상속 된 항목 dict에는 실제로 2 개의 dict-instance가 포함되어 있습니다. 첫 번째는 상속 된 컨테이너이고 두 번째는 클래스 속성을 보유하는 사전 입니다. slots 를 사용하여 피할 수 있습니다 .
ankostis

1
__dict__너무 오래 사용자가, 그것을 사용하는 그것의 벌금을 시도하지 않는 한, 실제로 단지가 처음 액세스 할 때 생성된다. __slots__그래도 좋을 것입니다.
Aaron Hall

9
쉼표 뒤에 공백을 사용하십시오. ;-)
James Burke

10

완전성을 위해 다음은 최신 Python 2.x 용 @ björn-pollex가 언급 한 문서에 대한 링크입니다 (작성 당시 2.7.7).

컨테이너 유형 에뮬레이션

(주석 기능을 사용하지 않아서 죄송합니다. stackoverflow에 의해 그렇게 할 수 없습니다.)


3

이 코드 덩어리의 문제점 :

class myDict(dict):
    def __init__(self):
        self._dict = {}

    def add(id, val):
        self._dict[id] = val


md = myDict()
md.add('id', 123)

... 당신의 'add'메서드 (... 그리고 당신이 클래스의 멤버가되기를 원하는 모든 메소드)는 다음과 같이 명시적인 'self'를 첫 번째 인자로 선언해야한다는 것입니다.

def add(self, 'id', 23):

키로 항목에 액세스하기 위해 연산자 오버로딩을 구현하려면 문서 에서 매직 메서드 __getitem____setitem__.

Python은 Duck Typing을 사용하기 때문에 실제로 수행하려는 작업에 대해 더 많이 알지 못해도 언어의 dict 클래스에서 사용자 정의 dict 클래스를 파생 할 이유가 없을 수 있습니다 (예 :이 인스턴스를 전달해야하는 경우). isinstance(MyDict(), dict) == True)를 제외하고는 깨질 수있는 일부 코드로 클래스를 만들면 클래스를 충분히 사전과 유사하게 만드는 API를 구현하고 거기서 멈추는 것이 더 나을 수 있습니다.


3

다음은 대체 솔루션입니다.

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.__dict__ = self

a = AttrDict()
a.a = 1
a.b = 2

사용자 정의 방법을 정의하지 않았고 다른 답변에서 말한 것처럼 다른 문제가 있기 때문에 이것은 나쁩니다.
Shital Shah

이것이 바로 내가 필요한 것입니다. 감사합니다!
jakebrinkmann

2

나는 이것에 대한 정답이 어디에도 보이지 않는다.

class MyClass(dict):
    
    def __init__(self, a_property):
        self[a_property] = a_property

당신이 정말로해야 할 일은 당신 자신을 정의하는 것뿐입니다 __init__. 그게 전부입니다.

다른 예 (약간 더 복잡함) :

class MyClass(dict):

    def __init__(self, planet):
        self[planet] = planet
        info = self.do_something_that_returns_a_dict()
        if info:
            for k, v in info.items():
                self[k] = v

    def do_something_that_returns_a_dict(self):
        return {"mercury": "venus", "mars": "jupiter"}

이 마지막 예제는 어떤 종류의 논리를 포함하려는 경우에 유용합니다.

어쨌든 ... 간단히 말해 class GiveYourClassAName(dict)당신의 수업이 딕셔너리처럼 행동하도록하기에 충분합니다. 수행하는 모든 dict 작업 self은 일반 dict와 같습니다.


1

이것이 나의 최선의 해결책입니다. 나는 이것을 여러 번 사용했습니다.

class DictLikeClass:
    ...
    def __getitem__(self, key):
        return getattr(self, key)

    def __setitem__(self, key, value):
        setattr(self, key, value)
    ...

다음과 같이 사용할 수 있습니다.

>>> d = DictLikeClass()
>>> d["key"] = "value"
>>> print(d["key"])

0

파이썬 내장 dict에서 상속하지 마십시오! 예를 들어 update메소드는을 사용 __setitem__하지 않으며 최적화를 위해 많은 작업을 수행합니다. UserDict를 사용하십시오.

from collections import UserDict

class MyDict(UserDict):
    def __delitem__(self, key):
        pass
    def __setitem__(self, key, value):
        pass

1
누군가가 기본 제공 딕셔너리에서 상속해서는 안되는 것은 무엇입니까? 문서 ( docs.python.org/3/library/collections.html#collections.UserDict )에서 : "이 클래스의 필요성은 dict에서 직접 하위 클래스화할 수있는 기능으로 부분적으로 대체되었습니다. 그러나이 클래스는 기본 사전을 속성으로 액세스 할 수 있기 때문입니다. " 또한 같은 페이지에서 : collections 모듈은 "버전 3.3부터 사용되지 않음, 버전 3.9에서 제거 될 것입니다 : Collections Abstract Base Classes를 collections.abc 모듈로 이동했습니다."... UserDict가 없습니다.
NumesSanguis 19 dec

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