これはpython-2.5とタグ付けされたかなり古い質問ですが、Python 3.6以降で機能する回答があります。これは、言語のより最新のバージョンを使用している人々にとって興味深いかもしれません。
typing.NamedTuple
Python3.5で追加された組み込みクラスを活用します。ドキュメントからは明らかではないかもしれませんが、各フィールドの「タイプ」は関数である可能性があります。
使用法の例では、Python 3.6まで追加されなかったいわゆるf文字列リテラルも使用していますが、コアデータ型変換を行うためにそれらを使用する必要はありません。
#!/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}')
ある意味では、クラスのインスタンスは作成されないため、どちらを使用するかはそれほど重要ではありません。1つを使用することは、レコードデータ構造でフィールド名とそのタイプの定義を指定および保持するためのクリーンな方法にすぎません。
Python 3.8のモジュールにATypedDict
が追加されました。typing
これは、入力情報の提供にも使用できますが、実際にはのような新しい型を定義していないため、少し異なる方法で使用する必要があります。NamedTuple
したがってdataclasses
、スタンドアロンの変換が必要です。働き:
#!/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}')