source

딕트를 "완벽하게" 재정의하려면?

gigabyte 2023. 2. 1. 21:50
반응형

딕트를 "완벽하게" 재정의하려면?

가능한 한 완전한 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)을 작성합니다._RaiseKeyErrordict.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 nowsuper2Python 하도록 완전한 인수를 지정합니다.

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이 구현한 몇 을 구현합니다.dictC에 기재되어 있는 도구 - 그래서 나는 다음 사항을 예상한다.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
    • 서 from from from from from from from from 에서 상속받은 dict, 이것은 코드를 바이패스합니다.시험해 보세요.
    • 이것은 python 코드에서 꽤 흔한 일이기 때문에 항상 2번받아들일 수 없게 만든다.
  • 를 하다isinstance(my_dict, dict)
    • Mutable Mapping을 단독으로 배제하기 때문에 #1로는 불충분합니다.
    • 이게 필요없다면 1번 추천드립니다.심플하고 예측가능합니다.
  • 완전히 제어할 수 있는 행동
    • 나는 물려받을 수 .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_mepython의에 의해(은 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

반응형