2011年3月

在循环 dictionary(字典)型数据时,键与值(Key , Value)可以在一个 for 循环中使用 dictinary 的iteritems() 函数同时检索到,如下:

>>> knights = {'gallahad': 'the pure','robin':'the brave'}
>>> for k,v in knights.iteritems(): print k,v
...
gallahad the pure
robin the brave

当我们循环一个序列时,元素的位置与元素值本身也可以同时被检索到,这个时候我们就需要使用enumerate() 函数:

>>> seq = ['tic', 'tac', 'toe']
>>> for i,v in enumerate(seq): print i,v
...
0 tic
1 tac
2 toe

如果同时将两个序列进行检索,我们可以使用 zip() 函数将这两个序列进行配对,然后再在同一个 for 循环中检索:

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['costony', 'the holy grail', 'blue']
>>> for q,a in zip(questions, answers):
... print 'What is your {0}? It is {1}.'.format(q,a)
...
What is your name? It is costony.
What is your quest? It is the holy grail.
What is your favorite color? It is blue.

在Python中,有三个函数是我们编程过程中经常会使用到的,在很多时候可以帮助我们解决很多麻烦的事情,它们分别是:@filter(function, sequence)@,@map(function,sequence)@和@reduce(function,sequence)@。

filter(function,sequence)

将 sequence 中的所有元素都作为参数传递给 function 参数指定的函数,如果该函数返回 True,则将该元素追加到结果集中,当整个 sequence 全部遍历完了之后,返回结果集,示例如下(该示例将找到出所有不能同时被 2 和 3 整除的数:

>>> def f(x): return x % 2 != 0 and x % 3 != 0
...
>>> filter(f,range(2,25))
[5, 7, 11, 13, 17, 19, 23]

map(function, sequence)

该函数将 sequence 中的所有元素都传递给 function 所指定的函数,将处理得到的结果加入以 sequence 中元素的顺序加入到结果集中,并返回结果集,如下所示,将求出 sequence 序列中所有数的立方:

>>> def cube(x): return x*x*x
...
>>> map(cube, range(1,11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

reduce(function, sequence)

该函数将只返回一个结果,其处理方式是:首先是将 sequence 中的第 0 位 和第 1 位元素传递给 function 所指定的函数,得到的结果作为下一次运算的一个参数,并且把 sequence 中的下一位元素作为另一个参数,如此直至 sequence 的所有元素都参与了运算,之后返回结果,如下示例将求出序列中的所有数的总和:

>>> def add(x,y): return x+y
...
>>> reduce(add,range(1,11))
55

使用 lambda 函数

很多时候我们传递给上面所说的三个函数的 函数 都是非常简单的,这个时候我们可以使用的 lambda 函数,比如上面三个示例我们可以像下面这么写:

>>> filter(lambda x: x % 2 != 0 and x % 3 != 0, range(2,25))
[5, 7, 11, 13, 17, 19, 23]
>>> map(lambda x: x*x*x, range(1,11))
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
>>> reduce(lambda x,y:x+y, range(1,11))
55

而对于 lambda 函数,我们还可以把多个lambda 函数都保存到一个字典中,如下面这样的方法去使用:

>>> lfs = {'check':lambda x: x % 2 != 0 and x % 3 != 0,
... 'cube':lambda x: x*x*x, 'add': lambda x,y: x+y}
>>> filter(lfs['check'], range(2,25))
[5, 7, 11, 13, 17, 19, 23]
>>> filter(lfs['cube'], range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> reduce(lfs['add'], range(1,11))
55

SQLAlchemy 是下一代的 Python Object Relational 映射器,有了这个东西,我们就可以更少的接触数据库的底层了,我现在基本上都在使用Flask,而Flask 里面也有SQLAlchemy的封装:flaskext.sqlalchemy ,使得我们在开发Flask应用的时候更方便的操作数据库,这里就来记录一个最通用的程序数据库设计中使用Flask SQLAlchemy的示例。

整个程序代码如下(感觉我这里看着不方便,就把下面的代码保存到任何一个现代的IDE或者程序编辑器里面都应该是可以看得很清楚的,我这里是因为Textile会把空行当作代码的结束,所以只能全部写到一起了):

#!/usr/bin/env python
# encoding: utf-8
"""
models.py
Created by Pan Tao on 2011-09-02.
Copyright (c) 2011 CosTony.Com. All rights reserved.
"""
from flask import Flask
from flaskext.sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/models.db'
app.config['SQLALCHEMY_BINDS'] = {
'user' : 'sqlite:////tmp/user_db.db',
'appmeta' : 'sqlite:////tmp/appmeta.db'
}
db = SQLAlchemy(app)
tags = db.Table('tags',
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id')),
db.Column('post_id', db.Integer, db.ForeignKey('post.id'))
)
class Role(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(20), unique = True)
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Role %r>' % self.name
class User(db.Model):
__bind_key__ = 'user'
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.String(80), unique = True)
email = db.Column(db.String(120), unique = True)
role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
role = db.relationship('Role',
backref = db.backref('users', lazy='dynamic'))
def __init__(self, username, email, role):
self.username = username
self.email = email
self.role = role
def __repr__(self):
return '<User %r>' % self.username
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.String(80))
body = db.Column(db.Text)
pub_date = db.Column(db.DateTime)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'),info={'bind_key': 'user'})
author = db.relationship('User',
backref=db.backref('posts', lazy='dynamic'))
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category',
backref=db.backref('posts', lazy='dynamic'))
tags = db.relationship('Tag', secondary=tags,
backref = db.backref('posts', lazy='dynamic'))
def __init__(self, title, body, category, tags, author, pub_date = None):
self.title = title
self.body = body
if pub_date is None:
pub_date = datetime.utcnow()
self.pub_date = pub_date
self.category = category
self.tags = tags
self.author = author
def __repr__(self):
return '<Post %r>' % self.title
class Category(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(50))
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Category %r>' % self.name
class Tag(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(20), unique = True)
def __init__(self,name):
self.name = name
def __repr__(self):
return '<Tag %r>' % self.name
def init_db():
db.drop_all()
db.create_all()

最开始,我们把需要使用的一些模块都导入进来,在这里,我导入了Flask, flaskext.sqlalchemy 中的 SQLAlchemy(注意这个和原生的 SQLAlchemy有一点点不一样的),然后 datetime 中导入 datetime(这个其实不导入也没有关系,只需要把 Post 类中的创建时间的属性删除掉就行了。

然后我创建了一个Flask实例(这个在Flask SQLAlchemy中是必须的,需要我们在创建 FlaskSQLAlchemy 的时候不指定 Flask app,那我们也必须在之后使用 Flask SQLAlchemy 实例之前,初始化它的Flask app。

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/models.db'
app.config['SQLALCHEMY_BINDS'] = {
'user' : 'sqlite:////tmp/user_db.db',
'appmeta' : 'sqlite:////tmp/appmeta.db'
}

上面这一段代码中,我对 Flask app 进行的配置,配置项为下面两个:

  1. SQLALCHEMY_DATABASE_URI : 这个是标准的 SQLAlchemy 数据库地址字符串,我在这里使用的是 SQLite 数据来测试(对于这个Python内置的数据库用起来还真方便)
  2. SQLALCHEMY_BINDS :在这个设置里面,我设置了两个属性,一个是用来保存程序中 user 数据的数据库,我把 user 表单独于其它表放在另外一个数据库中了,这个其实在很小的程序里没有啥用,我这里只是为了尽可能把Flask SQLAlchemy的入门教程讲明白哈。appmeta 被保存在另一个数据库中,另外,当我们初始化数据库之后,还会再创建一个 models 数据库,这个是把其它未指定的列都放在这个数据库里面的。

接着我就来始定义我这个程序里面需要的几个类了:

  • Role :用户组
  • User :用户类
  • Post :文章类
  • Category :文件分类的类
  • Tag :标签

上面这些类的关系如下面这样的:

  • Role -> User :一对多
  • User -> Post :一对多
  • Category -> Post : 一对多
  • Tag -> Post : 多对多

在一对多的实例中,比如Role -> User中,User中有一个 role_id 属性来保存这个用户所属的用户组的ID,然后有一个关系申明,这样可以让Role 对象 role 通过 role.users访问到这个用户组下面所有的用户数据。

而对于多对多的情况,如 Tag -> Post,我在最开始创建了一个表:tags,这个表用来保存Tag 与 Post 之间的关联关系,然后需要在Post类中申明一个 secondary 属性,表示关系是保存在那个表里面的。

最后就是在User类里面有一个不一样的地方,就是有一个 __bind_key__ = 'user' 这个属性,这里其实就是申明这个类的数据要使用 key 为 ”user“ 的数据库,这样的话,我们就需要在与 User 这个类有关联的类中,显示的申明 User 类的’bind_key’为’user‘。

最后面的那一个 init_db() 函数是用来初始化数据库的,如果未修改我代码里面的配置,那么在系统的 /tmp 目录下将会生成下面这些文件:

/tmp
/appmeta.db
/user_db.db
/models.db

大体上就这样的吧,反正自己以后再来看这个我还能看懂,所以就这样了吧^

在 Python 中有这么一个 property 函数,一直没弄明白是怎么回事儿,今天就好好的研究了一下下,我的理解是这样的,如下面这一段代码:

class C(object):
def __init__(self): self._x = 1
def getx(self): return self._x * 2
def setx(self,value): self._x = value/2
def delx(self): self._x = 1
x = property(getx,setx,delx,"I'm the property.")

上面这一段代码的最后一行用了一个 @property@ 函数,这一段代码的意思是:当我们给 class: C(object) 的实例的成员 x 赋值时,需要首先调用 setx(self,value) 函数,取其值时,需要先调用getx(self) 函数,删除它时,需要先调用 delx(self) 函数。

所以我们会有下面这样的运行结果:

>>> c = C()
>>> c.x
2
>>> c.x = 3
>>> c.x
2
>>> c._x
1
>>> del c.x
>>> c.x
2
>>>

我创建了一个 C 的实例,名为 ”c“,当我去调用 c.x 时,首先运行了 getx 函数,所以返回值是 1 * 2 ,也就是 2 了,而我给 c.x 赋值为 3,这个时候会运行 setx 函数,所以这个时候 c._x 的值变为了 3 / 2 ,其运算结果是 1,所以我再一次取 c.x 的值时,出现的并不是 3,而是 1 * 2 = 2。而我最后面删除 c.x ,但是并没有删除掉,而是重新为 c._x 赋值。

以前只知道使用下面这样的代码来创建我的Flask应用:

from flask import Flask
app = Flask(__name__)

不过从下面的Flask的 init 函数可以看出,其实还有好几个属性是可以在创建应用的时候就指定的:

 def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates'):
_PackageBoundObject.__init__(self, import_name,
template_folder=template_folder)
if static_path is not None:
from warnings import warn
warn(DeprecationWarning('static_path is now called '
'static_url_path'), stacklevel=2)
static_url_path = static_path
if static_url_path is not None:
self.static_url_path = static_url_path
if static_folder is not None:
self.static_folder = static_folder
....

从上面的代码可以看出,在标准的 Flask 应用目录结构上面还是可以修改的,标准的目录结构如下:

/application
/app.py
/static
/style.css
/....
/templates
/layout.html
/index.html
/....

如果是这样的话,那么在使用@url_for(‘static’,filename=‘style.css’)@函数时,Flask会发送上面static目录下style.css文件的内容,但是如果我们不想把静态文件放在 static 这个目录下,而是 misc 这个目录下面,那么可以是这样的创建Flask 应用

app = Flask(__name__,static_folder='misc')

这个时候,@url_for(‘static’,filename=‘style.css’)@ 这个函数发送的文件将是 /misc/style.css 。同样,我们也可以这样指定 templates 的目录为其它的或者更加适合自己的风格的,当然咯,做为一个Python程序员,不建议把个人风格带到程序里面去。