복붙노트

[PYTHON] 파이썬 dict : get 대 setdefault

PYTHON

파이썬 dict : get 대 setdefault

다음 두 표현은 나에게 동등한 것처럼 보입니다. 어느 것이 더 낫습니까?

data = [('a', 1), ('b', 1), ('b', 2)]

d1 = {}
d2 = {}

for key, val in data:
    # variant 1)
    d1[key] = d1.get(key, []) + [val]
    # variant 2)
    d2.setdefault(key, []).append(val)

결과는 동일하지만 어떤 버전이 더 좋거나 훨씬 더 파이썬적인가요?

개인적으로 나는 버전 2를 이해하기가 더 힘들다는 것을 알았고, 나는 setdefault를 이해하기가 매우 까다 롭다. 내가 올바르게 이해하면 사전에 "key"의 값을 찾는다. 사용할 수 없다면 "[]"를 입력하고, 값이나 "[]"에 대한 참조를 반환하고 "val"에 그 값을 덧붙인다. 참고. 확실히 부드럽지만 적어도 (적어도 나에게는) 직관적이지 않습니다.

내 마음에, 버전 1 이해하기 쉽습니다 (가능하다면, "키"에 대한 값을 얻지 못하면 "[]"을 얻고, [val]로 구성된 목록에 가입하고 그 결과를 "키" ). 그러나 이해하기가 더 직관적이지만,이 목록이 생성되면서이 버전이 덜 효과적 일 수 있습니다. 또 다른 단점은 "d1"이 오류가 발생하기 쉬운 표현에서 두 번 발생한다는 것입니다. 아마 get을 사용하는 것이 더 나은 구현이지만, 현재는 그것을 벗어날 수 있습니다.

내 생각에 버전 2는 경험이 부족한 사람들을 파악하기가 더 어렵지만 더 빠르고 더 바람직합니다. 의견?

해결법

  1. ==============================

    1.당신의 두 예제는 똑같은 일을하지만 get과 setdefault가하는 것을 의미하지는 않습니다.

    당신의 두 예제는 똑같은 일을하지만 get과 setdefault가하는 것을 의미하지는 않습니다.

    둘의 차이점은 기본적으로 d [key]가 매번 목록을 가리 키도록 수동으로 설정하는 것과는 달리 setdefault는 설정되지 않은 경우에만 d [key]를 목록에 자동으로 설정합니다.

    가능한 두 가지 방법을 비슷하게 만들어서 실행했습니다.

    from timeit import timeit
    
    print timeit("c = d.get(0, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
    print timeit("c = d.get(1, []); c.extend([1]); d[0] = c", "d = {1: []}", number = 1000000)
    print timeit("d.setdefault(0, []).extend([1])", "d = {1: []}", number = 1000000)
    print timeit("d.setdefault(1, []).extend([1])", "d = {1: []}", number = 1000000)
    

    그리고있어.

    0.794723378711
    0.811882272256
    0.724429205999
    0.722129751973
    

    따라서 setdefault는이 목적을 달성하는 것보다 약 10 % 빠릅니다.

    get 메소드를 사용하면 setdefault를 사용하여 할 수있는 것보다 적은 작업을 수행 할 수 있습니다. 키를 설정하지 않으려는 경우에도 키가 존재하지 않을 때 KeyError가 발생하지 않도록 할 수 있습니다 (키 이벤트가 자주 발생하는 경우).

    'setdefault'dict 메쏘드의 유스 케이스를 보시오. dict.get () 메쏘드는 두 메쏘드에 대한 더 많은 정보를위한 포인터를 반환합니다.

    setdefault에 대한 스레드는 대부분의 경우 defaultdict를 사용하려고한다고 결론 내립니다. get에 관한 스레드는 느린 것으로 결론을 내리고 종종 double lookup, defaultdict 사용 또는 오류 처리 (사전 크기 및 사용 사례에 따라 다름)를 사용하는 것이 좋습니다.

  2. ==============================

    2.Agf로부터받은 대답은 좋아하는 것과 비슷하지 않습니다. 후:

    Agf로부터받은 대답은 좋아하는 것과 비슷하지 않습니다. 후:

    print timeit("d[0] = d.get(0, []) + [1]", "d = {1: []}", number = 10000)
    

    d [0]은 10,000 개의 항목이있는 목록을 포함하는 반면,

    print timeit("d.setdefault(0, []) + [1]", "d = {1: []}", number = 10000)
    

    d [0]은 단순히 []입니다. 즉, d.setdefault 버전은 d에 저장된 목록을 수정하지 않습니다. 코드는 실제로 다음과 같아야합니다.

    print timeit("d.setdefault(0, []).append(1)", "d = {1: []}", number = 10000)
    

    실제로 잘못된 setdefault 예제보다 빠릅니다.

    여기서 차이점은 연결을 사용하여 추가 할 때마다 전체 목록이 복사 될 때마다 (그리고 측정 할 수있는 요소가 10,000 개가되면 일단 업데이트됩니다.) append를 사용하면 목록 업데이트가 O (1)로 상각됩니다. 즉 효과적으로 일정 시간입니다.

    마지막으로, 원래 질문에서 고려되지 않은 두 가지 옵션이 있습니다. 즉, defaultdict 또는 단순히 키가 들어 있는지 여부를 확인하기 위해 사전을 테스트하는 것입니다.

    따라서, d3을 가정하면, d4 = defaultdict (list), {}

    # variant 1 (0.39)
    d1[key] = d1.get(key, []) + [val]
    # variant 2 (0.003)
    d2.setdefault(key, []).append(val)
    # variant 3 (0.0017)
    d3[key].append(val)
    # variant 4 (0.002)
    if key in d4:
        d4[key].append(val)
    else:
        d4[key] = [val]
    

    변형 1은 매번 목록을 복사하기 때문에 가장 느립니다. 변형 2는 두 번째로 느립니다. 변형 3은 가장 빠르지 만 2.5보다 오래된 Python이 필요하고 변형 4가 변형 3보다 약간 느리면 작동하지 않습니다. .

    가능한 경우 변형 3을 사용하고, 변형 4는 defaultdict가 정확한 적합이 아닌 가끔 장소에 대한 옵션으로 사용한다고 말합니다. 원본 변형 모두를 피하십시오.

  3. ==============================

    3.collections 모듈에서 defaultdict를 보길 원할 것입니다. 다음은 예제와 동일합니다.

    collections 모듈에서 defaultdict를 보길 원할 것입니다. 다음은 예제와 동일합니다.

    from collections import defaultdict
    
    data = [('a', 1), ('b', 1), ('b', 2)]
    
    d = defaultdict(list)
    
    for k, v in data:
        d[k].append(v)
    

    여기에 더 많은 것이 있습니다.

  4. ==============================

    4.1. 좋은 예를 들면 다음과 같습니다 : http://code.activestate.com/recipes/66516-add-an-entry-to-a-dictionary-unless-the-entry-is-a/

    1. 좋은 예를 들면 다음과 같습니다 : http://code.activestate.com/recipes/66516-add-an-entry-to-a-dictionary-unless-the-entry-is-a/

    2. 더 많은 설명 : http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html

    dict.setdefault ()는 get 또는 set & get과 동일합니다. 또는 필요한 경우 설정하십시오. 사전 키의 계산이나 입력이 오래 걸리는 경우 특히 효율적입니다.

    dict.setdefault ()의 유일한 문제점은 필요 여부에 상관없이 항상 기본값이 평가된다는 것입니다. 기본값은 계산하는 데 비용이 많이 드는 경우에만 중요합니다. 이 경우 defaultdict를 사용하십시오.

    3. 마지막으로 차이가있는 공식 문서가 http://docs.python.org/2/library/stdtypes.html로 강조 표시되었습니다.

    setdefault (key [, default])   키가 사전에 있으면 해당 값을 반환합니다. 그렇지 않은 경우 key를 default 값으로 입력하고 default를 리턴하십시오. 기본값은 None입니다.

  5. ==============================

    5.

    In [1]: person_dict = {}
    
    In [2]: person_dict['liqi'] = 'LiQi'
    
    In [3]: person_dict.setdefault('liqi', 'Liqi')
    Out[3]: 'LiQi'
    
    In [4]: person_dict.setdefault('Kim', 'kim')
    Out[4]: 'kim'
    
    In [5]: person_dict
    Out[5]: {'Kim': 'kim', 'liqi': 'LiQi'}
    
    In [8]: person_dict.get('Dim', '')
    Out[8]: ''
    
    In [5]: person_dict
    Out[5]: {'Kim': 'kim', 'liqi': 'LiQi'}
    
  6. ==============================

    6.dict.get의 논리는 다음과 같습니다.

    dict.get의 논리는 다음과 같습니다.

    if key in a_dict:
        value = a_dict[key] 
    else: 
        value = default_value
    

    예를 들면 다음과 같습니다.

    In [72]: a_dict = {'mapping':['dict', 'OrderedDict'], 'array':['list', 'tuple']}
    In [73]: a_dict.get('string', ['str', 'bytes'])
    Out[73]: ['str', 'bytes']
    In [74]: a_dict.get('array', ['str', 'byets'])
    Out[74]: ['list', 'tuple']
    

    setdefault의 메커니즘은 다음과 같습니다.

        levels = ['master', 'manager', 'salesman', 'accountant', 'assistant']
        #group them by the leading letter
        group_by_leading_letter = {}
        # the logic expressed by obvious if condition
        for level in levels:
            leading_letter = level[0]
            if leading_letter not in group_by_leading_letter:
                group_by_leading_letter[leading_letter] = [level]
            else:
                group_by_leading_letter[leading_letter].append(word)
        In [80]: group_by_leading_letter
        Out[80]: {'a': ['accountant', 'assistant'], 'm': ['master', 'manager'], 's': ['salesman']}
    

    setdefault dict 메소드는 정확하게이 목적을위한 것입니다. 앞의 for 루프는 다음과 같이 다시 작성할 수 있습니다.

    In [87]: for level in levels:
        ...:     leading = level[0]
        ...:     group_by_leading_letter.setdefault(leading,[]).append(level)
    Out[80]: {'a': ['accountant', 'assistant'], 'm': ['master', 'manager'], 's': ['salesman']}
    

    매우 간단합니다. null이 아닌 목록에 요소를 추가하거나 null 목록에 요소를 추가하는 것을 의미합니다.

    defaultdict는 더 쉽게 만듭니다. 하나를 만들려면 dict의 각 슬롯에 대한 기본값을 생성하는 유형 또는 함수를 전달합니다.

    from collections import defualtdict
    group_by_leading_letter = defaultdict(list)
    for level in levels:
        group_by_leading_letter[level[0]].append(level)
    
  7. ==============================

    7.아직이 두 용어를 이해하는데 어려움을 겪고있는 사람들을 위해 get ()과 setdefault () 메소드의 기본적인 차이점을 설명하겠습니다.

    아직이 두 용어를 이해하는데 어려움을 겪고있는 사람들을 위해 get ()과 setdefault () 메소드의 기본적인 차이점을 설명하겠습니다.

    시나리오 -1

    root = {}
    root.setdefault('A', [])
    print(root)
    

    시나리오 -2

    root = {}
    root.get('A', [])
    print(root)
    

    시나리오 -1에서는 시나리오 2에서 { 'A': []}이 출력됩니다 {}

    따라서 setdefault ()는 dict에 부재 키를 설정하지만 get ()은 기본값 만 제공하지만 사전은 수정하지 않습니다.

    이제 이것이 유용 할 곳으로 가자. 당신이 값이리스트 인 dict 내의 엘리먼트를 검색하고 있다고 가정한다면 그리스트를 수정하고자한다면 그리스트를 가지고 새로운 키를 생성하라.

    setdefault () 사용

    def fn1(dic, key, lst):
        dic.setdefault(key, []).extend(lst)
    

    get ()를 사용하여

    def fn2(dic, key, lst):
        dic[key] = dic.get(key, []) + (lst) #Explicit assigning happening here
    

    이제 타이밍을 검사 할 수 있습니다.

    dic = {}
    %%timeit -n 10000 -r 4
    fn1(dic, 'A', [1,2,3])
    

    288 ns 걸렸습니다.

    dic = {}
    %%timeit -n 10000 -r 4
    fn2(dic, 'A', [1,2,3])
    

    128 초 남았습니다.

    따라서이 두 가지 접근 방식에는 매우 큰 타이밍 차이가 있습니다.

  8. from https://stackoverflow.com/questions/7423428/python-dict-get-vs-setdefault by cc-by-sa and MIT license