我经常需要读取一个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!
但是当与给定列对应的类不是很简单时,我遇到了问题。例如,类Performance
表示一些与性能相关的特性,需要从'300 hp,0-60:mph 5.2 s','540 hp,1/4 mile:8 sec @ 140等字符串创建英里”。特定的字符串模式在整个单个表中都是相同的,事先是已知的,并且我有清晰的语法来描述它。
现在,我可以按照我之前的方法编写:
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
被修改为考虑到四分之一英里的最终速度(而不仅仅是四分之一英里的时间),{ {1}}也必须重写。
我可以将所有实际构造功能移动到PerformanceFormat
,并将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)
# ...
必须做一些烦人的事情:它需要处理不同的情况,其中列由元组而不是单个可调用来描述。 (具体来说,它需要调用第一个元素,将它从单元格读取的值和元组中的第二个元素都传递给它。)
有更好,更清洁的方法吗?
答案 0 :(得分:0)
这是在正则表达式中使用命名组的方法的草图,对应于构造函数中的关键字参数:
class RegexFormat:
def __init__(self, cls, pattern):
self.cls = cls
self.regex = re.compile(pattern)
def __call__(self, string):
return self.cls(**self.regex.match(string).groupdict())
class Performance:
def __init__(self, hp=None, torque=None, quarter_mile_time=None)
#....
column_types = {'Car Make' : str,
'Quantity' : IntegerFormat(1000),
'Performance' : RegexFormat(Performance,
r'hp: (?P<hp>\d+) torque: (?P<torque>\d+)')}