采用Pythonic 方法
在前面部分中编写的代码结构与采用 Pythonic 方法实现的代码看起来不太相同。尤其是,您按照一定的顺序实现了一组将从在 HTML 中嵌入的代码调用的函数,将一个函数返回的结果用作另一个函数的参数。实际上,这是采用任何其他脚本语言(比如说 PHP)结构化您的代码的方式。
尽管 Python 的真正功能在于它能够隐藏令人厌烦的实现详细信息,从而提供一个简单、优美而有效的编码解决方案。字典、列表和列表内涵是常用的 Python 内置类型,在处理结构化数据时可以显著简化您的代码。返回在前面部分中讨论的 oraclepersist.py 脚本,对其进行升级,以便最大程度地利用这些杰出的 Python 语言工具。为了避免混淆,您可以将修订保存在一个单独的名为 oraclepersist_list.py 的文件中:
import urllib2
import xml.dom.minidom
url = 'http://feeds.delicious.com/v2/rss/OracleTechnologyNetwork/otntecharticle'
inxs = ['title','guid','pubDate','link']
num = 3
def getRSS(addr):
xmldoc = xml.dom.minidom.parseString(urllib2.urlopen(addr).read())
items = xmldoc.getElementsByTagName('item')
return items
articles = [dict(zip(inxs,[item.getElementsByTagName(inx)[0].firstChild.data for inx in inxs])) for index, item in enumerate(getRSS(url)) if index < num]
def processClick(guid, clks = 1):
db = cx_Oracle.connect('usr', 'pswd', '127.0.0.1/XE')
c = db.cursor()
c.execute('''call count_clicks(:guid, :clks)''', {'guid':guid, 'clks':clks})
db.close()
从以上代码可以看出,利用列表内涵(一种非常有效的结构化应用程序数据的机制)可以显著减少代码总量。此外,客户端也不必显式调用模块函数。因此,您现在可以重新编写按照前面部分所述嵌入在 clicktrack.psp 中的 Python 代码块,如下所示:
<%import oraclepersist_list
for article in oraclepersist_list.articles:
%>
...
尽管现在它更为简洁,但用户不需要进行任何更改。
但是,有人可能会说将 PSP 页面中的代码与其后端连接实在不是一个灵活的方法。例如,将要显示的链接数量以及要使用的 RSS 地址硬编码到 oraclepersist_list.py 脚本中,借助这个新的语法,您无法根据需要动态更改这些参数。要解决此问题,可以将列表内涵封装在 oraclepersist_list.py 脚本中的某个函数中,如下所示:
def getLatestItems(num = 3, url = 'http://feeds.delicious.com/v2/rss/OracleTechnologyNetwork/otntecharticle'):
inxs = ['title','guid','pubDate','link']
return [dict(zip(inxs,[item.getElementsByTagName(inx)[0].firstChild.data for inx in inxs])) for index, item in enumerate(getRSS(url)) if index < num]
...
正如您所见,以上代码仍然利用了基于使用列表内涵、列表和字典的高级语法,从而允许在 clicktrack.psp 页面中动态更改参数。以下代码片段将阐释现在如何显式指定要显示的文章链接数量:
<%import oraclepersist_list
for article in oraclepersist_list.getLatestItems(5):
%>
...
使用面向对象的方法
尽管 Python 中的面向对象编程 (OOP) 是完全可选的,但利用该范例可以最大程度地减少冗余,高效地自定义现有代码。与其他现代语言一样,Python 允许您使用类封装逻辑和数据,简化了数据定义和数据操作。
回到在前面部分中讨论的 oraclepersist_list.py 脚本,将 processClick 函数替换为如下所示的 HandleClick 类:
class HandleClick:
def __init__(self, usrname='usr', password ='pswd', orcldb='127.0.0.1/XE'):
self.dbconn = cx_Oracle.connect(usrname, password, orcldb)
def __del__(self):
self.dbconn.close()
def processClick(self,guid,clks):
self.dbconn.cursor().execute('''call count_clicks(:guid, :clks)''', {'guid':guid, 'clks':clks})
假设您将修订保存在 oraclepersist_class.py 文件中,更新后的 dispatcher.psp 现在可能如下所示:
<%
import oraclepersist_class
import urllib
from mod_python import util
params = util.FieldStorage(req)
h = oraclepersist_class.HandleClick()
h.processClick(urllib.unquote_plus(params['guid'].value), 1)
psp.redirect(urllib.unquote_plus(params['url'].value))
%>
...
下面您创建 HandleClick 类的一个实例,然后调用它的 processClick 方法,正确传递参数,就像您之前所做的那样。
在此处所讨论的 HandleClick 类中,特别令人感兴趣的是特殊类方法 methods __init__ 和 __del__ 的使用。与其他特殊方法一样,您从不直接调用它们。相反,Python 隐式调用它们以响应在实例生命周期期间发生的某些事件。因此在创建实例时调用 __init__ 构造函数,在销毁实例之前调用 __del__ 析构函数。
在上面的示例中,您在构造函数中连接到数据库并在析构函数中关闭该连接。但在某些情况下,采用这些方法实现更多操作可能是非常令人感兴趣的。例如,您可能希望在销毁实例之前从析构函数中发出 SQL 语句。以下代码片段将阐释如何重新编写 HandleClick 类,以便从析构函数中而不是从某个显式调用的类方法中调用 count_clicks 存储过程:
class HandleClick:
def __init__(self, usrname='usr', password ='pswd', orcldb='127.0.0.1/XE'):
self.dbconn = cx_Oracle.connect(usrname, password, orcldb)
self.params ={}
def __del__(self):
self.dbconn.cursor().execute('''call count_clicks(:guid, :clks)''', self.params)
self.dbconn.close()
def addArticleClick(self,guid,clks):
self.params['guid']=guid
self.params['clks']=clks
正如您所见,更新的 HandleClick 类中不再有 processClick。相反,客户端代码应调用 addArticleClick,该函数用要传递给 count_clicks 存储过程的参数填充该类的属性 params dictionary,将从析构函数中调用 count_clicks 存储过程。因此,现在您可以重新编写嵌入在 dispatcher.psp 页面中的 Python 代码块,如下所示:
<%
import oraclepersist_class
import urllib
from mod_python import util
params = util.FieldStorage(req)
h = oraclepersist_class.HandleClick()
h.addArticleClick(urllib.unquote_plus(params['guid'].value), 1)
del h
psp.redirect(urllib.unquote_plus(params['url'].value))
%>
...
注意,此处使用 del 语句取消包含绑定对 HandleClick 类的某个实例的引用的 h 变量。由于这是对该实例的唯一引用,因此之后 Python 将使用一种名为垃圾回收的机制隐式删除该实例。删除后,将自动触发 __del__ 析构函数,执行 SQL 语句,然后关闭连接。
上面的示例极好地说明了采用 Python 开发面向对象的代码时使用特殊方法可以获取的优势。在这个特殊示例中,客户端代码只负责为要针对数据库发出的查询设置参数,而 Python 隐式执行其余操作。
结论
正如您在本文中所学到的,开发一个可扩展的数据库驱动的 Web 应用程序需要进行较良好的规划。继续构建应用程序组件和编写代码之前,您必须首先决定可以在数据库中实现的应用程序逻辑的数量以及可以在前端层实现的操作。
设计文章示例时,将一些数据处理逻辑放到数据库中,实现几个 PL/SQL 存储过程。在这里您学习了如何使用 Oracle XML DB 特性从网页中获取 XML 数据,然后从获取的 XML 文档中提取所需的信息。然后,构建一些 Python 代码,用以编排存储过程所执行的完整操作流。依次从构建的 PSP 页面中调用这些 Python 代码,以实现应用程序的前端层。因此,您获得了相应的应用程序,该应用程序从网页中获取某些实时数据,并跟踪用户在您站点上的活动,将该信息存储在数据库中。在 Python 端,您看到了如何使用 Python 语言的内置工具获取、保留以及操作结构化数据,这些工具包括:列表、字典和列表内涵。您还了解了在将应用程序逻辑和数据封装到类中时如何利用 Python 的面向对象的特性。