파이썬 dict.update ()가 객체를 반환하지 않는 이유는 무엇입니까?


139

나는 노력하고있다 :

award_dict = {
    "url" : "http://facebook.com",
    "imageurl" : "http://farm4.static.flickr.com/3431/3939267074_feb9eb19b1_o.png",
    "count" : 1,
}

def award(name, count, points, desc_string, my_size, parent) :
    if my_size > count :
        a = {
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }
        a.update(award_dict)
        return self.add_award(a, siteAlias, alias).award

그러나 기능이 정말 번거 롭다면 오히려했을 것입니다.

        return self.add_award({
            "name" : name,
            "description" : desc_string % count,
            "points" : points,
            "parent_award" : parent,
        }.update(award_dict), siteAlias, alias).award

체인을 연결할 수 있도록 업데이트가 객체를 반환하지 않는 이유는 무엇입니까?

JQuery는이를 수행하여 체인을 수행합니다. 파이썬에서는 왜 허용되지 않습니까?


14
* TL; DRnewdict = dict(dict001, **dict002)
dreftymac

2
@dreftymac, 이해가되지 않습니다.
alancalvitti

@alancalvitti 그렇습니다. 실제로 지적해야 할 하나의 유효한 경고입니다.
dreftymac

답변:


219

파이썬은 주로 명령 쿼리 분리 의 실용적으로 색을 입힌 풍미를 구현합니다 : 뮤 테이터는 None(;-와 같은 실용적으로 예외가 발생 pop하여) 접근 자와 혼동 할 수 없으므로 반환합니다 ( 같은 맥락에서 할당은 표현이 아닙니다. -표현 분리 등이 있습니다).

즉 당신이 정말로 원하는 경우 병합 것들까지 많은 방법이 예는,이없는 것은 아닙니다 dict(a, **award_dict)많이 원하는 것으로 보인다 것과 같은 새로운 딕셔너리하게 .update왜 당신이 정말로 느끼는 경우가 중요하다는 것을 사용하지 - 반환 ?

편집 : btw, 특정 경우에 a따라 길을 따라 만들 필요가 없습니다 .

dict(name=name, description=desc % count, points=points, parent_award=parent,
     **award_dict)

정확히 같은 의미로 하나의 딕셔너리를 생성하여 a.update(award_dict)(충돌의 경우, 항목 사실을 포함하여 award_dict명시 적으로 제공하고 그 재정의, 즉, 다른 의미를 얻기 위해이 같은 충돌을 "승리"명시 적 항목을 가지고, 키워드 위치 앞에award_dict 유일한 위치 인수 로 전달 하고 형식 등의 구부러짐 등).**dict(award_dict, name=name


글쎄, 그것은 내가 만든 후에 또 다른 사전을 만들 것입니다. dict을 만들고 다른 많은 값을 추가 한 다음 함수에 제공하고 싶었습니다.
Paul Tarjan

@Paul, 그리고 이것이 바로 당신이하고있는 일입니다. 당신이 원했던 중첩 된 방법보다 훨씬 더 읽기 쉬운 두 개의 문장으로 "정말 번거 롭습니다". 내 대답을 편집 만들지 않도록하는 방법을 보여 a, BTW, 모두
알렉스 마르 텔리

1
독창적 인 솔루션은 강력하지 않습니다. award_dict에 이미 지정된 키가 포함 된 경우 반복되는 키워드 인수에 대해 SyntaxError가 발생합니다. jamylak의 솔루션 dict (itertools.chain (d1.iteritems (), .. d <n> .iteritems ()))는 사전에 중복 키가있는 경우에만 작동 할뿐만 아니라 나중에 여러 사전을 여러 사전을 dicts와 병합 할 수 있습니다. 최종 값에 우선하는 체인.
Matt

2
또한 award_dict의 키가 문자열이 아닌 경우 통역사는TypeError
kunl

3
dict(old_dict, old_key=new_value)키워드에 여러 값을 던지지 않고 새로운 dict를 반환합니다.
Charmy

35

파이썬의 API는 관례에 따라 프로 시저와 함수를 구분합니다. 함수는 매개 변수에서 새 값을 계산합니다 (대상 객체 포함). 프로 시저는 객체를 수정하고 아무것도 반환하지 않습니다 (즉, None을 반환합니다). 따라서 절차에는 부작용이 있지만 기능에는 없습니다. 업데이트는 절차이므로 값을 반환하지 않습니다.

그렇게하는 동기는 그렇지 않으면 바람직하지 않은 부작용이 생길 수 있다는 것입니다. 치다

bar = foo.reverse()

reverse (제자리에서 목록을 뒤집는)가 목록을 반환 할 경우 사용자는 reverse가 bar에 할당 된 새 목록을 반환한다고 생각할 수 있으며 foo도 수정된다는 사실을 절대 알 수 없습니다. 리버스 리턴 없음을 설정하면 바가 반전의 결과가 아니라는 것을 즉시 인식하고 리버스의 효과가 더 가깝게 보입니다.


1
감사합니다. 역순으로도 옵션을 제공하지 않는 이유는 무엇입니까? 공연? 하고는 reverse(foo)이상한 느낌.
Paul Tarjan

옵션을 추가하는 것은 부적절합니다. 매개 변수에 따라 메소드의 특성이 변경됩니다. 그러나 메소드는 실제로 고정 된 리턴 유형을 가져야합니다 (불행히도이 규칙이 깨지는 경우가 있습니다). 되 돌린 사본을 쉽게 만들 수 있습니다.를 사용하여 사본을 bar=foo[:]만든 다음 사본을 되 돌리십시오.
Martin v. Löwis

3
나는 그 이유가 명백하다고 생각합니다. 에서 수정되지 않았다고 bar = foo.reverse()생각할 수 foo있습니다. 혼동을 피하기 위해 foo.reverse()bar = reversed(foo).
Roberto Bonvallet

매개 변수를 기반으로 매개 변수의 특성을 변경하면 무엇이 문제입니까?
Julien


15
>>> dict_merge = lambda a,b: a.update(b) or a
>>> dict_merge({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

병합 된 dict를 반환 할뿐만 아니라 첫 번째 매개 변수를 그 자리에서 수정합니다. 따라서 dict_merge (a, b)는 a를 수정합니다.

또는 물론 인라인으로 모두 할 수 있습니다.

>>> (lambda a,b: a.update(b) or a)({'a':1, 'b':3},{'c':5})
{'a': 1, 'c': 5, 'b': 3}

10
-1 lambda처럼 사용되지 않으며, 대신에 통상의 기능을 사용 def하는 대신
jamylak

8
람다도 필요하지 않습니다.a.update(b) or a
Pycz

10

댓글에 대한 평판이 충분하지 않습니다.

@ beardc 이것은 CPython이 아닌 것 같습니다. PyPy에서 "TypeError : 키워드는 문자열이어야합니다"

**kwargs병합 할 사전에 string 유형의 키만 있기 때문에 솔루션 만 작동 합니다 .

>>> dict({1:2}, **{3:4})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

vs

>>> dict({1:2}, **{'3':4})
{1: 2, '3': 4}

5

허용되지 않는 것이 아니라 dicts그 방식으로 구현되지 않았습니다.

Django의 ORM을 보면 체인을 광범위하게 사용합니다. 권장하지 않으며 업데이트를 수행하기 위해 상속 dict하고 재정의 updatereturn self수도 있습니다.

class myDict(dict):
    def update(self, *args):
        dict.update(self, *args)
        return self

고맙습니다, 이것은 dict를 패치 할 수 있습니다. dict ()가 왜이 기능 자체를 허용하지 않는지 알고 싶었습니다 (시연하기 쉽기 때문에). 장고 패치는 이런 식으로 지시합니까?
Paul Tarjan

2

내가 얻을 수있는 제안 된 솔루션에 가깝습니다.

from collections import ChainMap

return self.add_award(ChainMap(award_dict, {
    "name" : name,
    "description" : desc_string % count,
    "points" : points,
    "parent_award" : parent,
}), siteAlias, alias).award

1

파티에 늦게 온 사람들을 위해, 나는 타이밍을 모아서 (Py 3.7) .update()입력을 보존 할 때 기반 방법이 약간 (~ 5 %) 더 빨라지고 현장 업데이트 만하면 눈에 띄게 (~ 30 %) 빠르다는 것을 보여주었습니다 .

평소와 같이 모든 벤치 마크는 소금 한 덩어리로 가져와야합니다.

def join2(dict1, dict2, inplace=False):
    result = dict1 if inplace else dict1.copy()
    result.update(dict2)
    return result


def join(*items):
    iter_items = iter(items)
    result = next(iter_items).copy()
    for item in iter_items:
        result.update(item)
    return result


def update_or(dict1, dict2):
    return dict1.update(dict2) or dict1


d1 = {i: str(i) for i in range(1000000)}
d2 = {str(i): i for i in range(1000000)}

%timeit join2(d1, d2)
# 258 ms ± 1.47 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit join(d1, d2)
# 262 ms ± 2.97 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dict(d1, **d2)
# 267 ms ± 2.74 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit {**d1, **d2}
# 267 ms ± 1.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

인플레 이스 작업의 타이밍은 약간 까다롭기 때문에 추가 복사 작업을 통해 수정해야합니다 (첫 번째 타이밍은 참조 용임).

%timeit dd = d1.copy()
# 44.9 ms ± 495 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit dd = d1.copy(); join2(dd, d2)
# 296 ms ± 2.05 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); join2(dd, d2, True)
# 234 ms ± 1.02 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit dd = d1.copy(); update_or(dd, d2)
# 235 ms ± 1.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

0
import itertools
dict_merge = lambda *args: dict(itertools.chain(*[d.iteritems() for d in args]))

0

파이썬 3.4에서 직접 시도해 보았습니다 (멋진 {**dict_1, **dict_2}구문 을 사용할 수 없었습니다 ).

사전에 문자열이 아닌 키를 가지고 임의의 양의 사전을 제공하고 싶었습니다.

또한 새로운 사전을 만들고 싶었 기 때문에 사용하지 않기로 결정했습니다 collections.ChainMap( dict.update처음 사용하고 싶지 않은 이유와 유사합니다) .

내가 작성한 글은 다음과 같습니다.

def merge_dicts(*dicts):
    all_keys  = set(k for d in dicts for k in d.keys())
    chain_map = ChainMap(*reversed(dicts))
    return {k: chain_map[k] for k in all_keys}

merge_maps({'1': 1}, {'2': 2, '3': 3}, {'1': 4, '3': 5})
# {'1': 4, '3': 5, '2': 2}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.