Python 不是强类型语言,开发人员没有给数据定义类型的习惯。这样虽然灵活,但处理复杂业务逻辑的时候却不够方便——缺乏类型检查可能导致很难发现错误,在 IDE 里编码时也没有代码提示。所以开发了这个小工具来解决它。
from typing import List
class Person:
name: str
age: int
class Company:
name: str
revenue: float
employees: List[Person]
之所以选择类变量来定义,是因为它最简洁和直观。相比之下,如果在__init__方法中初始化实例变量,是没有办法获取类型定义( type_hint )的;如果用 @property 注解或者 getter,setter 方法的话,显然就更复杂了。它们都不如直接定义类变量简单优美。不过使用类变量也有缺点:就是它在这里被当成元数据来使用了,如果真的需要定义类级别共享的变量,无法区分。这个问题可以在后面通过开发自定义注解来解决。
from objtyping import objtyping
company1 = objtyping.from_dict_list({
'name': 'Apple',
'revenue': 18.5,
'employees': [{
'name': 'Tom',
'age': 20
}, {
'name': 'Jerry',
'age': 31
}]
}, Company)
此时的 company1 就是完整的 Company 对象了, 可以直接使用 company1.name, company1.employees[0].name 等形式访问里面的属性。
from objtyping import objtyping
dict_list = objtyping.to_dict_list(company1)
此时的 dict_list 对象,就是一大堆 dict 和 list 层级嵌套的原始类型数据
Python 没有 js 那么方便的初始化对象方式,但有这个工具就可以这样写(就是前面基础使用的汇总):
from typing import List
from objtyping import objtyping
class Person:
name: str
age: int
class Company:
name: str
revenue: float
employees: List[Person]
def __str__(self): # 其实一般可能都是这样简单用一下的
return "'{}' has {} employees: {}".format(self.name, len(self.employees), ' and '.join(map(lambda emp: emp.name, self.employees)))
if __name__ == '__main__':
company1 = objtyping.from_dict_list({
'name': 'Apple',
'revenue': 18.5,
'employees': [{
'name': 'Tom',
'age': 20
}, {
'name': 'Jerry',
'age': 31
}]
}, Company)
print(company1)
输出结果:
'Apple' has 2 employees: Tom and Jerry
Python 的常见的序列化需求,包括 json 和 yaml 数据格式,它们都有相对完善的处理库。但同样是不强调类型的缘故,它们处理的对象都是原始的 dict-list 格式。正好可以借助这个工具实现进一步转化。
示例
import json
import sys
from typing import List
from objtyping import objtyping
class X:
x: int
y: str
class A:
q: str
a: str
b: int
c: List[X]
if __name__ == '__main__':
print("\r\n-----json-------")
json_obj = json.loads('{"q":9, "a":"Mark", "b":3, "c":[{"x":15, "y":"male"},{"x":9, "y":"female", "z":13}]}')
typed_obj = objtyping.from_dict_list(json_obj, A)
d_l_obj = objtyping.to_dict_list(typed_obj)
print(json.dumps(d_l_obj))
sys.exit()
输出结果
-----json-------
{"q": "9", "a": "Mark", "b": 3, "c": [{"x": 15, "y": "male"}, {"x": 9, "y": "female", "z": 13}]}
这里需要注意的是:本来属性"q",在最初的 json 结构中,是个数字,但由于类变量定义中是字符串,转换成业务对象以后,它的类型就是字符串了——objtyping 工具,会试图按照类定义,在基础类型之间强制转换。
示例
import sys
from ruamel.yaml import YAML
from typing import List
from objtyping import objtyping
class X:
x: int
y: str
class A:
q: str
a: str
b: int
c: List[X]
if __name__ == '__main__':
print("\r\n-----yaml-------")
yaml = YAML()
yaml_obj = yaml.load('''
q: 9
a: Mark
b: 3
c:
- x: 15
y: male
- x: 9
y: female
z: 13
''')
typed_obj = objtyping.from_dict_list(yaml_obj, A)
d_l_obj = objtyping.to_dict_list(typed_obj)
yaml.dump(d_l_obj, sys.stdout)
sys.exit()
输出结果
-----yaml-------
q: '9'
a: Mark
b: 3
c:
- x: 15
y: male
- x: 9
y: female
z: 13
这里的属性"q"同样被强转了类型。
项目地址:github
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.