我经常需要读取一个 CSV 文件并使用它来填充一个内部类似表的结构,其中包含适当的对象,如下所示:
column_types = {'Car Make' : str, 'Year' : int,
'Quantity' : int, 'Weight' : float}
# read_from_file will call str()/int()/etc., passing it
# the string that it finds in each cell
t = Table.read_from_file('mytable.txt', column_types)
t[0]['Car Model'] # 'Nissan'
t[0]['Year'] # 2010
t[0]['Quantity'] # 40
t[0]['Weight'] # 2105.53
如果内置转换不够,事情会变得更加复杂。例如,在某些表中,数量可能以千表示。为此,我创建了以下类IntegerFormat
:
class IntegerFormat(int): # this case is really simple, so I subclass built-in
def __init__(self, unit):
self.unit = unit
# create object from string
def __call__(self, string):
value = int(string) * self.unit
column_types = {'Car Make' : str, 'Year' : int,
'Quantity' : IntegerFormat(1000)}
# no change to Table.read_from_file required
t = Table.read_from_file('mytable.txt', column_types)
t[0]['Quantity'] # 40000; now we know the correct units!
但是当与给定列对应的类不是很简单时,我遇到了一个问题。例如,classPerformance
表示一些与性能相关的特征,需要从诸如“300 hp, 0-60: mph 5.2 s”、“540 hp, 1/4 mile: 8 sec @140 mph”之类的字符串创建。特定的字符串模式在单个表中将是相同的,是预先知道的,我有清晰的语法来描述它。
现在,我可以按照我之前的方法编写:
class PerformanceFormat:
def __init__(self, pattern):
# convert pattern into some internal form and store it in self.pattern
# ...
def __call__(self, string):
# process the string to obtain parameters that Performance.__init__ expects
# create Performance object and return it
# ...
return Performance(hp, torque, quarter_mile_time)
但是,PerformanceFormat
与Performance
: 如果Performance
被修改以考虑四分之一英里的最终速度(而不仅仅是四分之一英里的时间),PerformanceFormat
则与 : if 的关系非常紧密,也必须重写。
我可以将所有实际的构造功能移动到Performance
中,并且PerformanceFormat
仅限于存储字符串模式。但是在那种情况下,当Table.read_from_file()
需要读取相关单元格时,仅仅拥有实例是不够的PerformanceFormat
——它怎么知道Performance
需要创建的是类实例?
我想我可以这样做:
column_types = {'Car Make' : str,
'Quantity' : IntegerFormat(1000),
'Performance' : (Performance, PerformanceType('hp: * torque: *')}
这里PerformanceType
可以创建一个可以输入的标准表示Performance
,大大减少两者之间的耦合:
class Performance:
def __init__(self, string, format):
standard_representation = format(string)
# ...
也许更好的是,PerformanceType
可以被动地存储模式,完全消除耦合:
class Performance:
def __init__(self, string, format):
standard_representation = self.to_standard(string, format)
# ...
那太棒了。但是现在Table.read_from_file
必须做一些烦人的事情:它需要以不同的方式处理那些由元组而不是单个可调用对象描述列的情况。(具体来说,它需要调用第一个元素,将它从单元格中读取的值和元组中的第二个元素都传递给它。)
有没有更好、更清洁的方法?