Chinaunix首页 | 论坛 | 博客
  • 博客访问: 54908
  • 博文数量: 5
  • 博客积分: 2127
  • 博客等级: 上尉
  • 技术积分: 135
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-19 21:20
文章分类

全部博文(5)

文章存档

2011年(5)

分类: Python/Ruby

2011-06-16 16:15:47

    Python有自己的DB-API,请参见:
问题:
  查询的结果集通过 Cursor 对象的 fetch* 方法集访问,或者直接在 Cursor 上迭代,因为 Cursor 本身是可迭代的。但返回的结果集都使用 tuple 来表示一行,tuple 需要通过索引访问,例如:
  1. >>>cursor = db.cursor();
  2. >>> # List all the seminars
  3. >>>cursor.execute('select * from Seminars')
  4. >>>cursor.fetchall()
  5.  [(4, 'Web Commerce', 300.0, 26), (1, 'Python Programming', 200.0, 15), (3, 'Socket Programming', 475.0, 7), (2, 'Intro to Linux', 100.0, 32), ]
通过索引访问存在危险,如果表的结构发生改变(在 select * 的情况下)或者 select 语句改了,很容易出错,例如有SQL:
  1. select first_name, last_name, age from person
Python 的访问代码差不多是这个样子:
  1. for row in cursor:
  2.     p = Person(firstName=row[0], lastName=row[1], age=row[2])
  3.     # Do something with p
如果 select 改动了,多取了性别出来(0 和 1 表示 男女),SQL 变成:
  1. select first_name, last_name, gender, age from person
可以看到 row[2] 不再是年龄,但程序不会出错,会把0/1当作年龄继续运行,会发生什么事?想想,你永远年轻,当然永远不能退休。
解决方案:
    SQLAlchemy 这样的中间件有完整的解决方案,它使用预定义的类来包装结果集,或者使用一个字典来表示一行,可以通过索引或列名类访问,可以想见 row['age'] 没有上面的问题。SQLAlchemy 请参见:
如果只是需要数据库查询的话,在 Python 2.6+ 中有更轻量级的方案,只需几行代码,速度也不受影响。
namedtuple 是 Python 2.6+ 中提供的增强的 tuple。只要申明一下,就可以通过名字访问 tuple 中的数据:
  1. >>> # Basic example
  2. >>> Point = namedtuple('Point', ['x', 'y'])
  3. >>> p = Point(11, y=22) # instantiate with positional or keyword arguments
  4. >>> p[0] + p[1] # indexable like the plain tuple (11, 22)
  5. 33
  6. >>> x, y = p # unpack like a regular tuple
  7. >>> x, y
  8. (11, 22)
  9. >>> p.x + p.y # fields also accessible by name
  10. 33
  11. >>> p # readable __repr__ with a name=value style
  12. Point(x=11, y=22)
namedtuple 请参见:

我们可以写一个函数如下:
  1. def refineDBResultSet(cursor, className = 'MuteRecord'):
  2.     '''
  3.     This method refined the result set returned by a DB cursor
  4.     Generates an new iterator derived from namedtuple
  5.     As namedtuple is introduced from python 2.6, so this method need python 2.6 or later.
  6.     @param curser: The DB cursor on which just executed a sql
  7.     @param className: The given name for the namedtuple
  8.     '''
  9.     fields = ','.join([f[0].lower() for f in cursor.description])
  10.     Mute = collections.namedtuple(className, fields)
  11.     return itertools.imap(Mute._make, cursor)
该函数接受一个 Cursor 和一个可选的 class 名字,返回一个 Iterator 的 Adapter (迭代器的适配器,有些拗口,可以看作另一个迭代器,只不过迭代出的数据做了转换)。那个 Mute._make 是新申明的 namedtuple 类的工厂函数,接受一个 tuple,输出相应的 namedtuple。正好被用来在适配器里转换数据。好了,来看看新的访问代码:
  1. for person in refineDBResultSet(cursor, 'Person'):
  2.     print(person.first_name)
  3.     print(person.last_name)
  4.     print(person.age)
  5.     # Do something with person
很简单,我们已经可以把一行看作一个Person对象来处理了,那个class名字'Person'可有可无。而且也不再有位置索引的困扰,即使select语句改变了,除非person.age没有了会报错,否则不会取错的。


阅读(2451) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~