속성과 같은 dict 키에 액세스합니까?
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 키에 액세스 할 때의주의 사항과 함정은 무엇입니까?
이를 수행하는 가장 좋은 방법은 다음과 같습니다.
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!"
배열 표기법을 사용하는 경우 모든 유효한 문자열 문자를 키의 일부로 사용할 수 있습니다. 예를 들어obj['!#$%^&*()_']
에서 이 다른 SO 질문 기존의 코드를 단순화 좋은 구현 예제가있다. 어때요?
class AttributeDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
훨씬 더 간결은에 들어가기 추가 cruft에 대한 공간을 확보하지 않으며 __getattr__
및 __setattr__
향후 기능.
내가 묻는 질문에 대답
파이썬은 왜 그것을 즉시 제공하지 않습니까?
파이썬 의 선과 관련 이 있다고 생각합니다 . "한 가지 확실한 방법이 있어야합니다." 이 사전에서 액세스 값이 분명한 방법을 만들 것입니다 : 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 파일에서 prefs를 읽어야 할 때) dicts를 자주 사용 합니다. 다른 경우에는 미학적 이유로 동적 클래스를 인스턴스화하고 그것에 대한 일부 특성을 포기하려는 유혹을받는 경우 가독성을 높이기 위해 일관성에 대한 dict을 계속 사용합니다.
OP가 오랫동안 만족스럽게 해결 한 것으로 확신하지만 여전히이 기능을 원한다면 pypi에서 패키지 중 하나를 다운로드하여 제안합니다.
무리 는 내가 더 잘 알고있는 것입니다. 의 하위 클래스dict
이므로 모든 기능이 있습니다.- AttrDict 도 꽤 좋은 것처럼 보이지만, 나는 그것에 익숙하지 않으며 Bunch 만큼 세부적으로 소스를 보지 않았습니다.
- 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}
주의 사항 : 어떤 이유로 이런 클래스는 멀티 프로세싱 패키지를 깨뜨리는 것 같습니다. 나는이 SO를 찾기 전에 잠시 동안이 버그로 고생했습니다 : python multiprocessing에서 예외 찾기
__eq__
또는 같은 방법 인 키를 원한다면 어떻게해야 __getattr__
합니까?
그리고 당신은 편지로 시작하지 않은 항목을 가질 수 없으므로 0343853
키로 사용 하는 것은 불가능합니다.
문자열을 사용하지 않으려면 어떻게해야합니까?
표준 라이브러리에서 편리한 컨테이너 클래스를 가져올 수 있습니다.
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__
튜플은 dict 키를 사용할 수 있습니다. 구문에서 튜플에 어떻게 액세스 하시겠습니까?
또한 namedtuple 은 속성 액세스를 통해 값을 제공 할 수있는 편리한 구조입니다.
일반적으로 작동하지 않습니다. 유효한 모든 dict 키가 주소 지정 가능한 속성 ( "키")을 만드는 것은 아닙니다. 따라서 조심해야합니다.
파이썬 객체는 모두 기본적으로 사전입니다. 그래서 많은 성능이나 다른 형벌이 있는지 의심합니다.
내가 그들을 모두 지배하기 위해 쓴 작은 파이썬 클래스 인 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'>
다음은 내장을 사용하는 불변 레코드의 간단한 예입니다 collections.namedtuple
.
def record(name, d):
return namedtuple(name, d.keys())(**d)
사용 예 :
rec = record('Model', {
'train_op': train_op,
'loss': loss,
})
print rec.loss(..)
이것은 원래의 질문을 다루지는 않지만이 기능을 제공하는 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'
setattr () 및 getattr ()이 이미 존재 하므로 직접 작성할 필요가 없습니다 .
클래스 객체의 장점은 클래스 정의와 상속에서 작용할 것입니다.
이 스레드의 입력을 기반으로 이것을 만들었습니다. 그래도 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
분명히이 라이브러리 ( https://pypi.python.org/pypi/attrdict) 가 있습니다.이 라이브러리는 이 정확한 기능과 재귀 병합 및 json 로딩을 구현합니다. 살펴볼 가치가 있습니다.
답에 다양성을 더하기 위해 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
당신이 필요로하는 모두는 얻을 것입니다 setattr
및 getattr
더 - 방법 getattr
실제 속성을 검사에게 DICT 키를 확인하고 움직임을. 은 setstaet
산세 / unpickling "송이"에 대한 수정 사항에 대한 수정이다 - inerested 체크하면 https://github.com/scikit-learn/scikit-learn/issues/6196
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
방금 만든이 클래스를 사용하여 수행 할 수 있습니다. 이 클래스를 사용하면 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']
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
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
해결책은 다음과 같습니다.
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__()
이런 식으로 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
함수 인수의 이름이 예약 키워드와 충돌하는 경우 일반적으로 단일 후행 밑줄을 추가하는 것이 좋습니다 ...
변수 이름은 함수 이름과 동일한 규칙을 따릅니다.
d.Firstname
d.Country
가독성을 높이기 위해 필요에 따라 밑줄로 구분 된 단어가있는 소문자 기능 명명 규칙을 사용하십시오.
때로는 pandas와 같은 라이브러리 에서 이러한 문제가 발생 하여 이름으로 DataFrame 열을 점으로 액세스 할 수 있습니다. 명명 제한을 해결하는 기본 메커니즘은 배열 표기법 (괄호 안의 문자열)입니다.
이러한 제약 조건이 사용 사례에 적용되지 않으면 점선 액세스 데이터 구조에 대한 몇 가지 옵션이 있습니다 .
dict_to_obj https://pypi.org/project/dict-to-obj/ 를 사용할 수 있습니다. 요청한 내용을 정확하게 수행합니다.
From dict_to_obj import DictToObj
a = {
'foo': True
}
b = DictToObj(a)
b.foo
True
이것은 '좋은'답변은 아니지만 이것이 멋지다고 생각했습니다 (현재 형태의 중첩 된 dicts를 처리하지는 않습니다). dict을 함수로 간단히 감싸십시오.
def make_funcdict(d={}, **kwargs)
def funcdict(d={}, **kwargs):
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'}
그리고 거기에 있습니다. 이 방법의 이점과 단점을 제안하는 사람이 있으면 기쁠 것입니다.
이것이 내가 사용하는 것입니다
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)
참고 URL : https://stackoverflow.com/questions/4984647/accessing-dict-keys-like-an-attribute
'Programing' 카테고리의 다른 글
NSUserDefaults에 사용자 정의 객체를 저장하는 방법 (0) | 2020.03.29 |
---|---|
jsdoc에서“객체”인수를 설명하는 방법? (0) | 2020.03.29 |
JPA에 @Transient 주석이있는 이유는 무엇입니까? (0) | 2020.03.29 |
두 개의 절대 경로 (또는 URL)에서 Java로 상대 경로를 구성하는 방법은 무엇입니까? (0) | 2020.03.29 |
OS X에서 Python의 기본 버전을 3.x로 설정하는 방법은 무엇입니까? (0) | 2020.03.29 |