Как организовать обработку поля namedtuple при [первом] вызове?

Использую namedtuple для обработки полей CSV-файла, читаемого построчно.

Проблема #1: Перед загрузкой записей в namedtuple я очищаю каждую из них от обрамляющих кавычек через strip('"'). Всего в строке 59 полей, но реально в работе используются десятка полтора => проделывается лишняя работа. Нужно организовать "ленивые" вычисления.

Проблему #2: Хотелось бы производить дополнительную обработку поля при [первом?] обращении. Удалить обрамляющие кавычки, строку - кешировать через intern(), число - преобразовать в int(). Поля, которые не используются в процессе работы программы, никак специально обрабатываться не должны, чтобы сэкономить ресурсы.

Пример:
XDR = namedtuple('XDR', 'f_str, f_int')
xdr = XDR(['"answer"', '"42"'])

Ожидаемый результат:
xdr.f_str возвращает intern('answer')
xdr.f_int возвращает число 42, т.е. type(xdr.f_int) is int

Пока копаю тут: pythondoeswhat.blogspot.ru/2011/09/namedtupledict.html

UPD Похоже, нашёл именно то, что надо: https://github.com/brennerm/PyTricks/blob/master/c...
  • Вопрос задан
  • 328 просмотров
Решения вопроса 1
adugin
@adugin Автор вопроса, куратор тега Python
Пока удалось родить такое кривенькое решение. При каждом обращении атрибут пересчитывается заново. Хорошо бы добавить memoization. Но, по крайней мере, работает:
>>> def namedtuplex(*args, **kwargs):
    def getitem(self, key):
        if type(key) is str:
            value = getattr(self, key)
        else:
            value = tuple.__getitem__(self, key)
        if type(value) is str:
            value = value.strip('"')
            try:
                value = eval(value)
            except:
                value = intern(value)
        return value
    ntuple = collections.namedtuple(*args, **kwargs)
    ntuple.__getitem__ = getitem
    return ntuple

>>> XDR = namedtuplex('XDR', 'a b c d e f g')
>>> xdr = XDR('"abc"', 'def', '5', '"3.14"', 2.71, [1,2], None)
>>> xdr.a
'abc'
>>> xdr.b
'def'
>>> xdr.c
5
>>> xdr.d
3.14
>>> xdr.e
2.71
>>> xdr.f
[1, 2]
>>> xdr.g
>>> xdr['a']
'abc'
>>> xdr['b']
'def'
>>> xdr['c']
5
>>> xdr['d']
3.14
>>> xdr['e']
2.71
>>> xdr['f']
[1, 2]
>>> xdr['g']
>>>
В зависимость от конкретного применения можно сделать что-то такое:
eval(value.strip('"'))
Либо разбирать непосредственно через int() и float() в блоках try..except.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
собственно для выборки чтения из файла csv можно использовать модуль csv (там проблемы с юникодом но вполне решаемые)
код ниже расчитан на работу в 2,7, с юникодом, поэтому функция intern не используется, но при желании вполне можно добавить в метод to_str
в целом бы я организовал нечто подобное:
#!/usr/bin/env python
# -*- coding: utf-8
from __future__ import unicode_literals
from collections import namedtuple


def to_int(element):
    """
    Преобразуем строку в число.
    ARGUMENTS:
        :type element: str | unicode
        :rtype: int | long | None
    """
    try:
        # если числа записаны без кавычек
        # return int(element)
        # если при числа всегда обрамлёны кавычками
        return int(element[1:-1])
        # если может быть обрамлён
        # return int(element.strip('"'))
    except ValueError:
        return None


def to_str(element):
    """
    обрезаем кавычки
    ARGUMENTS:
        :type element: str | unicode
        :rtype: str | unicode
    """
    # если при этом всегда обрамлён кавычками
    return element[1:-1]
    # если может быть обрамлён
    # return element.strip('"')


def to_original(element):
    """
    собственно ничего не делаем со строкой
    ARGUMENTS:
        :type element: str | unicode
        :rtype: str | unicode
    """
    return element


# предположим что мы не испольщуем модуль csv
# а сами всё считываем построчно из файла
data = {
    '"1";"2";"test\"0";"test1";"123"',
    '"2";"3";"test1";"test2";"234"',
    '"4";"5";"test2";"test3";"345"',
}
# описываем какие колонки нас интересуют, и функции которые будут использоваться при их обработке
column_map = {
    0: to_int,
    2: to_str,
    4: to_original
}
XDR = namedtuple('XDR', 'num, string, other')
#: :type: list[XDR]
xdrs = list()
for row in data:
    row_splited = row.split(';')
    xdrs.append(XDR(*(func(row_splited[column]) for column, func in column_map.iteritems())))

print xdrs

вывод:
[XDR(num=1, string=u'test"0', other=u'"123"'), XDR(num=2, string=u'test1', other=u'"234"'), XDR(num=4, string=u'test2', other=u'"345"')]
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы