在Python中操作数据库结果集的最佳实践?

时间:2008-09-22 19:18:21

标签: python database

我正在编写一个简单的Python Web应用程序,其中包含为iPhone格式化的多页业务数据。我很熟悉Python编程,但我不太熟悉Python“习惯用法”,特别是关于类和对象。 Python的面向对象设计与我使用过的其他语言略有不同。所以,即使我的应用程序正在运行,我也很好奇是否有更好的方法来实现我的目标。

细节:如何通常在Python中实现请求转换渲染数据库工作流?目前,我使用pyodbc获取数据,将结果复制到对象的属性,执行一些计算并使用这些对象的列表进行合并,然后从对象列表中呈现输出。 (下面的示例代码,SQL查询编辑。)这是否理智?有没有更好的办法?在我相对无知的Python中,是否有任何特定的“陷阱”我偶然发现了?我特别关注如何使用空的“Record”类实现行列表。

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 个答案:

答案 0 :(得分:1)

您是否考虑过使用ORM? SQLAlchemy非常好,Elixir让它变得美丽。它确实可以减少处理数据库所需的样板代码量。此外,提到的很多 gotchas 已经出现,而且SQLAlchemy开发人员已经处理过它们。

答案 1 :(得分:1)

空的Record类和(通常)适用于单个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 )

关键是不要以微小的方式轻易地重构代码。

关键在于: A类封装责任

是的,一个空洞的类 通常是个问题。这意味着责任分散在其他地方。

类似的分析适用于记录的收集。这不仅仅是一个简单的列表,因为集合 - 作为一个整体 - 具有它执行的操作。

“Request-Transform-Render”不太对劲。你有一个Model(Record类)。构建模型的实例(可能是因为Request。)Model对象负责自己的状态转换和更新。也许它们会被一些检查其状态的对象显示(或渲染)。

这就是“变换”步骤,通常会在各处散布责任,从而违反良好的设计。 “变形”是非对象设计的延续,其中责任是一个模糊的概念。

答案 2 :(得分:0)

根据您对数据的处理程度,您可能不需要填充中间对象。游标的标题数据结构将允许您获取列名称 - 一些内省将允许您创建具有列名称的字典:行的值对。 您可以将字典传递给%运算符。 odbc模块的文档将解释如何获取列元数据。

这段代码以这种方式显示%运算符的应用程序。

>>> a={'col1': 'foo', 'col2': 'bar', 'col3': 'wibble'}
>>> 'Col1=%(col1)s, Col2=%(col2)s, Col3=%(col3)s' % a
'Col1=foo, Col2=bar, Col3=wibble'
>>> 

答案 3 :(得分:-2)

对于iPhone应用程序使用ORM可能是一个坏主意,因为性能问题,您希望代码尽可能快。所以你无法避免样板代码。如果您正在考虑使用ORM,除了SQLAlchemy,我还会推荐Storm。