속성과 같은 dict 키에 액세스합니까?


303

dict obj.foo대신 dict 키에 액세스하는 것이 더 편리하다는 것을 알았습니다 obj['foo']. 그래서이 스 니펫을 작성했습니다.

class AttributeDict(dict):
    def __getattr__(self, attr):
        return self[attr]
    def __setattr__(self, attr, value):
        self[attr] = value

그러나 파이썬 이이 기능을 즉시 제공하지 않는 이유가 있다고 가정합니다. 이런 식으로 dict 키에 액세스 할 때의주의 사항과 함정은 무엇입니까?


16
어디에서나 고정 크기 제한 세트에서 하드 코드 된 키에 액세스하는 경우 키를 보유한 객체를 만드는 것이 좋습니다. collections.namedtuple이것에 매우 유용합니다.

6
stackoverflow.com/questions/3031219/...는 유사한 솔루션을 제공하지만 추가 단계를 진행
keflavich

1
github.com/bcj/AttrDict 에서 이를 위한 모듈을 찾았 습니다 . 여기 및 관련 질문의 솔루션과 어떻게 비교되는지 모르겠습니다.
matt wilkie 0시 52 분

나도 비슷한 핵을 사용 easydict.EasyDict
muon

'.'을 사용하여 사전 멤버에 액세스하는 더 많은 방법 : stackoverflow.com/questions/2352181/…
Pale Blue Dot

답변:


304

이를 수행하는 가장 좋은 방법은 다음과 같습니다.

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

일부 전문가 :

  • 실제로 작동합니다!
  • 사전 클래스 메소드가 음영 처리되지 않았습니다 (예 : .keys()제대로 작동합니다. 물론 값을 지정하지 않는 한 아래 참조)
  • 속성과 아이템은 항상 동기화
  • 존재하지 않는 키에 속성으로 액세스하려고하면 AttributeError대신에KeyError

단점 :

  • 들어오는 데이터로 덮어 쓰는 경우 와 같은 방법 .keys()제대로 작동 하지 않습니다.
  • 원인 메모리 누수 파이썬 <2.7.4 / Python3 <3.2.3
  • Pylint과 함께 바나나를 이동 E1123(unexpected-keyword-arg)하고E1103(maybe-no-member)
  • 처음에는 마치 순수한 마법처럼 보입니다.

작동 방식에 대한 간단한 설명

  • 모든 파이썬 객체는 내부적으로 속성을이라는 사전에 저장합니다 __dict__.
  • 내부 사전 __dict__이 "단순한 사전"일 필요는 없기 때문에 하위 dict()사전을 내부 사전에 할당 할 수 있습니다 .
  • 이 경우 AttrDict()인스턴스화하는 인스턴스를 (있는 그대로) 지정하면 __init__됩니다.
  • super()__init__()메소드 를 호출 하여 해당 함수가 모든 사전 인스턴스화 코드를 호출하기 때문에 (이미) 사전과 똑같이 동작하는지 확인했습니다 .

파이썬이이 기능을 즉시 제공하지 않는 한 가지 이유

"cons"목록에서 언급했듯이, 이것은 저장된 키의 네임 스페이스 (임의의 데이터 및 / 또는 신뢰할 수없는 데이터에서 올 수 있습니다!)를 내장 된 dict 메소드 속성의 네임 스페이스와 결합합니다. 예를 들면 다음과 같습니다.

d = AttrDict()
d.update({'items':["jacket", "necktie", "trousers"]})
for k, v in d.items():    # TypeError: 'list' object is not callable
    print "Never reached!"

1
>>> class MyD (object) : ... def init __ (self, d) : ... self .__ dict = d
Rafe

2.7
pi

1
그것이 내가 사용하는 것이므로 <= 2.7.3으로 만드십시오.
pi.

1
2.7.4 릴리스 노트에서는 수정되지 않았다고 언급합니다 (이전은 아님).
Robert Siemer

1
@viveksinghggits는를 통해 사물에 액세스하기 때문에 .언어의 규칙을 어길 수 없습니다. :) AttrDict공백이 포함 된 필드를 다른 것으로 자동 변환 하고 싶지는 않습니다 .
Yurik

125

배열 표기법을 사용하는 경우 모든 유효한 문자열 문자를 키의 일부로 사용할 수 있습니다. 예를 들어obj['!#$%^&*()_']


1
@ 이즈 카타 예. SE에 관한 재미있는 점은 일반적으로 '최상의 질문'이 있다는 것입니다. 제목과 '하단 질문', 아마도 SE가 "제목이 다 말하는 것"을 듣고 싶지 않기 때문일 것입니다. '캐비티'는 가장 아래쪽에 있습니다.
n611x007

2
아니 자바 스크립트 언어 프로그래밍의 특히 좋은 예이지만, JS에서 개체 속성 액세스 및 일반적인 경우 편리 할 수 있습니다 배열 표기법 모두 지원하는지 법적 속성 이름없는 기호에 대한 일반적인 대체합니다.
André Caron

@Izkata 이것은 어떻게 질문에 대답합니까? 이 답변은 키가 어떤 이름이든 가질 수 있다고 말합니다.
Melab

4
@Melab 질문은 What would be the caveats and pitfalls of accessing dict keys in this manner?(속성으로), 여기에 표시된 대부분의 문자를 사용할 수 없다는 것입니다.
이즈 카타

83

에서 이 다른 SO 질문 기존의 코드를 단순화 좋은 구현 예제가있다. 어때요 :

class AttributeDict(dict): 
    __getattr__ = dict.__getitem__
    __setattr__ = dict.__setitem__

훨씬 더 간결은에 들어가기 추가 cruft에 대한 공간을 확보하지 않으며 __getattr____setattr__향후 기능.


이 메소드를 사용하여 AttributeDict.update 또는 AttributeDict.get을 호출 할 수 있습니까?
Dor

13
런타임에 새로운 속성을 추가하면 dict 자체에 추가되지 않고 dict 속성에 추가됩니다. 예 d = AttributeDict(foo=1). d.bar = 1bar 속성은 dict 속성 안에 저장 되지만 dict 자체 에는 저장 되지 않습니다. 인쇄 d에는 foo 항목 만 표시됩니다.
P3trus

7
내가 말할 수있는 한 완벽하게 작동하기 때문에 +1. @GringoSuave, @Izkata, @ P3trus 나는 실패한다고 주장하는 사람에게 작동하지 않는 예제를 보여달라고 요청합니다 d = AttributeDict(foo=1);d.bar = 1;print d=> {'foo': 1, 'bar': 1}Works for me!
Dave Abrahams

4
@DaveAbrahams 전체 질문을 읽고 Hery, Ryan 및 TheCommunistDuck의 답변을보십시오. 이 작업을 수행 하는 방법 이 아니라 발생할 수있는 문제대해 묻습니다 .
이즈 카타

6
당신은 제공해야 __getattr__이 제기 방법 AttributeError지정된 속성이 존재하지 않는 경우는 true, 그렇지 않은 경우는 상황이 좋아하는 getattr(obj, attr, default_value)(즉, 반환하지 않습니다하지 작업을 할 default_value경우 attr에 존재하지 않는 obj)
jcdude

83

내가 묻는 질문에 대답

파이썬은 왜 그것을 즉시 제공하지 않습니까?

파이썬선과 관련 이 있다고 생각합니다 . "한 가지 확실한 방법이 있어야합니다." 이 사전에서 액세스 값이 분명한 방법을 만들 것입니다 : obj['key']obj.key .

경고와 함정

여기에는 코드의 명확성 및 혼란 부족이 포함됩니다. 즉, 다음 에 나중에 코드를 유지 관리 하려는 다른 사람이나 잠시 동안 코드를 다시 사용하지 않으면 다른 사람 에게 혼란을 줄 수 있습니다 . 다시 Zen에서 : "가독성이 중요합니다!"

>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1

경우 d인스턴스화 또는 KEY 정의 또는 d[KEY] 멀리 떨어진 곳에서 할당d.spam 사용되는이 일반적으로 사용되는 관용구하지 않기 때문에, 그것은 쉽게 이루어지고 있는지에 대한 혼란으로 이어질 수 있습니다. 나는 그것이 나를 혼란스럽게 할 가능성이 있다는 것을 안다.

KEY또한 다음과 같이 값을 변경하면 변경 사항이 누락됩니다 d.spam.

>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'

노력할 가치가없는 IMO.

다른 물건들

다른 사람들이 지적했듯이 문자열만이 아닌 해시 가능 객체를 dict 키로 사용할 수 있습니다. 예를 들어

>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>> 

합법적이지만

>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
  File "<stdin>", line 1
  d.(2, 3)
    ^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>> 

아니다. 이를 통해 객체 속성에 액세스 할 때 보유 할 수없는 사전 키에 대해 인쇄 가능한 모든 문자 또는 기타 해시 가능 객체에 액세스 할 수 있습니다. 이를 통해 Python Cookbook (Ch. 9) 의 레시피와 같은 캐시 된 객체 메타 클래스와 같은 마술이 가능해 집니다.

내가 편집하는 곳

나는 spam.eggs이상의 미학을 선호하고 spam['eggs'](더 깨끗해 보인다고 생각합니다 namedtuple.)를 만났을 때이 기능을 갈망하기 시작했습니다 . 그러나 다음을 수행 할 수 있다는 편의성이 우선합니다.

>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>

이것은 간단한 예이지만, obj.key표기법을 사용하는 것과 다른 상황에서 (예 : XML 파일에서 사전 설정을 읽어야 할 때) dicts를 자주 사용 합니다. 다른 경우에는 미학적 이유로 동적 클래스를 인스턴스화하고 그에 대한 일부 특성을 포기하려는 유혹을받는 경우 가독성을 높이기 위해 일관성에 대한 dict을 계속 사용합니다.

OP가 오랫동안 만족스럽게 해결 한 것으로 확신하지만 여전히이 기능을 원한다면 pypi에서 패키지 중 하나를 다운로드하여 제안합니다.

  • 무리 는 내가 더 친숙한 것입니다. 의 하위 클래스dict이므로 모든 기능이 있습니다.
  • AttrDict 도 꽤 좋은 것처럼 보이지만 나는 그것에 익숙하지 않고 Bunch 만큼 많은 소스를 보지 못했습니다.
  • Addict 적극적으로 유지 관리되며 attr와 같은 액세스 등을 제공합니다.
  • Rotareti의 의견에서 언급했듯이 Bunch는 더 이상 사용되지 않지만 Munch 라는 활성 포크가 있습니다.

그러나 코드의 가독성을 향상시키기 위해 그의 표기 스타일을 혼합 하지 않는 것이 좋습니다 . 이 표기법을 선호하는 경우에는 동적 객체를 인스턴스화하고 원하는 속성을 추가 한 다음 하루에 호출해야합니다.

>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}


주석에서 후속 질문에 대답하기 위해 업데이트

엘모 는 다음 과 같은 의견에서 다음과 같이 묻습니다.

더 깊이 가고 싶다면 어떻게해야합니까? (type (...) 참조)

이 유스 케이스를 사용한 적이 없지만 ( dict일관 적으로 nested를 사용하는 경향이 있지만 ) 다음 코드가 작동합니다.

>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
...     setattr(d, x, C())
...     i = 1
...     for y in 'one two three'.split():
...         setattr(getattr(d, x), y, i)
...         i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}

1
무리 는 더 이상 사용되지 않지만 활성 포크가 있습니다 : github.com/Infinidat/munch
Rotareti

@Rotareti-고마워요! 이것은 내가 사용하는 기능이 아니므로 그 사실을 몰랐습니다.
Doug R.

더 깊이 가고 싶다면 어떻게해야합니까? (type (...) 참조)
Ole Aldric

6
파이썬은 폭우가 높고 뒤집힌 우산과 같습니다. 모든 것이 시작하기에 현명하고 펑키 해 보입니다. 어느 정도 시간이 지나면 SE에서 내장 전문가를 읽고 갑자기 전체 페이로드가 어깨 아래로 되돌아갑니다. 여전히 흠뻑 젖어 있으면 더 가벼워지고 모든 것이 깨끗하고 상쾌합니다.
Ole Aldric


19

표준 라이브러리에서 편리한 컨테이너 클래스를 가져올 수 있습니다.

from argparse import Namespace

코드 비트를 복사하지 않아도됩니다. 표준 사전 액세스는 없지만 원하는 경우 쉽게 다시 가져올 수 있습니다. argparse의 코드는 간단합니다.

class Namespace(_AttributeHolder):
    """Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple
    string representation.
    """

    def __init__(self, **kwargs):
        for name in kwargs:
            setattr(self, name, kwargs[name])

    __hash__ = None

    def __eq__(self, other):
        return vars(self) == vars(other)

    def __ne__(self, other):
        return not (self == other)

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

2
OP가 첫 번째 주석을 처리하는 표준 라이브러리를 참조하기위한 PLUS 1
Gordon Bean

4
파이썬은이 경우에 더 빠른 클래스 (C로 구현 됨)를 포함합니다. types.SimpleNamespace docs.python.org/dev/library/types.html#types.SimpleNamespace
Nuno André

18

__eq__또는 같은 방법 인 키를 원한다면 어떻게해야 __getattr__합니까?

그리고 당신은 편지로 시작하지 않은 항목을 가질 수 없으므로 0343853키로 사용 하는 것은 불가능합니다.

문자열을 사용하지 않으려면 어떻게해야합니까?


실제로 또는 예를 들어 키와 같은 다른 객체. 그러나 나는 그 오류를 '예상 된 행동'으로 분류하고 싶습니다. 제 질문에 예기치 않은 것을 목표로 삼았습니다.
Izz ad-Din Ruhulessin

pickle.dump용도__getstate__
Cees Timmerman

12

튜플은 dict 키를 사용할 수 있습니다. 구문에서 튜플에 어떻게 액세스 하시겠습니까?

또한 namedtuple 은 속성 액세스를 통해 값을 제공 할 수있는 편리한 구조입니다.


7
명명 된 튜플의 단점은 변경할 수 없다는 것입니다.
Izz ad-Din Ruhulessin

10
불변이되는 것은 버그가 아니라 튜플의 기능이라고 말하는 사람들도 있습니다.
ben author

9

내가 모두를 지배하기 위해 작은 파이썬 클래스 인 Prodict어떻습니까? )

또한 자동 코드 완성 , 재귀 객체 인스턴스화자동 유형 변환 기능을 사용할 수 있습니다 !

당신은 당신이 요구 한 것을 정확하게 할 수 있습니다 :

p = Prodict()
p.foo = 1
p.bar = "baz"

예 1 : 타입 힌트

class Country(Prodict):
    name: str
    population: int

turkey = Country()
turkey.name = 'Turkey'
turkey.population = 79814871

자동 코드 완성

예 2 : 자동 유형 변환

germany = Country(name='Germany', population='82175700', flag_colors=['black', 'red', 'yellow'])

print(germany.population)  # 82175700
print(type(germany.population))  # <class 'int'>

print(germany.flag_colors)  # ['black', 'red', 'yellow']
print(type(germany.flag_colors))  # <class 'list'>

2
pip를 통해 python2에 설치되지만 python2에서는 작동하지 않습니다
Ant6n

2
@ Ant6n은 형식 주석 때문에 파이썬 3.6 이상이 필요합니다
Ramazan Polat

8

일반적으로 작동하지 않습니다. 유효한 모든 dict 키가 주소 지정 가능한 속성 ( "키")을 만드는 것은 아닙니다. 따라서 조심해야합니다.

파이썬 객체는 모두 기본적으로 사전입니다. 따라서 많은 성능이나 다른 처벌이 의심됩니다.


8

이것은 원래의 질문을 다루지는 않지만이 기능을 제공하는 lib를 찾을 때 나와 같은 사람들에게 유용해야합니다.

중독자 는 이것에 대한 좋은 lib 디렉토리입니다 : https://github.com/mewwts/addict 그것은 이전 답변에서 언급 된 많은 관심을 담당한다.

문서의 예 :

body = {
    'query': {
        'filtered': {
            'query': {
                'match': {'description': 'addictive'}
            },
            'filter': {
                'term': {'created_by': 'Mats'}
            }
        }
    }
}

중독자와 함께 :

from addict import Dict
body = Dict()
body.query.filtered.query.match.description = 'addictive'
body.query.filtered.filter.term.created_by = 'Mats'

8

파이썬 생태계에서 현재 "상태 키로 attr"상태가 무엇인지 궁금합니다. 여러 의견 제시 자들이 지적했듯이, 이것은 함정과 풋 건이 여러 개 있기 때문에 처음부터 직접 롤링하려는 것이 아닐 수도 있습니다. 또한 사용하지 않는 것이 좋습니다Namespace 기본 수업으로 . 나는 그 길을 가봤습니다.

다행히도이 기능을 제공하는 여러 오픈 소스 패키지가 있으며 설치 준비가 완료되었습니다! 불행히도 여러 패키지가 있습니다. 2019 년 12 월 현재 시놉시스는 다음과 같습니다.

경쟁자 (가장 최근 커밋에 대한 커밋 | #commits | #contribs | coverage %) :

  • 중독자 (2019-04-28 | 217 | 22 | 100 %)
  • 뭉크 (2019-12-16 | 160 | 17 |? %)
  • easydict (2018-10-18 | 51 | 6 |? %)
  • 특성 (2019-02-01 | 108 | 5 | 100 %)
  • prodict (2019년 10월 1일 | 65 | 1 |? %)

더 이상 유지 관리되지 않거나 유지 관리되지 않음 :

  • treedict (2014-03-28 | 95 | 2 |? %)
  • 무리 (2012-03-12 | 20 | 2 |? %)
  • 네오 번치

나는 현재 뭉크 또는 중독자를 추천 합니다. 커밋, 기고자 및 릴리스가 가장 많으며 각각에 대해 건강한 오픈 소스 코드베이스를 제안합니다. 그들은 가장 깨끗하게 보이는 readme.md, 100 % 적용 범위 및 잘 보이는 테스트 세트를 가지고 있습니다.

내 자신의 dict / attr 코드를 굴려서이 옵션을 모두 알지 못했기 때문에 많은 시간을 낭비하는 것 외에도이 경주에 개가 없습니다 (현재!). 나는 미래에 중독자 / 뭉크에 기여할 수 있습니다. 당신이 그들을 좋아한다면, 기여하십시오! 특히 munch는 codecov 배지를 사용할 수 있고 addict는 python 버전 배지를 사용할 수 있습니다.

중독자 전문가 :

  • 재귀 초기화 (foo.abc = 'bar'), dict-like arguments는 중독자가됩니다.

중독자 단점 :

  • typing.Dict당신이 그림자from addict import Dict
  • 키 검사가 없습니다. 재귀 초기화를 허용하기 때문에 키의 철자가 틀리면 KeyError 대신 새 속성을 만듭니다 (AljoSt 덕분에)

뭉크 프로 :

  • 독특한 이름
  • JSON 및 YAML 용 내장 ser / de 함수

뭉크 단점 :

  • 재귀 적 인 초기화 없음 / 한 번에 하나의 attr 만 초기화 할 수 있음

내가 편집하는 곳

많은 달 전에 텍스트 편집기를 사용하여 파이썬을 작성했을 때 나 자신이나 다른 개발자와 함께하는 프로젝트에서 dict-attrs 스타일, 선언하여 키를 삽입하는 기능이 마음에 들었습니다 foo.bar.spam = eggs. 이제 팀에서 일하고 모든 것을 위해 IDE를 사용하며 정적 분석, 기능 기술 및 유형 힌트를 선호하여 이러한 종류의 데이터 구조 및 동적 타이핑을 피했습니다. 필자는이 기술을 실험하면서 Pstruct를 자신 만의 디자인 객체로 서브 클래 싱했습니다.

class  BasePstruct(dict):
    def __getattr__(self, name):
        if name in self.__slots__:
            return self[name]
        return self.__getattribute__(name)

    def __setattr__(self, key, value):
        if key in self.__slots__:
            self[key] = value
            return
        if key in type(self).__dict__:
            self[key] = value
            return
        raise AttributeError(
            "type object '{}' has no attribute '{}'".format(type(self).__name__, key))


class FooPstruct(BasePstruct):
    __slots__ = ['foo', 'bar']

이것은 당신에게 여전히 dict처럼 행동하는 객체를 제공하지만 훨씬 더 엄격한 방식으로 속성과 같은 키에 액세스 할 수 있습니다. 여기서 장점은 (또는 코드의 불쾌한 소비자) 필드가 존재하고 존재하지 않는 필드를 정확히 알고 IDE가 필드를 자동 완성 할 수 있다는 것입니다. 또한 바닐라 서브 클래 싱 dict은 JSON 직렬화가 쉽다는 것을 의미합니다. 이 아이디어의 다음 진화는 이러한 인터페이스를내는 맞춤형 프로토 타입 생성기 일 것이라고 생각합니다. gRPC를 통해 언어 간 데이터 구조와 IPC를 거의 무료로 얻을 수 있습니다.

attr-dicts를 사용하기로 결정했다면 자신과 팀 동료의 정신을 위해 어떤 필드가 예상되는지 문서화해야합니다.

이 게시물을 최신 상태로 유지하려면 언제든지 수정 / 업데이트하십시오!


2
addict단점은 속성의 철자가 틀렸을 때 예외 Dict를 일으키지 않는다는 것입니다. 새 속성을 반환하므로 (foo.abc = 'bar'가 작동하는 데 필요합니다).
AljoSt

5

다음은 내장을 사용하는 불변 레코드의 간단한 예입니다 collections.namedtuple.

def record(name, d):
    return namedtuple(name, d.keys())(**d)

사용 예 :

rec = record('Model', {
    'train_op': train_op,
    'loss': loss,
})

print rec.loss(..)

5

답에 다양성을 더하기 위해 sci-kit learn 는 이것을 다음과 같이 구현했습니다 Bunch.

class Bunch(dict):                                                              
    """ Scikit Learn's container object                                         

    Dictionary-like object that exposes its keys as attributes.                 
    >>> b = Bunch(a=1, b=2)                                                     
    >>> b['b']                                                                  
    2                                                                           
    >>> b.b                                                                     
    2                                                                           
    >>> b.c = 6                                                                 
    >>> b['c']                                                                  
    6                                                                           
    """                                                                         

    def __init__(self, **kwargs):                                               
        super(Bunch, self).__init__(kwargs)                                     

    def __setattr__(self, key, value):                                          
        self[key] = value                                                       

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

    def __getattr__(self, key):                                                 
        try:                                                                    
            return self[key]                                                    
        except KeyError:                                                        
            raise AttributeError(key)                                           

    def __setstate__(self, state):                                              
        pass                       

당신이 필요로하는 모두는 얻을 것입니다 setattrgetattr더 - 방법 getattr실제 속성을 검사에게 DICT 키를 확인하고 움직임을. 은 setstaet산세 / unpickling "송이"에 대한 수정 사항에 대한 수정이다 - inerested 체크하면 https://github.com/scikit-learn/scikit-learn/issues/6196


3

setattr () 및 getattr ()이 이미 존재 하므로 직접 작성할 필요가 없습니다 .

클래스 객체의 장점은 아마도 클래스 정의와 상속에서 작용합니다.


3

이 스레드의 입력을 기반으로 이것을 만들었습니다. 그래도 odict를 사용해야하므로 get 및 set attr을 재정의해야했습니다. 나는 이것이 대부분의 특수 용도에 효과가 있다고 생각합니다.

사용법은 다음과 같습니다.

# Create an ordered dict normally...
>>> od = OrderedAttrDict()
>>> od["a"] = 1
>>> od["b"] = 2
>>> od
OrderedAttrDict([('a', 1), ('b', 2)])

# Get and set data using attribute access...
>>> od.a
1
>>> od.b = 20
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])

# Setting a NEW attribute only creates it on the instance, not the dict...
>>> od.c = 8
>>> od
OrderedAttrDict([('a', 1), ('b', 20)])
>>> od.c
8

클래스:

class OrderedAttrDict(odict.OrderedDict):
    """
    Constructs an odict.OrderedDict with attribute access to data.

    Setting a NEW attribute only creates it on the instance, not the dict.
    Setting an attribute that is a key in the data will set the dict data but 
    will not create a new instance attribute
    """
    def __getattr__(self, attr):
        """
        Try to get the data. If attr is not a key, fall-back and get the attr
        """
        if self.has_key(attr):
            return super(OrderedAttrDict, self).__getitem__(attr)
        else:
            return super(OrderedAttrDict, self).__getattr__(attr)


    def __setattr__(self, attr, value):
        """
        Try to set the data. If attr is not a key, fall-back and set the attr
        """
        if self.has_key(attr):
            super(OrderedAttrDict, self).__setitem__(attr, value)
        else:
            super(OrderedAttrDict, self).__setattr__(attr, value)

이것은 이미 스레드에서 언급 된 매우 멋진 패턴이지만, dict을 가져 와서 IDE 등에서 자동 완성 기능이있는 객체로 변환하려는 경우 :

class ObjectFromDict(object):
    def __init__(self, d):
        self.__dict__ = d


3

이것이 내가 사용하는 것입니다

args = {
        'batch_size': 32,
        'workers': 4,
        'train_dir': 'train',
        'val_dir': 'val',
        'lr': 1e-3,
        'momentum': 0.9,
        'weight_decay': 1e-4
    }
args = namedtuple('Args', ' '.join(list(args.keys())))(**args)

print (args.lr)

이것은 좋은 빠르고 더러운 답변입니다. 내 유일한 관찰 / 코멘트는 namedtuple 생성자가 문자열 목록을 받아 들일 것이라고 생각하므로 솔루션을 단순화 할 수 있습니다.namedtuple('Args', list(args.keys()))(**args)
Dan Nguyen

2

방금 만든이 클래스를 사용하여 수행 할 수 있습니다. 이 클래스를 사용하면 Map다른 사전 (json 직렬화 포함) 또는 점 표기법과 같은 객체를 사용할 수 있습니다 . 나는 당신을 돕기를 바랍니다 :

class Map(dict):
    """
    Example:
    m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
    """
    def __init__(self, *args, **kwargs):
        super(Map, self).__init__(*args, **kwargs)
        for arg in args:
            if isinstance(arg, dict):
                for k, v in arg.iteritems():
                    self[k] = v

        if kwargs:
            for k, v in kwargs.iteritems():
                self[k] = v

    def __getattr__(self, attr):
        return self.get(attr)

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

    def __setitem__(self, key, value):
        super(Map, self).__setitem__(key, value)
        self.__dict__.update({key: value})

    def __delattr__(self, item):
        self.__delitem__(item)

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

사용 예 :

m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
# Add new key
m.new_key = 'Hello world!'
print m.new_key
print m['new_key']
# Update values
m.new_key = 'Yay!'
# Or
m['new_key'] = 'Yay!'
# Delete key
del m.new_key
# Or
del m['new_key']

1
dict메서드와 같이 그림자를 만들 수 있습니다 . 예 : m=Map(); m["keys"] = 42; m.keys()gives TypeError: 'int' object is not callable.
bfontaine

@bfontaine 아이디어는 일종이 field/attribute아니라 일종의 method것이지만 메소드 대신 숫자를 할당하면을 사용하여 해당 메소드에 액세스 할 수 있습니다 m.method().
epool

2

Kinvais의 답변을 기반으로하지만 http://databio.org/posts/python_AttributeDict.html 에서 제안 된 AttributeDict의 아이디어를 통합하는 다른 구현을 게시하겠습니다 .

이 버전의 장점은 중첩 된 사전에서도 작동한다는 것입니다.

class AttrDict(dict):
    """
    A class to convert a nested Dictionary into an object with key-values
    that are accessible using attribute notation (AttrDict.attribute) instead of
    key notation (Dict["key"]). This class recursively sets Dicts to objects,
    allowing you to recurse down nested dicts (like: AttrDict.attr.attr)
    """

    # Inspired by:
    # http://stackoverflow.com/a/14620633/1551810
    # http://databio.org/posts/python_AttributeDict.html

    def __init__(self, iterable, **kwargs):
        super(AttrDict, self).__init__(iterable, **kwargs)
        for key, value in iterable.items():
            if isinstance(value, dict):
                self.__dict__[key] = AttrDict(value)
            else:
                self.__dict__[key] = value

1
class AttrDict(dict):

     def __init__(self):
           self.__dict__ = self

if __name__ == '____main__':

     d = AttrDict()
     d['ray'] = 'hope'
     d.sun = 'shine'  >>> Now we can use this . notation
     print d['ray']
     print d.sun

1

해결책은 다음과 같습니다.

DICT_RESERVED_KEYS = vars(dict).keys()


class SmartDict(dict):
    """
    A Dict which is accessible via attribute dot notation
    """
    def __init__(self, *args, **kwargs):
        """
        :param args: multiple dicts ({}, {}, ..)
        :param kwargs: arbitrary keys='value'

        If ``keyerror=False`` is passed then not found attributes will
        always return None.
        """
        super(SmartDict, self).__init__()
        self['__keyerror'] = kwargs.pop('keyerror', True)
        [self.update(arg) for arg in args if isinstance(arg, dict)]
        self.update(kwargs)

    def __getattr__(self, attr):
        if attr not in DICT_RESERVED_KEYS:
            if self['__keyerror']:
                return self[attr]
            else:
                return self.get(attr)
        return getattr(self, attr)

    def __setattr__(self, key, value):
        if key in DICT_RESERVED_KEYS:
            raise AttributeError("You cannot set a reserved name as attribute")
        self.__setitem__(key, value)

    def __copy__(self):
        return self.__class__(self)

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

1

이런 식으로 dict 키에 액세스 할 때의주의 사항과 함정은 무엇입니까?

@Henry가 제안했듯이 dots-access가 dicts에서 사용되지 않는 한 가지 이유는 dict 키 이름을 python-valid 변수로 제한하여 가능한 모든 이름을 제한하기 때문입니다.

다음은 dict에 따라 점으로 구분 된 액세스가 일반적으로 도움이되지 않는 이유에 대한 예입니다 d.

타당성

다음 속성은 Python에서 유효하지 않습니다.

d.1_foo                           # enumerated names
d./bar                            # path names
d.21.7, d.12:30                   # decimals, time
d.""                              # empty strings
d.john doe, d.denny's             # spaces, misc punctuation 
d.3 * x                           # expressions  

스타일

PEP8 규칙은 속성 이름 지정에 소프트 제약을 부과합니다.

A. 예약 키워드 (또는 내장 함수) 이름 :

d.in
d.False, d.True
d.max, d.min
d.sum
d.id

함수 인수의 이름이 예약 키워드와 충돌하는 경우 일반적으로 단일 후행 밑줄을 추가하는 것이 좋습니다 ...

B. 메소드변수 이름 에 대한 사례 규칙 :

변수 이름은 함수 이름과 동일한 규칙을 따릅니다.

d.Firstname
d.Country

가독성을 높이기 위해 필요에 따라 밑줄로 구분 된 단어가있는 소문자 기능 명명 규칙을 사용하십시오.


때때로 팬더와 같은 라이브러리 에서 이러한 우려가 제기됩니다. 하여 이름으로 DataFrame 열을 점으로 액세스 할 수 있습니다. 명명 제한을 해결하는 기본 메커니즘은 배열 표기법 (괄호 안의 문자열)입니다.

이러한 제약 조건이 사용 사례에 적용되지 않으면 점선 액세스 데이터 구조에 대한 몇 가지 옵션이 있습니다 .



1

이것은 '좋은'답변은 아니지만 이것이 멋지다고 생각했습니다 (현재 형태의 중첩 된 dicts를 처리하지는 않습니다). dict을 함수로 간단히 감싸십시오.

def make_funcdict(d=None, **kwargs)
    def funcdict(d=None, **kwargs):
        if d is not None:
            funcdict.__dict__.update(d)
        funcdict.__dict__.update(kwargs)
        return funcdict.__dict__
    funcdict(d, **kwargs)
    return funcdict

이제 약간 다른 구문이 있습니다. dict 항목을 속성에 액세스합니다 f.key. 일반적인 방식으로 dict 항목 (및 다른 dict 메소드)에 액세스하려면 f()['key']키워드 인수 및 / 또는 사전을 사용하여 f를 호출하여 dict를 편리하게 업데이트 할 수 있습니다.

d = {'name':'Henry', 'age':31}
d = make_funcdict(d)
>>> for key in d():
...     print key
... 
age
name
>>> print d.name
... Henry
>>> print d.age
... 31
>>> d({'Height':'5-11'}, Job='Carpenter')
... {'age': 31, 'name': 'Henry', 'Job': 'Carpenter', 'Height': '5-11'}

그리고 거기에 있습니다. 이 방법의 이점과 단점을 제안하는 사람이 있으면 기쁠 것입니다.


0

Doug가 언급했듯이 obj.key기능 을 달성하는 데 사용할 수있는 Bunch 패키지가 있습니다 . 실제로 새로운 버전이 있습니다

네오 번치

그것은 neobunchify 기능을 통해 dict를 NeoBunch 객체로 변환하는 훌륭한 기능을 가지고 있습니다. 나는 Mako 템플릿을 많이 사용하고 NeoBunch 객체가 훨씬 읽기 쉽도록 데이터를 전달하므로 Python 프로그램에서 일반적인 dict를 사용하지만 Mako 템플릿에서 점 표기법을 원하면 다음과 같이 사용할 수 있습니다.

from mako.template import Template
from neobunch import neobunchify

mako_template = Template(filename='mako.tmpl', strict_undefined=True)
data = {'tmpl_data': [{'key1': 'value1', 'key2': 'value2'}]}
with open('out.txt', 'w') as out_file:
    out_file.write(mako_template.render(**neobunchify(data)))

그리고 Mako 템플릿은 다음과 같습니다.

% for d in tmpl_data:
Column1     Column2
${d.key1}   ${d.key2}
% endfor

NeoBunch에 링크는 404입니다
DeusXMachina

0

가장 쉬운 방법은 클래스를 정의하여 네임 스페이스라고 부르는 것입니다. dict에서 dict .update () 객체를 사용합니다 . 그러면 dict이 객체로 취급됩니다.

class Namespace(object):
    '''
    helps referencing object in a dictionary as dict.key instead of dict['key']
    '''
    def __init__(self, adict):
        self.__dict__.update(adict)



Person = Namespace({'name': 'ahmed',
                     'age': 30}) #--> added for edge_cls


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