Python 自動將dict-list嵌套數據 轉換成 帶類型定義的對象

objtyping 帶類型定義的對象轉換器

由來

Python不是強類型語言,開發人員沒有給數據定義類型的習慣。這樣雖然靈活,但處理複雜業務邏輯的時候卻不夠方便——缺少類型檢查可能致使很難發現錯誤,在IDE裏編碼時也沒有代碼提示。因此開發了這個小工具來解決它。python

基本用法

  • 首先定義業務類,並經過類變量定義每一個字段的類型。git

    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方法的話,顯然就更復雜了。它們都不如直接定義類變量簡單優美。不過使用類變量也有缺點:就是它在這裏被當成元數據來使用了,若是真的須要定義類級別共享的變量,沒法區分。這個問題能夠在後面經過開發自定義註解來解決。github

  • 下一步就能夠把符合這個類定義結構的dict-list嵌套數據,轉化爲該類實例對象了:json

    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 等形式訪問裏面的屬性。工具

  • 固然也能夠把業務對象再轉回dict-list嵌套的形式編碼

    from objtyping import objtyping
    
    dict_list = objtyping.to_dict_list(company1)

    此時的dict_list對象,就是一大堆dict和list層級嵌套的原始類型數據code

使用場景

初始化對象

Python沒有js那麼方便的初始化對象方式,但有這個工具就能夠這樣寫(就是前面基礎使用的彙總):orm

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格式。正好能夠藉助這個工具實現進一步轉化。開發

json

示例

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工具,會試圖按照類定義,在基礎類型之間強制轉換。

yaml

示例

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"一樣被強轉了類型。


項目地址:>https://github.com/songofhawk...

相關文章
相關標籤/搜索