답변:
당신이 말할 수는 defaultdict
설정 기본값에 유용 DICT를 작성하기 전에 및 setdefault
기본값을 설정하는 데 유용 동안 또는 DICT를 작성 후 .
아마도 가장 일반적인 유스 케이스 : 항목 그룹화 (정렬되지 않은 데이터, 그렇지 않으면 사용 itertools.groupby
)
# really verbose
new = {}
for (key, value) in data:
if key in new:
new[key].append( value )
else:
new[key] = [value]
# easy with setdefault
new = {}
for (key, value) in data:
group = new.setdefault(key, []) # key might exist already
group.append( value )
# even simpler with defaultdict
from collections import defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append( value ) # all keys have a default already
때로는 dict를 만든 후 특정 키가 있는지 확인하려고합니다. defaultdict
이 경우 명시 적 액세스에서만 키를 생성하기 때문에 작동하지 않습니다. 많은 헤더와 함께 HTTP를 사용한다고 생각하십시오. 일부는 선택 사항이지만 기본값을 원합니다.
headers = parse_headers( msg ) # parse the message, get a dict
# now add all the optional headers
for headername, defaultvalue in optional_headers:
headers.setdefault( headername, defaultvalue )
defaultdict
. 첫 번째 단락에서 무슨 뜻인지 예를 들어 줄 수 있습니까?
setdefault
. 반면 defaultdict
에 A 가 모두 defaultvalues
같지 않으면 (즉, 일부는 0
있고 일부는 []
) 작동하지 않습니다 .
headers = dict(optional_headers)
입니다. 기본값이 모두 같지 않은 경우. 그리고 최종 결과는 HTTP 헤더를 먼저 얻은 다음 얻지 못한 기본값을 설정하는 것과 같습니다. 그리고 이미 가지고 있다면 꽤 사용할 수 있습니다 optional_headers
. 주어진 2 단계 코드를 시도하여 귀하의 코드와 비교하면 내가 무엇을 의미하는지 알 수 있습니다.
new.setdefault(key, []).append(value)
defaultdict
이 낫다는 것이 이상하다는 것을 알았 습니다 setdefault
(지금 유스 케이스는 어디에 있습니까?). 또한 IMO 예제를 ChainMap
더 잘 처리 할 수 있습니다 http
.
setdefault
이 함수와 같이 키워드 인수 dicts에 일반적으로 사용 합니다.
def notify(self, level, *pargs, **kwargs):
kwargs.setdefault("persist", level >= DANGER)
self.__defcon.set(level, **kwargs)
try:
kwargs.setdefault("name", self.client.player_entity().name)
except pytibia.PlayerEntityNotFound:
pass
return _notify(level, *pargs, **kwargs)
키워드 인수를 취하는 함수 주위의 래퍼에서 인수를 조정하는 데 좋습니다.
defaultdict
새 목록처럼 기본값이 정적 일 때 좋지만 동적 인 경우에는 그다지 많지 않습니다.
예를 들어 문자열을 고유 한 정수로 매핑하려면 사전이 필요합니다. defaultdict(int)
항상 기본값으로 0을 사용합니다. 마찬가지로 defaultdict(intGen())
항상 1을 생성합니다.
대신, 나는 정규 dict을 사용했습니다.
nextID = intGen()
myDict = {}
for lots of complicated stuff:
#stuff that generates unpredictable, possibly already seen str
strID = myDict.setdefault(myStr, nextID())
참고 dict.get(key, nextID())
나중에뿐만 아니라이 값을 참조 할 수 있어야하기 때문에 불충분하다.
intGen
int를 자동으로 증가시키고 그 값을 반환하는 작은 클래스입니다.
class intGen:
def __init__(self):
self.i = 0
def __call__(self):
self.i += 1
return self.i
누군가 이것을 할 수있는 방법이 있다면 defaultdict
그것을보고 싶습니다.
intGen
있습니다 itertools.count().next
.
nextID()
myDict.setdefault()
반환되는 값이로 사용되지 않더라도 호출 될 때마다의 값이 증가 합니다 strID
. 이것은 어쨌든 쓸모없는 것처럼 보이며 내가 setdefault()
일반적으로 좋아하지 않는 것 중 하나를 보여줍니다. 즉 default
, 실제로 사용되는지 여부를 항상 평가합니다 .
defaultdict
: myDict = defaultdict(lambda: nextID())
. 나중에 strID = myDict[myStr]
루프에서.
myDict = defaultdict(nextID)
않습니까?
대부분의 답변 상태 setdefault
또는 defaultdict
키가 존재하지 않을 때 기본값을 설정할 수 있습니다. 그러나의 사용 사례와 관련하여 작은주의 사항을 지적하고 싶습니다 setdefault
. 파이썬 인터프리터가 실행될 때 setdefault
키가 사전에 존재하더라도 항상 함수에 대한 두 번째 인수를 평가합니다. 예를 들면 다음과 같습니다.
In: d = {1:5, 2:6}
In: d
Out: {1: 5, 2: 6}
In: d.setdefault(2, 0)
Out: 6
In: d.setdefault(2, print('test'))
test
Out: 6
보시다시피 print
, 사전에 2가 이미 존재하더라도 실행되었습니다. setdefault
예를 들어와 같은 최적화 에 사용하려는 경우 특히 중요합니다 memoization
. 에 대한 두 번째 인수로 재귀 함수 호출을 추가하면 setdefault
파이썬이 항상 함수를 재귀 적으로 호출하므로 성능을 얻지 못합니다.
메모가 언급되었으므로 메모로 함수를 향상시키는 것을 고려하는 경우 functools.lru_cache 데코레이터를 사용하는 것이 더 나은 대안입니다. lru_cache는 재귀 함수에 대한 캐싱 요구 사항을보다 잘 처리합니다.
Muhammad가 말했듯이 때때로 기본값을 설정하려는 상황이 있습니다. 이에 대한 좋은 예는 데이터 구조가 먼저 채워지고 쿼리됩니다.
트라이를 고려하십시오. 단어를 추가 할 때 하위 노드가 필요하지만 존재하지 않는 경우 트리를 확장하기 위해 하위 노드를 만들어야합니다. 단어의 존재를 쿼리 할 때 누락 된 하위 노드는 해당 단어가 존재하지 않으며 단어를 만들지 않아야 함을 나타냅니다.
defaultdict는 이것을 할 수 없습니다. 대신 get 및 setdefault 메소드를 사용하는 일반 dict를 사용해야합니다.
이론적으로 말하면 때로는 기본값을 설정하고 때로는하지 않으려는 setdefault
경우 여전히 유용합니다 . 실생활에서는 그런 유스 케이스를 보지 못했습니다.
그러나 흥미로운 사용 사례는 표준 라이브러리 (Python 2.6, _threadinglocal.py)에서 나옵니다.
>>> mydata = local()
>>> mydata.__dict__
{'number': 42}
>>> mydata.__dict__.setdefault('widgets', [])
[]
>>> mydata.widgets
[]
나는 사용 __dict__.setdefault
이 꽤 유용한 경우 라고 말할 것 입니다.
편집 : 발생하는 것처럼 이것은 표준 라이브러리의 유일한 예이며 주석입니다. 따라서의 존재를 정당화하는 것만으로는 충분하지 않을 수 있습니다 setdefault
. 여전히 여기에 설명이 있습니다.
객체는 속성에 속성을 저장 __dict__
합니다. 그와 같이, __dict__
속성은 객체 생성 후 언제든지 쓸 수 있습니다. 또한 사전이 아닙니다 defaultdict
. 일반적인 경우의 객체가 가지고하는 것은 분별없는 __dict__
A와 defaultdict
그 속성과 모든 법적 식별자를 가진 각 개체를 만들 것 때문이다. 따라서 __dict__.setdefault
유용하지 않은 것으로 간주되면 완전히 삭제하는 것 외에는 제거하는 Python 객체의 변경을 예측할 수 없습니다 .
__dict__
구현이 dict
아니라 a defaultdict
입니다.
setdefault
파이썬 에 머무르는 것에 대해서는 신경 쓰지 않지만 이제는 거의 쓸모가 없다는 것이 궁금합니다.
setdefault
존재하거나 존재하지 않는 키를 통해 dict에 할당하고 있음을 명시하고 존재하지 않는 경우 기본값으로 작성하려고합니다 d.setdefault(key,[]).append(value)
. 예 : . 다른 곳에서 당신이 프로그램에서 alist=d[k]
k는 계산되며, 당신이 K 경우 예외가 발생 싶어하지 않는 defaultdict로 필요할 수 있습니다 D (에 assert k in d
또는if not ( k in d): raise KeyError
의 한 가지 단점 defaultdict
이상은 dict
( dict.setdefault
)는 점이다 defaultdict
오브젝트가 새 항목 생성 매번 (로 예를 들어, 기존의 키가 주어진다 비 ==
, print
). 또한 defaultdict
클래스는 일반적으로 클래스보다 덜 일반적 dict
이며 IME를 직렬화하기가 더 어렵습니다.
PS IMO 기능은 대상을 돌연변이시키는 것은 아니며, 대상을 돌연변이해서는 안된다.
defaultdict(lambda l=[]: l)
대신 쉽게 할 수 있습니다 .
다음은 setdefault의 유용성을 보여주는 몇 가지 예입니다.
"""
d = {}
# To add a key->value pair, do the following:
d.setdefault(key, []).append(value)
# To retrieve a list of the values for a key
list_of_values = d[key]
# To remove a key->value pair is still easy, if
# you don't mind leaving empty lists behind when
# the last value for a given key is removed:
d[key].remove(value)
# Despite the empty lists, it's still possible to
# test for the existance of values easily:
if d.has_key(key) and d[key]:
pass # d has some values for key
# Note: Each value can exist multiple times!
"""
e = {}
print e
e.setdefault('Cars', []).append('Toyota')
print e
e.setdefault('Motorcycles', []).append('Yamaha')
print e
e.setdefault('Airplanes', []).append('Boeing')
print e
e.setdefault('Cars', []).append('Honda')
print e
e.setdefault('Cars', []).append('BMW')
print e
e.setdefault('Cars', []).append('Toyota')
print e
# NOTE: now e['Cars'] == ['Toyota', 'Honda', 'BMW', 'Toyota']
e['Cars'].remove('Toyota')
print e
# NOTE: it's still true that ('Toyota' in e['Cars'])
나는 받아 들여진 대답을 다시 쓰고 초보자를 위해 그것을 촉진시킵니다.
#break it down and understand it intuitively.
new = {}
for (key, value) in data:
if key not in new:
new[key] = [] # this is core of setdefault equals to new.setdefault(key, [])
new[key].append(value)
else:
new[key].append(value)
# easy with setdefault
new = {}
for (key, value) in data:
group = new.setdefault(key, []) # it is new[key] = []
group.append(value)
# even simpler with defaultdict
new = defaultdict(list)
for (key, value) in data:
new[key].append(value) # all keys have a default value of empty list []
또한 메소드를 참조로 분류했습니다.
dict_methods_11 = {
'views':['keys', 'values', 'items'],
'add':['update','setdefault'],
'remove':['pop', 'popitem','clear'],
'retrieve':['get',],
'copy':['copy','fromkeys'],}
사전에 기본값 (!!!)을 설정할 때 setdefault를 자주 사용합니다. 다소 일반적으로 os.environ 사전 :
# Set the venv dir if it isn't already overridden:
os.environ.setdefault('VENV_DIR', '/my/default/path')
간결하게 말하면 다음과 같습니다.
# Set the venv dir if it isn't already overridden:
if 'VENV_DIR' not in os.environ:
os.environ['VENV_DIR'] = '/my/default/path')
결과 변수를 사용할 수도 있습니다.
venv_dir = os.environ.setdefault('VENV_DIR', '/my/default/path')
그러나 불이행이 존재하기 전에 필요한 것보다 덜 필요합니다.
방금 우연히 발견 한 하나의 매우 중요한 유스 케이스 : dict.setdefault()
단일 표준 객체 만 원할 때 멀티 스레드 코드에 적합합니다 (동일한 여러 객체가 아닌).
예를 들어 (Int)Flag
Python 3.6.0 의 Enum에는 버그가 있습니다 . 여러 스레드가 복합 (Int)Flag
멤버를 놓고 경쟁하는 경우 둘 이상의 결과가 발생할 수 있습니다.
from enum import IntFlag, auto
import threading
class TestFlag(IntFlag):
one = auto()
two = auto()
three = auto()
four = auto()
five = auto()
six = auto()
seven = auto()
eight = auto()
def __eq__(self, other):
return self is other
def __hash__(self):
return hash(self.value)
seen = set()
class cycle_enum(threading.Thread):
def run(self):
for i in range(256):
seen.add(TestFlag(i))
threads = []
for i in range(8):
threads.append(cycle_enum())
for t in threads:
t.start()
for t in threads:
t.join()
len(seen)
# 272 (should be 256)
해결 방법은 setdefault()
계산 된 복합 멤버를 저장하는 마지막 단계로 사용하는 것입니다. 이미 저장된 다른 멤버는 새 멤버 대신 사용되어 고유 한 Enum 멤버를 보장합니다.
[편집] 매우 잘못되었습니다! setdefault는 항상 long_computation을 트리거하여 Python을 열망합니다.
터틀의 답변을 확장합니다. 나에게 가장 좋은 사용 사례는 캐시 메커니즘입니다. 대신에:
if x not in memo:
memo[x]=long_computation(x)
return memo[x]
3 줄과 2 ~ 3 개의 조회를 사용 하면 행복하게 쓸 것입니다 .
return memo.setdefault(x, long_computation(x))
long_computation(x)
는 if 만 호출됩니다 x not in memo
. 두 번째 long_computation(x)
는 항상 호출됩니다. 대입 만 조건부이며 동등한 코드 setdefault
는 다음과 같습니다. v = long_computation(x)
/ if x not in memo:
/ memo[x] = v
.
나는 여기에 주어진 대답을 좋아합니다.
http://stupidpythonideas.blogspot.com/2013/08/defaultdict-vs-setdefault.html
즉, 성능이 중요하지 않은 앱에서 결정은 다운 스트림에서 빈 키의 조회 ( 즉, KeyError
기본값) 를 처리하는 방법에 따라 결정해야합니다 .
다른 사용 사례 는 이미 설정된 키의 값 을 덮어 쓰지 않으려setdefault()
는 경우 입니다. defaultdict
덮어 쓰지만 setdefault()
그렇지 않습니다. 중첩 된 사전의 경우 현재 하위 사전을 제거하지 않기 위해 키가 아직 설정되지 않은 경우에만 기본값을 설정하려는 경우가 더 많습니다. 당신이 사용할 때 setdefault()
입니다.
예 defaultdict
:
>>> from collection import defaultdict()
>>> foo = defaultdict()
>>> foo['a'] = 4
>>> foo['a'] = 2
>>> print(foo)
defaultdict(None, {'a': 2})
setdefault
덮어 쓰지 않습니다 :
>>> bar = dict()
>>> bar.setdefault('a', 4)
>>> bar.setdefault('a', 2)
>>> print(bar)
{'a': 4}