Лучшие практики для манипулирования наборами результатов базы данных в Python? - PullRequest
4 голосов
/ 22 сентября 2008

Я пишу простое веб-приложение на Python, которое состоит из нескольких страниц бизнес-данных, отформатированных для iPhone. Мне удобно программировать на Python, но я не очень знаком с "идиомой" Python, особенно в отношении классов и объектов. Объектно-ориентированный дизайн Python несколько отличается от других языков, с которыми я работал. Поэтому, хотя мое приложение работает, мне любопытно, есть ли лучший способ для достижения моих целей.

Особенности: Как обычно реализуется рабочий процесс базы данных request-transform-render в Python? В настоящее время я использую pyodbc для извлечения данных, копирования результатов в атрибуты объекта, выполнения некоторых вычислений и слияний с использованием списка этих объектов, а затем визуализации вывода из списка объектов. (Пример кода ниже, запросы SQL отредактированы.) Это нормально? Есть ли способ лучше? Есть ли какие-то конкретные "ошибки", с которыми я столкнулся в моем относительном незнании Python? Меня особенно беспокоит то, как я реализовал список строк, используя пустой класс "Запись".

class Record(object):
    pass

def calculate_pnl(records, node_prices):
    for record in records:
        try:
            # fill RT and DA prices from the hash retrieved above
            if hasattr(record, 'sink') and record.sink:
                record.da = node_prices[record.sink][0] - node_prices[record.id][0]
                record.rt = node_prices[record.sink][1] - node_prices[record.id][1]
            else:
                record.da = node_prices[record.id][0]
                record.rt = node_prices[record.id][1]

            # calculate dependent values: RT-DA and PNL
            record.rtda = record.rt - record.da
            record.pnl = record.rtda * record.mw
        except:
            print sys.exc_info()

def map_rows(cursor, mappings, callback=None):
    records = []
    for row in cursor:
        record = Record()
        for field, attr in mappings.iteritems():
            setattr(record, attr, getattr(row, field, None))
        if not callback or callback(record):
            records.append(record)

    return records

def get_positions(cursor):
    # get the latest position time
    cursor.execute("SELECT latest data time")
    time = cursor.fetchone().time
    hour = eelib.util.get_hour_ending(time)

    # fetch the current positions
    cursor.execute("SELECT stuff FROM atable", (hour))

    # read the rows
    nodes = {}
    def record_callback(record):
        if abs(record.mw) > 0:
            if record.id: nodes[record.id] = None
            return True
        else:
            return False
    records = util.map_rows(cursor, {
        'id': 'id',
        'name': 'name',
        'mw': 'mw'
    }, record_callback)

    # query prices
    for node_id in nodes:
        # RT price
        row = cursor.execute("SELECT price WHERE ? ? ?", (node_id, time, time)).fetchone()
        rt5 = row.lmp if row else None

        # DA price
        row = cursor.execute("SELECT price WHERE ? ? ?", (node_id, hour, hour)).fetchone()
        da = row.da_lmp if row else None

        # update the hash value
        nodes[node_id] = (da, rt5)

    # calculate the position pricing
    calculate_pnl(records, nodes)

    # sort
    records.sort(key=lambda r: r.name)

    # return the records
    return records

Ответы [ 4 ]

1 голос
/ 23 сентября 2008

Пустой класс Record и функция с плавающей точкой, которая (как правило) применяется к отдельной записи, - это подсказка, что вы не разработали свой класс должным образом.

class Record( object ):
    """Assuming rtda and pnl must exist."""
    def __init__( self ):
        self.da= 0
        self.rt= 0
        self.rtda= 0 # or whatever
        self.pnl= None # 
        self.sink = None # Not clear what this is
    def setPnl( self, node_prices ):
        # fill RT and DA prices from the hash retrieved above
        # calculate dependent values: RT-DA and PNL

Теперь ваш calculate_pnl( records, node_prices ) проще и правильно использует объект.

def calculate_pnl( records, node_prices ):
    for record in records:
        record.setPnl( node_prices )

Суть не в том, чтобы банально реорганизовать код небольшими способами.

Дело в следующем: Класс Инкапсулирует Ответственность .

Да, пустой класс обычно является проблемой. Это означает, что обязанности разбросаны где-то еще.

Аналогичный анализ имеет место для сбора записей. Это больше, чем простой список, поскольку коллекция в целом содержит операции, которые она выполняет.

«Запрос-преобразование-рендеринг» не совсем верно. У вас есть модель (класс записи). Создаются экземпляры модели (возможно, из-за запроса.) Объекты модели отвечают за свои собственные преобразования и обновления состояния. Возможно, они отображаются (или отображаются) каким-либо объектом, который проверяет их состояние.

Это тот шаг «Преобразование», который часто нарушает хороший дизайн, распределяя ответственность повсюду. «Преобразование» является пережитком не-объектного дизайна, где ответственность была туманной концепцией.

1 голос
/ 22 сентября 2008

Рассматривали ли вы использование ORM? SQLAlchemy довольно хорош, а Elixir делает его красивым. Это действительно может уменьшить количество стандартного кода, необходимого для работы с базами данных. Кроме того, многие из упомянутых gotchas уже обнаружены, и разработчики SQLAlchemy справились с ними.

0 голосов
/ 23 сентября 2008

В зависимости от того, сколько вы хотите сделать с данными, вам может не понадобиться заполнять промежуточный объект. Структура данных заголовка курсора позволит вам получить имена столбцов - немного самоанализа позволит вам создать словарь с парами col-name: value для строки. Вы можете передать словарь оператору%. Документы для модуля odbc объяснят, как получить метаданные столбца.

Этот фрагмент кода показывает применение оператора% таким образом.

>>> a={'col1': 'foo', 'col2': 'bar', 'col3': 'wibble'}
>>> 'Col1=%(col1)s, Col2=%(col2)s, Col3=%(col3)s' % a
'Col1=foo, Col2=bar, Col3=wibble'
>>> 
0 голосов
/ 22 сентября 2008

Использование ORM для приложения iPhone может быть плохой идеей из-за проблем с производительностью, вы хотите, чтобы ваш код работал как можно быстрее. Таким образом, вы не можете избежать стандартного кода. Если вы рассматриваете ORM, кроме SQLAlchemy, я бы порекомендовал Storm.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...