[python] defaultdict의 default_factory에 키를 전달하는 영리한 방법이 있습니까?

클래스에는 하나의 매개 변수를 취하는 생성자가 있습니다.

class C(object):
    def __init__(self, v):
        self.v = v
        ...

코드 어딘가에서 dict의 값이 키를 알고 있으면 유용합니다.
신생아 기본값에 전달 된 키와 함께 defaultdict를 사용하고 싶습니다.

d = defaultdict(lambda : C(here_i_wish_the_key_to_be))

어떤 제안?



답변

그것은 거의 자격 없다 영리한 -하지만 서브 클래스는 당신의 친구입니다 :

class keydefaultdict(defaultdict):
    def __missing__(self, key):
        if self.default_factory is None:
            raise KeyError( key )
        else:
            ret = self[key] = self.default_factory(key)
            return ret

d = keydefaultdict(C)
d[x] # returns C(x)


답변

아니 없어.

defaultdict누락 된 항목 key을 기본적 으로 전달하도록 구현을 구성 할 수 없습니다 default_factory. 유일한 옵션은 직접 구현하는 것입니다.defaultdict 위의 @JochenRitzel이 제안한대로 하위 클래스 입니다.

그러나 이것은 “영리”하거나 표준 라이브러리 솔루션만큼 깔끔하지도 않습니다 (존재한다면). 따라서 귀하의 간결한 예 / 아니오 질문에 대한 대답은 분명히 “아니오”입니다.

표준 라이브러리에 자주 필요한 도구가 없다는 것은 너무 나쁩니다.


답변

나는 당신이 defaultdict여기에 전혀 필요하지 않다고 생각합니다 . 왜 그냥 dict.setdefault방법을 사용하지 않습니까?

>>> d = {}
>>> d.setdefault('p', C('p')).v
'p'

물론 많은 인스턴스가 생성 C됩니다. 문제인 경우 더 간단한 접근 방식이 효과가 있다고 생각합니다.

>>> d = {}
>>> if 'e' not in d: d['e'] = C('e')

defaultdict내가 볼 수있는 한 다른 대안 보다 빠를 것 입니다.

in테스트 속도 와 try-except 절 사용 에 관한 ETA :

>>> def g():
    d = {}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(g)
0.19638929363557622
>>> def f():
    d = {}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(f)
0.6167065411074759
>>> def k():
    d = {'a': 2}
    if 'a' in d:
        return d['a']


>>> timeit.timeit(k)
0.30074866358404506
>>> def p():
    d = {'a': 2}
    try:
        return d['a']
    except KeyError:
        return


>>> timeit.timeit(p)
0.28588609450770264


답변

다음은 자동으로 값을 추가하는 사전의 작동 예입니다. / usr / include에서 중복 파일을 찾는 데모 작업. 사용자 지정 사전 PathDict 에는 네 줄만 필요합니다.

class FullPaths:

    def __init__(self,filename):
        self.filename = filename
        self.paths = set()

    def record_path(self,path):
        self.paths.add(path)

class PathDict(dict):

    def __missing__(self, key):
        ret = self[key] = FullPaths(key)
        return ret

if __name__ == "__main__":
    pathdict = PathDict()
    for root, _, files in os.walk('/usr/include'):
        for f in files:
            path = os.path.join(root,f)
            pathdict[f].record_path(path)
    for fullpath in pathdict.values():
        if len(fullpath.paths) > 1:
            print("{} located in {}".format(fullpath.filename,','.join(fullpath.paths)))


답변