私の目標は、CSV ソース リストの修飾子のスタックに基づいてフレームワークを最適化することです。各修飾子は、ヘッダー リストを使用して名前付きベースで機能します。
CSV の例 (ヘッダーを含む):
date;place
13/02/2013;New York
15/04/2012;Buenos Aires
29/10/2010;Singapour
毎回データを再編成せずに csv モジュールによって生成されたリストを使用できるようにするために、namedtuple に基づいていくつかのコードを作成しました。以下の生成コード:
class MyNamedList(object):
__slots__ = ("__values")
_fields = ['date', 'ignore', 'place']
def __init__(self, values):
self.__values = values
if len(self.__values) <= 151:
for i in range(len(self.__values), 151):
self.__values += [None,]
@property
def date(self):
return self.__values[0]
@date.setter
def date(self, val):
self.__values[0] = val
@property
def ignore(self):
return self.__values[150]
@ignore.setter
def ignore(self, val):
self.__values[150] = val
@property
def place(self):
return self.__values[1]
@b.setter
def place(self, val):
self.__values[1] = val
このクラスを使用したパフォーマンスには非常に失望しています。70000行のcsvファイルの各行に対して単純な修飾子関数(「無視」をTrueに100回変更します。はい、それが役に立たないことを知っています)を呼び出すと、9秒かかります(元のpythonを使用したpypy.5.5を使用) foo という名前のリストには 1.1 秒かかります (pypy と元の python と同じ)。
両方のアプローチ間で同等のパフォーマンスを得るためにできることはありますか? 私にとっては、record.ignore = True
直接インライン化 (またはそのように) することができ、したがってrecord[150] = True
. これを実現するために私が見ていないブロックポイントはありますか?
私が変更しているレコードは、実際には (今のところ) CSV ファイルの各行に対して作成されていないことに注意してください。つまり、リストへのアイテムの追加は、反復の前に 1 回だけ行われます。
更新:サンプルコード
--> 名前付きリストの使用
import namedlist
MyNamedList=namedlist.namedlist("MyNamedList", {"a":1, "b":2, "ignore":150})
test = MyNamedList([0,1])
def foo(a):
test.ignore = True # x100 times
import csv
stream = csv.reader(open("66666.csv", "rb"))
for i in stream:
foo(i)
--> 名前付きリストを使用しない
import namedlist
import csv
MyNamedList=namedlist.namedlist("MyNamedList", {"a":1, "b":2, "ignore":150})
test = MyNamedList([0,1])
sample_data = []
for i in range(len(sample_data), 151):
sample_data += [None,]
def foo(a):
sample_data[150] = True # x100 times
stream = csv.reader(open("66666.csv", "rb"))
for i in stream:
foo(i)
更新 #2 : namedlist.py のコード (namedtuple.py に大きく基づいています)
# Retrieved from http://code.activestate.com/recipes/500261/
# Licensed under the PSF license
from keyword import iskeyword as _iskeyword
import sys as _sys
def namedlist(typename, field_indices, verbose=False, rename=False):
# Parse and validate the field names. Validation serves two purposes,
# generating informative error messages and preventing template injection attacks.
field_names = field_indices.keys()
for name in [typename,] + field_names:
if not min(c.isalnum() or c=='_' for c in name):
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores: %r' % name)
if _iskeyword(name):
raise ValueError('Type names and field names cannot be a keyword: %r' % name)
if name[0].isdigit():
raise ValueError('Type names and field names cannot start with a number: %r' % name)
seen_names = set()
for name in field_names:
if name.startswith('_') and not rename:
raise ValueError('Field names cannot start with an underscore: %r' % name)
if name in seen_names:
raise ValueError('Encountered duplicate field name: %r' % name)
seen_names.add(name)
# Create and fill-in the class template
numfields = len(field_names)
argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
max_index=-1
for name in field_names:
index = field_indices[name]
if max_index < index:
max_index = index
max_index += 1
template = '''class %(typename)s(object):
__slots__ = ("__values") \n
_fields = %(field_names)r \n
def __init__(self, values):
self.__values = values
if len(self.__values) <= %(max_index)s:
for i in range(len(self.__values), %(max_index)s):
self.__values += [None,]'''% locals()
for name in field_names:
index = field_indices[name]
template += ''' \n
@property
def %s(self):
return self.__values[%d]
@%s.setter
def %s(self, val):
self.__values[%d] = val''' % (name, index, name, name, index)
if verbose:
print template
# Execute the template string in a temporary namespace
namespace = {'__name__':'namedtuple_%s' % typename,
'_property':property, '_tuple':tuple}
try:
exec template in namespace
except SyntaxError, e:
raise SyntaxError(e.message + ':\n' + template)
result = namespace[typename]
# For pickling to work, the __module__ variable needs to be set to the frame
# where the named tuple is created. Bypass this step in enviroments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython).
try:
result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
return result