딕트를 "완벽하게" 재정의하려면?
가능한 한 완전한 dict 서브클래스를 만들려면 어떻게 해야 합니까?최종 목표는 키가 소문자인 단순한 dict를 갖는 것입니다.
이 작업을 수행하기 위해 무시할 수 있는 몇 가지 작은 원형이 있는 것처럼 보이지만, 모든 연구와 시도를 통해 볼 때 그렇지 않은 것 같습니다.
/
__setitem__
를 덮어쓰면get
/set
작동하지 않습니다.떻게 하하 을을 을?? ???별적 로구 ?요 요? ???하지 않도록 또, 「」 「」 「」 「」 「」 「」 「」 「」를 ?
__setstate__
그냥 변환 가능한 매핑을 사용해야 할까요? (사용하지 않는 것이 좋을 것 같습니다.)
UserDict
★★★★★★★★★★★★★★★★★」DictMixin
렇면면면면면면?의사들은 별로 계몽적이지 않아요
입니다.get()
동작하지 않습니다.그 밖에도 많은 사소한 문제가 있습니다.
class arbitrary_dict(dict):
"""A dictionary that applies an arbitrary key-altering function
before accessing the keys."""
def __keytransform__(self, key):
return key
# Overridden methods. List from
# https://stackoverflow.com/questions/2390827/how-to-properly-subclass-dict
def __init__(self, *args, **kwargs):
self.update(*args, **kwargs)
# Note: I'm using dict directly, since super(dict, self) doesn't work.
# I'm not sure why, perhaps dict is not a new-style class.
def __getitem__(self, key):
return dict.__getitem__(self, self.__keytransform__(key))
def __setitem__(self, key, value):
return dict.__setitem__(self, self.__keytransform__(key), value)
def __delitem__(self, key):
return dict.__delitem__(self, self.__keytransform__(key))
def __contains__(self, key):
return dict.__contains__(self, self.__keytransform__(key))
class lcdict(arbitrary_dict):
def __keytransform__(self, key):
return str(key).lower()
하다, , 하다, 하다, 하다, 하다, 하다, 쓸 수 요.dict
모듈의 ABC(Abstract Base Class)를 쉽게 사용할 수 있습니다.메서드를 놓쳤는지도 알 수 있으므로 ABC를 종료하는 최소한의 버전은 다음과 같습니다.
from collections.abc import MutableMapping
class TransformedDict(MutableMapping):
"""A dictionary that applies an arbitrary key-altering
function before accessing the keys"""
def __init__(self, *args, **kwargs):
self.store = dict()
self.update(dict(*args, **kwargs)) # use the free update to set keys
def __getitem__(self, key):
return self.store[self._keytransform(key)]
def __setitem__(self, key, value):
self.store[self._keytransform(key)] = value
def __delitem__(self, key):
del self.store[self._keytransform(key)]
def __iter__(self):
return iter(self.store)
def __len__(self):
return len(self.store)
def _keytransform(self, key):
return key
ABC에서 몇 가지 무료 메서드를 얻을 수 있습니다.
class MyTransformedDict(TransformedDict):
def _keytransform(self, key):
return key.lower()
s = MyTransformedDict([('Test', 'test')])
assert s.get('TEST') is s['test'] # free get
assert 'TeSt' in s # free __contains__
# free setdefault, __eq__, and so on
import pickle
# works too since we just use a normal dict
assert pickle.loads(pickle.dumps(s)) == s
같으면 하지 않겠다.dict
(또는 기타 빌트인)을 직접 실행합니다.대부분의 경우 의미가 없습니다.실제로 하는 것은 의 인터페이스를 실장하는 것입니다.그것이 바로 ABC의 목적입니다.
가능한 한 완전한 dict 서브클래스를 만들려면 어떻게 해야 합니까?
최종 목표는 키가 소문자인 단순한 dict를 갖는 것입니다.
★★★을
__getitem__
/__setitem__
get 하지 get/set 하다.떻게게작?작 동??별적 로구 ?요 요? ???하지 않도록 또, 「」 「」 「」 「」 「」 「」 「」 「」를 ?
__setstate__
, repr이 합니까?
__init__
ㅇㅇㅇㅇㅇ로 요?
mutablemapping
안 될 것 요.UserDict
★★★★★★★★★★★★★★★★★」DictMixin
렇면면면면면면?의사들은 별로 계몽적이지 않아요
첫접근법이 될 문제가 이 될 수 있습니다. 아직이 없기 는 그 대안을 .dict
여기서 하겠습니다.
받아들여진 답이 뭐가 문제죠?
이것은 나에게 꽤 간단한 요구인 것 같다.
가능한 한 완전한 dict 서브클래스를 만들려면 어떻게 해야 합니까?최종 목표는 키가 소문자인 단순한 dict를 갖는 것입니다.
은 실제로 가 되지 않습니다.dict
는 불합격입니다
>>> isinstance(MyTransformedDict([('Test', 'test')]), dict)
False
타입 클래스입니다만, 가 「」를 것이 입니다.dict
그을 '수 없기 에 이 - 이 코드로는 '수정'이 안 됩니다.을 사용하다
그 외의 문제점은 다음과 같습니다.
- 인 클래스 메서드(클래스 메서드)도 되어 있지 .
fromkeys
된 답변도 .
__dict__
을 더 - 더 많은 공간을 차지합니다.>>> s.foo = 'bar' >>> s.__dict__ {'foo': 'bar', 'store': {'test': 'test'}}
" " " "dict
상속을 통해 dict 방식을 재사용할 수 있습니다.키가 문자열일 경우 소문자 형식의 dict로 전달되는 인터페이스 레이어를 작성하기만 하면 됩니다.
★★★을
__getitem__
/__setitem__
get 하지 get/set 하다.떻게게작?작 동??별적 로구 ?요 요? ???
,아닌가까운점,가까운점,가까운점,가까운점,가까운점,가까운점,가까운점,가까운점,가까운점.MutableMapping
(합격된 답을 참고하세요) 하지만 그 이상의 일은 아닙니다.
3의 를 감안하여 Python 2」3)을 작성합니다._RaiseKeyError
dict.pop
문자열 키를 소문자로 하는 함수를 만듭니다.
from itertools import chain
try: # Python 2
str_base = basestring
items = 'iteritems'
except NameError: # Python 3
str_base = str, bytes, bytearray
items = 'items'
_RaiseKeyError = object() # singleton for no-default behavior
def ensure_lower(maybe_str):
"""dict keys can be any hashable object - only call lower if str"""
return maybe_str.lower() if isinstance(maybe_str, str_base) else maybe_str
합니다 - using useing at 、 용 now now - now now nowsuper
2Python 하도록 완전한 인수를 지정합니다.
class LowerDict(dict): # dicts take a mapping or iterable as their optional first argument
__slots__ = () # no __dict__ - that would be redundant
@staticmethod # because this doesn't make sense as a global function.
def _process_args(mapping=(), **kwargs):
if hasattr(mapping, items):
mapping = getattr(mapping, items)()
return ((ensure_lower(k), v) for k, v in chain(mapping, getattr(kwargs, items)()))
def __init__(self, mapping=(), **kwargs):
super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
def __getitem__(self, k):
return super(LowerDict, self).__getitem__(ensure_lower(k))
def __setitem__(self, k, v):
return super(LowerDict, self).__setitem__(ensure_lower(k), v)
def __delitem__(self, k):
return super(LowerDict, self).__delitem__(ensure_lower(k))
def get(self, k, default=None):
return super(LowerDict, self).get(ensure_lower(k), default)
def setdefault(self, k, default=None):
return super(LowerDict, self).setdefault(ensure_lower(k), default)
def pop(self, k, v=_RaiseKeyError):
if v is _RaiseKeyError:
return super(LowerDict, self).pop(ensure_lower(k))
return super(LowerDict, self).pop(ensure_lower(k), v)
def update(self, mapping=(), **kwargs):
super(LowerDict, self).update(self._process_args(mapping, **kwargs))
def __contains__(self, k):
return super(LowerDict, self).__contains__(ensure_lower(k))
def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
return type(self)(self)
@classmethod
def fromkeys(cls, keys, v=None):
return super(LowerDict, cls).fromkeys((ensure_lower(k) for k in keys), v)
def __repr__(self):
return '{0}({1})'.format(type(self).__name__, super(LowerDict, self).__repr__())
에는 거의 방식을 을 통해 다음과 같은 방법을 얻을 수 .len
,clear
,items
,keys
,popitem
, , , , 입니다.values
좀 잘 되는 건 예요.이것이 제대로 되기 위해서는 신중한 생각이 필요했지만, 이것이 효과가 있는 것을 보는 것은 사소한 일입니다.
:haskey
되었습니다.) Python 2에서는 삭제되었습니다.
사용법은 다음과 같습니다.
>>> ld = LowerDict(dict(foo='bar'))
>>> ld['FOO']
'bar'
>>> ld['foo']
'bar'
>>> ld.pop('FoO')
'bar'
>>> ld.setdefault('Foo')
>>> ld
{'foo': None}
>>> ld.get('Bar')
>>> ld.setdefault('Bar')
>>> ld
{'bar': None, 'foo': None}
>>> ld.popitem()
('bar', None)
하지 않도록 또, 「」 「」 「」 「」 「」 「」 「」 「」를 ?
__setstate__
산봉우
그리고 dict 서브클래스 피클도 괜찮고
>>> import pickle
>>> pickle.dumps(ld)
b'\x80\x03c__main__\nLowerDict\nq\x00)\x81q\x01X\x03\x00\x00\x00fooq\x02Ns.'
>>> pickle.loads(pickle.dumps(ld))
{'foo': None}
>>> type(pickle.loads(pickle.dumps(ld)))
<class '__main__.LowerDict'>
__repr__
, repr이 합니까?
__init__
정의했습니다update
★★★★★★★★★★★★★★★★★」__init__
당신은 __repr__
★★★★★★★★★★★★★★★★★★:
>>> ld # without __repr__ defined for the class, we get this
{'foo': None}
이렇게 쓰다, , 쓰다, 이렇게 쓰면 좋아요.__repr__
코드 디버깅 기능을 향상시킵니다.인 는 ★★★★★★★★★★★★★★★★★★★★★★★★★★」eval(repr(obj)) == obj
코드로 간단하게 할 수 있는 경우는, 다음의 것을 강하게 추천합니다.
>>> ld = LowerDict({})
>>> eval(repr(ld)) == ld
True
>>> ld = LowerDict(dict(a=1, b=2, c=3))
>>> eval(repr(ld)) == ld
True
동일한 오브젝트를 다시 작성하기 위해 필요한 것은 바로 이것입니다.로그나 백트레이스에 표시되는 것은 다음과 같습니다.
>>> ld
LowerDict({'a': 1, 'c': 3, 'b': 2})
결론
ㅇㅇㅇㅇㅇ로 요?
mutablemapping
안 될 것 요.UserDict
★★★★★★★★★★★★★★★★★」DictMixin
렇면면면면면면?의사들은 별로 계몽적이지 않아요
네, 몇 줄의 코드가 더 있지만 포괄적이기 위한 거예요첫 번째 의향은 수용된 답변을 사용하는 것입니다.문제가 있는 경우 답변을 검토하겠습니다.좀 더 복잡하고 인터페이스를 올바르게 하기 위한 ABC가 없기 때문입니다.
섣부른 최적화로 인해 성능을 찾는 작업이 더욱 복잡해지고 있습니다. MutableMapping
보다 심플합니다.을 사용하다하다그러나 모든 차이를 설명하기 위해 비교와 대조를 해 봅시다.
나는 비슷한 사전을 에 넣으려는 압력이 있었다는 것을 덧붙여야 한다.collections
모듈이 거부되었습니다.대신 이렇게 하는 것이 좋습니다.
my_dict[transform(key)]
훨씬 쉽게 디버깅할 수 있습니다.
비교 및 대조
.MutableMapping
)fromkeys
) 및 (), (11) (11)dict
실행할 __iter__
★★★★★★★★★★★★★★★★★」__len__
저는 '실행'을 해야 합니다.get
,setdefault
,pop
,update
,copy
,__contains__
, , , , 입니다.fromkeys
할 수 에 매우 사소한입니다.- 、 하 、 - 、 - 、 - - 。
MutableMapping
는 Python에서 Python이 구현한 몇 을 구현합니다.dict
C에 기재되어 있는 도구 - 그래서 나는 다음 사항을 예상한다.dict
서브클래스는 경우에 따라서는 퍼포먼스가 향상됩니다.
.__eq__
방식하다고 가정하지만, dict는 대등하다고 합니다.dict
서브클래스는 보다 빠르게 비교됩니다.
요약:.
- '''
MutableMapping
는, 보다 버그가 많은 참조)를해, 실패한다, 「dect」, 「dect」, 「dect」, 「dect」의 「dec」의 「dec」의 「dec」의 「dec」가 됩니다.isinstance(x, dict)
- '''
dict
이 적어, 「」, 「」, 「」, 「」, 「」를 통과합니다.isinstance(x, dict)
그러나 구현이 더 복잡합니다.
어느 것이 더 완벽할까요?그건 완벽에 대한 너의 정의에 달려있어.
두 가지 제안을 모두 시도해 본 후, Python 2.7에 대해 음흉해 보이는 중간 경로를 택했다.3이 더 제정신일 수도 있지만, 나에게는:
class MyDict(MutableMapping):
# ... the few __methods__ that mutablemapping requires
# and then this monstrosity
@property
def __class__(self):
return dict
정말 싫지만 제 욕구에 맞는 것 같아요.
- 로 할 수 있다
**my_dict
- 를 하다
isinstance(my_dict, dict)
- 완전히 제어할 수 있는 행동
- 나는 물려받을 수 .
dict
- 나는 물려받을 수 .
만약 당신이 다른 사람과 구별하고 싶다면, 나는 개인적으로 다음과 같은 것을 사용한다(더 좋은 이름을 추천한다).
def __am_i_me(self):
return True
@classmethod
def __is_it_me(cls, other):
try:
return other.__am_i_me()
except Exception:
return False
자기 에게 전화를 걸기 __am_i_me
python의에 의해(은 python으로 되었습니다)._MyDict__am_i_me
을 이용하다보다 slightly slightly slightly slightly slightly slightly slightly slightly slightly slightly slightly 보다 조금 더 _method
으로나).s(실천적으로나)입니다.
까지는 별로 불만이__class__
덮어쓰다.다른 사람들이 이런 문제에 직면하게 되면 기쁘겠지만, 그 결과를 완전히 이해할 수는 없습니다.그러나 지금까지는 아무런 문제가 없었습니다.이를 통해 많은 장소에서 미드레인지 품질의 코드를 변경할 필요 없이 이행할 수 있었습니다.
사례: https://repl.it/repls/TraumaticToughCockatoo
기본적으로 현재 #2 옵션을 복사하고 추가합니다.print 'method_name'
모든 메서드에 행하고 나서, 다음의 조작을 실시해 주세요.
d = LowerDict() # prints "init", or whatever your print statement said
print '------'
splatted = dict(**d) # note that there are no prints here
다른 시나리오에서도 비슷한 동작을 볼 수 있습니다.를 말하다dict
는 다른 타입을 은 없습니다.**your_dict
다른 모든 방법이 어떻게 수행되든 비어 있게 됩니다.
하면 잘 될 예요.MutableMapping
, , , , , , , 。dict
걷잡을 수 없게 됩니다.
편집: 업데이트로, 이것은 지금까지 거의 2년 동안 단 한 건도 문제 없이 수십만 줄(예: 수백만 줄)의 복잡하고 레거시가 많은 비단뱀에서 실행되고 있습니다.그래서 저는 매우 만족합니다. :)
제2장: 제2장, 제2장. @classmethod __class__
does does 。isinstance
- '확인' -@property __class__
실행: https://repl.it/repls/UnitedScientificSequence
요구 사항이 좀 더 엄격했습니다.
- 케이스 정보를 보관해야 했습니다(문자열은 사용자에게 표시되는 파일의 경로이지만 윈도 앱이므로 내부적으로는 모든 조작이 대소문자를 구분하지 않아야 합니다).
- 가능한 한 작은 키가 필요했습니다(370개 중 110MB를 잘라내는 메모리 성능에 차이가 있었습니다).즉, 키의 소문자 버전을 캐싱할 수 없습니다.
- 가능한 한 빠른 데이터 구조를 생성해야 했습니다(이번에도 성능과 속도가 달라졌습니다).나는 빌트인(built-in)으로 가야 했다.
처음에는 대소문자를 구분하지 않는 유니코드 서브클래스를 투박한 패스 클래스로 대체하려고 했습니다만,
- 올바른 결과를 얻기 어렵다는 것이 증명되었습니다. 참조: python의 대소문자를 구분하지 않는 문자열 클래스
- 키지저분하게 , 에러에 빠지기 쉬운 구조(하고, , dict instance가 있는지는 불명확합니다.
some_dict[CIstr(path)]
그래서 나는 마침내 그 무감각한 받아쓰기를 해야만 했다.@AaronHall의 코드 덕분에 10배 쉬워졌습니다.
class CIstr(unicode):
"""See https://stackoverflow.com/a/43122305/281545, especially for inlines"""
__slots__ = () # does make a difference in memory performance
#--Hash/Compare
def __hash__(self):
return hash(self.lower())
def __eq__(self, other):
if isinstance(other, CIstr):
return self.lower() == other.lower()
return NotImplemented
def __ne__(self, other):
if isinstance(other, CIstr):
return self.lower() != other.lower()
return NotImplemented
def __lt__(self, other):
if isinstance(other, CIstr):
return self.lower() < other.lower()
return NotImplemented
def __ge__(self, other):
if isinstance(other, CIstr):
return self.lower() >= other.lower()
return NotImplemented
def __gt__(self, other):
if isinstance(other, CIstr):
return self.lower() > other.lower()
return NotImplemented
def __le__(self, other):
if isinstance(other, CIstr):
return self.lower() <= other.lower()
return NotImplemented
#--repr
def __repr__(self):
return '{0}({1})'.format(type(self).__name__,
super(CIstr, self).__repr__())
def _ci_str(maybe_str):
"""dict keys can be any hashable object - only call CIstr if str"""
return CIstr(maybe_str) if isinstance(maybe_str, basestring) else maybe_str
class LowerDict(dict):
"""Dictionary that transforms its keys to CIstr instances.
Adapted from: https://stackoverflow.com/a/39375731/281545
"""
__slots__ = () # no __dict__ - that would be redundant
@staticmethod # because this doesn't make sense as a global function.
def _process_args(mapping=(), **kwargs):
if hasattr(mapping, 'iteritems'):
mapping = getattr(mapping, 'iteritems')()
return ((_ci_str(k), v) for k, v in
chain(mapping, getattr(kwargs, 'iteritems')()))
def __init__(self, mapping=(), **kwargs):
# dicts take a mapping or iterable as their optional first argument
super(LowerDict, self).__init__(self._process_args(mapping, **kwargs))
def __getitem__(self, k):
return super(LowerDict, self).__getitem__(_ci_str(k))
def __setitem__(self, k, v):
return super(LowerDict, self).__setitem__(_ci_str(k), v)
def __delitem__(self, k):
return super(LowerDict, self).__delitem__(_ci_str(k))
def copy(self): # don't delegate w/ super - dict.copy() -> dict :(
return type(self)(self)
def get(self, k, default=None):
return super(LowerDict, self).get(_ci_str(k), default)
def setdefault(self, k, default=None):
return super(LowerDict, self).setdefault(_ci_str(k), default)
__no_default = object()
def pop(self, k, v=__no_default):
if v is LowerDict.__no_default:
# super will raise KeyError if no default and key does not exist
return super(LowerDict, self).pop(_ci_str(k))
return super(LowerDict, self).pop(_ci_str(k), v)
def update(self, mapping=(), **kwargs):
super(LowerDict, self).update(self._process_args(mapping, **kwargs))
def __contains__(self, k):
return super(LowerDict, self).__contains__(_ci_str(k))
@classmethod
def fromkeys(cls, keys, v=None):
return super(LowerDict, cls).fromkeys((_ci_str(k) for k in keys), v)
def __repr__(self):
return '{0}({1})'.format(type(self).__name__,
super(LowerDict, self).__repr__())
암묵적인 것과 명시적인 것은 여전히 문제가 있지만, 일단 문제가 해결되면 속성/변수의 이름을 ci로 바꾸는 것(그리고 ci가 대소문자를 구분하지 않는다는 것을 설명하는 큰 문서 코멘트)이 완벽한 해결책이라고 생각합니다.코드의 독자는 대소문자를 구분하지 않는 기본 데이터 구조를 다루고 있다는 것을 충분히 인식해야 합니다.이렇게 하면 버그를 재현하기 어려운 문제가 해결되기를 바라며, 대소문자를 구분하는 것으로 요약할 수 있습니다.
코멘트/수정 환영합니다:)
당신이 해야 할 일은
class BatchCollection(dict):
def __init__(self, *args, **kwargs):
dict.__init__(*args, **kwargs)
또는
class BatchCollection(dict):
def __init__(self, inpt={}):
super(BatchCollection, self).__init__(inpt)
개인 용도로 사용하는 샘플
### EXAMPLE
class BatchCollection(dict):
def __init__(self, inpt={}):
dict.__init__(*args, **kwargs)
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에서만 테스트 완료
collections.UserDict
는 커스텀이 인 경우가 많습니다.dict
.
다른 답변에서 보듯이 덮어쓰기가 매우 까다롭습니다.dict
「」는 「」, 「」는 「」입니다.UserDict
을 사용하다원래 질문에 답하려면 낮은 키를 사용하여 받아쓰기를 받을 수 있습니다.
import collections
class LowercaseDict(collections.UserDict):
def __getitem__(self, key):
return super().__getitem__(key.lower())
def __setitem__(self, key, value):
return super().__setitem__(key.lower(), value)
def __delitem__(self, key):
return super().__delitem__(key.lower())
# Unfortunately, __contains__ is required currently due to
# https://github.com/python/cpython/issues/91784
def __contains__(self, key):
return key.lower() in self.data
d = LowercaseDict(MY_KEY=0) # Keys normalized in .__init__
d.update({'OTHER_KEY': 1}) # Keys normalized in .update
d['Hello'] = d['other_KEY']
assert 'HELLO' in d
print(d) # All keys normalized {'my_key': 0, 'other_key': 1, 'hello': 1}
리고 to to to와는 collections.abc.MutableMapping
필요없음__iter__
,__len__
,__init__
,... 서브클래스UserDict
훨씬 쉬워요.
, <고객명>님UserDict
는 입니다.MutableMapping
이에요.dict
: (으) :
assert not isinstance(collections.UserDict(), dict)
assert isinstance(collections.UserDict(), collections.abc.MutableMapping)
언급URL : https://stackoverflow.com/questions/3387691/how-to-perfectly-override-a-dict
'source' 카테고리의 다른 글
mysql 쿼리에서 << 고객명 >>님의 의미는 무엇입니까? (0) | 2023.02.01 |
---|---|
Python 개체의 메서드 찾기 (0) | 2023.02.01 |
mysqldb를 통해 팬더 데이터 프레임을 데이터베이스에 삽입하려면 어떻게 해야 합니까? (0) | 2023.02.01 |
범위를 포함할 때 인덱스에서 먼저 높은 카디널리티 열을 선택하십시오. (0) | 2023.02.01 |
큰 파일을 한 줄씩 읽는 방법 (0) | 2023.01.22 |