我知道这是一个相当老的问题,标记为 python-2.5,但这里的答案适用于 Python 3.6+,使用该语言的更新版本的人们可能会感兴趣。
typing.NamedTuple
它利用了Python 3.5 中添加的内置类。从文档中可能不明显的是,每个字段的“类型”可以是一个函数。
示例用法代码还使用所谓的f-string文字,直到 Python 3.6 才添加,但不需要使用它们来进行核心数据类型转换。
#!/usr/bin/env python3.6
import ast
import csv
from typing import NamedTuple
class Record(NamedTuple):
""" Define the fields and their types in a record. """
IsActive: bool
Type: str
Price: float
States: ast.literal_eval # Handles string represenation of literals.
@classmethod
def _transform(cls: 'Record', dict_: dict) -> dict:
""" Convert string values in given dictionary to corresponding Record
field type.
"""
return {name: cls.__annotations__[name](value)
for name, value in dict_.items()}
filename = 'test_transform.csv'
with open(filename, newline='') as file:
for i, row in enumerate(csv.DictReader(file)):
row = Record._transform(row)
print(f'row {i}: {row}')
输出:
row 0: {'IsActive': True, 'Type': 'Cellphone', 'Price': 34.0, 'States': [1, 2]}
row 1: {'IsActive': False, 'Type': 'FlatTv', 'Price': 3.5, 'States': [2]}
row 2: {'IsActive': True, 'Type': 'Screen', 'Price': 100.23, 'States': [5, 1]}
row 3: {'IsActive': True, 'Type': 'Notebook', 'Price': 50.0, 'States': [1]}
通过创建一个仅包含通用类方法的基类来概括这一点并不简单,因为实现的方式typing.NamedTuple
。
为了避免这个问题,在 Python 3.7+ 中,dataclasses.dataclass
可以使用 a 代替,因为它们没有继承问题——所以创建一个可以重用的通用基类很简单:
#!/usr/bin/env python3.7
import ast
import csv
from dataclasses import dataclass, fields
from typing import Type, TypeVar
T = TypeVar('T', bound='GenericRecord')
class GenericRecord:
""" Generic base class for transforming dataclasses. """
@classmethod
def _transform(cls: Type[T], dict_: dict) -> dict:
""" Convert string values in given dictionary to corresponding type. """
return {field.name: field.type(dict_[field.name])
for field in fields(cls)}
@dataclass
class CSV_Record(GenericRecord):
""" Define the fields and their types in a record.
Field names must match column names in CSV file header.
"""
IsActive: bool
Type: str
Price: float
States: ast.literal_eval # Handles string represenation of literals.
filename = 'test_transform.csv'
with open(filename, newline='') as file:
for i, row in enumerate(csv.DictReader(file)):
row = CSV_Record._transform(row)
print(f'row {i}: {row}')
从某种意义上说,使用哪一个并不是很重要,因为从未创建过类的实例——使用一个只是在记录数据结构中指定和保存字段名称及其类型的定义的一种干净方式。
ATypedDict
被添加到typing
Python 3.8 中的模块中,它也可用于提供类型信息,但必须以稍微不同的方式使用,因为它实际上并没有定义新类型 like NamedTuple
and dataclasses
do——因此它需要一个独立的转换功能:
#!/usr/bin/env python3.8
import ast
import csv
from dataclasses import dataclass, fields
from typing import TypedDict
def transform(dict_, typed_dict) -> dict:
""" Convert values in given dictionary to corresponding types in TypedDict . """
fields = typed_dict.__annotations__
return {name: fields[name](value) for name, value in dict_.items()}
class CSV_Record_Types(TypedDict):
""" Define the fields and their types in a record.
Field names must match column names in CSV file header.
"""
IsActive: bool
Type: str
Price: float
States: ast.literal_eval
filename = 'test_transform.csv'
with open(filename, newline='') as file:
for i, row in enumerate(csv.DictReader(file), 1):
row = transform(row, CSV_Record_Types)
print(f'row {i}: {row}')