Ibo*_*lit 6 python types type-hinting
我有一段旧的 python 代码,用于解析格式严格的文本文件(存储用于本地化的字符串)。由于结构是分层的,并且某些元素可能存在也可能不存在,因此程序使用嵌套的defaultdicts 来表示它。就像是:
terms = defaultdict(lambda: defaultdict(str)) # dict<key, dict<lang, translation>>
Run Code Online (Sandbox Code Playgroud)
因为这些字典没有类型化(因为它们的成员可以是任何类型)并且因为它们是嵌套的,并且因为我需要向该层次结构添加另一个级别,所以我决定向该程序添加类型化:
from typing import Tuple, Dict, Set, List, NewType
Key = NewType('Key', str)
Lang = NewType('Lang', str)
Translation = NewType('Translation', str)
PLIndex = NewType('PLIndex', int)
Run Code Online (Sandbox Code Playgroud)
但是,我终生无法弄清楚如何重写terms =上面的行以defaultdict输入那些嵌套的s。
我最终做的只是基本上包装dict到我的类型中,这看起来不太好:
class Forms:
def __init__(self):
self.dct: Dict[PLIndex, Translation] = {}
def __getitem__(self, item: PLIndex) -> Translation:
return self.dct[item]
def __setitem__(self, key: PLIndex, value: Translation) -> None:
self.dct[key] = value
class Translations:
def __init__(self):
self.dct: Dict[Lang, Forms] = {}
def __getitem__(self, item: Lang) -> Forms:
if item not in self.dct:
self.dct[item] = Forms()
return self.dct[item]
def __setitem__(self, key: Lang, value: Forms) -> None:
self.dct[key] = value
def items(self):
return self.dct.items()
class Terms:
def __init__(self):
self.dct: Dict[Key, Translations] = {}
def __getitem__(self, item: Key) -> Translations:
if item not in self.dct:
self.dct[item] = Translations()
return self.dct[item]
def __setitem__(self, key: Key, value: Translations) -> None:
self.dct[key] = value
def __len__(self):
return len(self.dct)
def items(self):
return self.dct.items()
...
terms = Terms()
Run Code Online (Sandbox Code Playgroud)
有没有一种方法可以将我的Forms,Translations和其他类型声明为仅NewType用于dict/ 的sdefaultdict并且能够以terms =强制嵌套字典的正确类型的方式重写?或者我可以扩展dict/ defaultdict(而不是包装它们)并能够强制执行正确的类型?或者有更好的方法吗?
在我看来,包装字典似乎是毫无意义的代码(因为它没有添加任何新功能,但您仍然必须维护它),如果可能的话,我会避免它。
现在,以下内容对我有用:
from collections import defaultdict
from typing import Tuple, Dict, DefaultDict, Set, List, NewType
Key = NewType('Key', str)
Lang = NewType('Lang', str)
Translation = NewType('Translation', str)
PLIndex = NewType('PLIndex', int)
FormsDict = DefaultDict[PLIndex, Translation]
TranslationsDict = DefaultDict[Lang, FormsDict]
TermsDict = DefaultDict[Key, TranslationsDict]
terms: TermsDict = defaultdict( # TermsDict
lambda: defaultdict( # TranslationsDict
lambda: defaultdict( # FormsDict
lambda: Translation("") # Default value "" (as Translation)
)
)
)
Run Code Online (Sandbox Code Playgroud)
我已经对此进行了测试mypy --strict并且它通过了验证。使用它defaultdict并仍然通过验证,似乎您需要cast
from typing import cast
terms[Key("key1")].update(
cast(TranslationsDict, {
Lang("en_GB.UTF-8"): cast(FormsDict, {
PLIndex(100): Translation("key1")
})
})
)
print(terms)
Run Code Online (Sandbox Code Playgroud)
输出:
defaultdict(<function <lambda> at 0x107d31cb0>, {
'key1': defaultdict(<function <lambda>.<locals>.<lambda> at 0x107d31d40>, {
'en_GB.UTF-8': {100: 'key1'}})})
Run Code Online (Sandbox Code Playgroud)