Python的动态重新封装的教程


Posted in Python onApril 11, 2015

让我们描绘一下本文的情节:假设您要在本地机器上运行一个进程,而部分程序逻辑却在另一处。让我们特别假设这个程序逻辑会不时更新, 而您运行进程时,希望使用最新的程序逻辑。有许多方法可以满足刚提到的要求;本文将向您说明其中几种方法。

随着“可爱的 Python”专栏不断进行,已经讨论了我的公共域实用程序 Txt2Html 的正在进行的增强。该实用程序将“智能 ASCII”文本文件转换成 HTML。以前的文章讨论了实用程序的 Web 代理版本和实用程序的 curses 界面。同样,我偶尔注意到可以用更有效的方法转换某些 ASCII 标记,或者解决了一个在处理某个特殊标记结构中的错误。

事实上,本专栏的文章都是用 ASCII 编写的,然后在编辑过程中转换成您可以阅读的 HTML 格式。在发表文章草稿之前,我运行了类似以下处理的程序:
文章的命令行 HTML 化

txt2html charming_python_7.txt > charming_python_7.html

如果愿意,我可以指定一些标志来修改操作;但不管怎样,事实上转换器的最新版本在我的本地驱动器和路径中。如果在另一台机器上工作,或者对于要使用该实用程序的读者,则过程比较麻烦:请访问我的网站,注意比较版本号和文件日期(有时更改太小,我不会更改版本号),下载当前版本、将当前版本复制到正确目录,然后运行命令行转换器。(请参阅本文后面的 参考资料。)

以上的过程包括几个需要手工操作且比较费时的步骤。应该更简单,而且可以做到这点。
命令行 Web 访问

大多数人认为 Web 是在 GUI 环境中交互式浏览页面的一种方法。那样做当然很好,但命令行中也有许多功能。带文本模式 Web 浏览器 lynx 的系统完全可以将整个 Web 看作是命令行工具使用的另一个文件集。例如,我发现有些命令很有用:
使用 lynx 进行命令行 Web 浏览

lynx -dump http://gnosis.cx/publish/.
lynx -dump http://ibm.com/developerworks/. > ibm_developer.txt
lynx -dump http://gnosis.cx/publish | wc | sed "s/( *[0-9]* *\)\([0-9]*\)\(.*\)/\2/g"

第一行说:“将 David Mertz 的主页(以 ASCII 文本)显示到控制台。”第二行说:“将 IBM 的当前 developerWorks 主页的 ASCII 版本保存到文件。”第三行示例说:“显示 David 主页的字数。”(不必担心细节,它只显示与管道结合的命令行工具。)

关于 lynx,有一点要注意它(使用 -dump 选项时)执行几乎与 Txt2Html 完全相反的操作:前一种工具将 HTML 转换成文本;而后一种工具则转换成其它格式。但没有理由不使用与 lynx 一样流行的 Txt2Html。可以使用一个很短的 Python 脚本完成这个操作:
'fetch_txt2html.py' 命令行转换器

import sys
 from urllib import urlopen, urlencode
 if len(sys.argv) == 2:
  cgi = 'http://gnosis.cx/cgi/txt2html.cgi'
  opts = urlencode({'source':sys.argv[1], 'proxy':'NONE'})
  print urlopen(cgi, opts).read()
       else:
  print "Please specify URL for Txt2Html conversion"

要运行这个脚本,只要执行如下操作:

python fetch_txt2html.py http://gnosis.cx/publish/programming/charming_python_7.txt

这并没有向您提供本地 Txt2Html 处理的全部开关,但如有必要,添加它们也很容易。可以像使用任何命令行工具一样来输送和重定向输出。但是,在上述版本中,只能处理 URL 可以到达的数据文件,而不能处理本地文件。

实际上, fetch_txt2html.py 可以完成 lynx 不能完成的任务(Txt2Html 本身也不能):它不仅从 URL 取得数据源,而且还远程获取 程序逻辑 。如果使用 fetch_txt2html.py ,就 不必在本地机器上安装 Txt2Html;将(使用最新版本)远程调用处理,并且将把结果发送回来,就像运行的是本地进程。很棒吧?Txt2Html 的本地版本可以访问远程 URL,就像访问本地文件一样,但它还不能保证它自身是最新的……!

动态初始化

使用 fetch_txt2html.py 确保了在转换中始终使用最新的程序逻辑。但是,这个方法可以完成的另一件事情是将处理器(和内存)的需求转移给 gnosis.cx Web 服务器。此特殊进程的负载并不是特别高,但人们却很可能认为在客户机上处理的其它类型的进程会更有效且令人满意。

组织 Txt2Html 的方式 -- 也就是组织大多数程序的方式 -- 是用一些由各种实用函数提供的核心流量控制函数。尤其是这些实用函数是一些经常更新的函数;核心函数( main() 和一些其它函数)只有在做重大改写时才会变动。总而言之,在每个程序运行时有效更新的就是实用函数。其实,大部分情况下,主 Txt2Html 模块 dmTxt2Html 中的大多数函数就够了。
'd2h_textfuncs.py' 动态 Txt2Html 更新

"""Hot-pluggable replacement functions for Txt2Html""" 
     #-- Functions to massage blocks by type 
#def 
     Titleify(block):
    #def Authorify(block):
    # ... [more block massaging functions] ... 
#-- Utility functions for text transformation 
#def AdjustCaps(txt):
    #def capwords(txt):
    #def URLify(txt):
    def Typographify 
    (txt):
  
    # [module] names 
  r = re.compile(r
    ""'([\(\s'/">]|^)\[(.*?)\]([<\s\.\),:;'"?!/-])""" , re.M | re.S)
  txt = r.sub(
    '\\1<em><code>\\2</code></em>\\3' ,txt)
  
    # *strongly emphasize* words 
  r = re.compile(r
    ""'([\(\s'/"]|^)\*(.*?)\*([\s\.\),:;'"?!/-])""" , re.M | re.S)
  txt = r.sub(
    '\\1<strong>\\2</strong>\\3' , txt)
  
    # ... [more text massaging] ... 
     
     return 
     
     txt
    # ... [more text transformation functions] .....

要使用最新和最具体的支持模块,需要一些准备步骤。首先,将主 Txt2Html 模块下载到本地系统(这是一次性步骤)。其次,在本地系统上创建类似于以下示例的 Python 脚本:
'dyn_txt2html.py' 命令行转换器

from 
     dmTxt2Html 
    import 
     *   
    # Import the body of 'Txt2Html' code 
    
from 
     urllib 
    import 
     urlopen
    import 
     sys
    # Check for updated functions (fail gracefully if not fetchable) 
    
try 
    :
  updates = urlopen(
    'http://gnosis.cx/download/t2h_textfuncs.py' ).read()
  fh = open(
    't2h_textfuncs.py' , 
    'w' )
  fh.write(updates)
  fh.close()
    except 
    :
  sys.stderr.write(
    'Cannot currently download Txt2Html updates' )
    # Import the updated functions (if available) 
    
try 
    :
  
    from 
     t2h_textfuncs 
    import 
     *
    except 
    :
  sys.stderr.write(
    'Cannot import the updated Txt2Html functions' )
    # Set options based on runmode (shell vs. CGI) 
    
if 
     len(sys.argv) >= 2:
  cfg_dict = ParseArgs(sys.argv[1:])
  main(cfg_dict)
    else 
    :
  
    print"Please specify URL (and options) for Txt2Html conversion"

在 dyn_txt2html.py 脚本中,请注意当执行 from t2h_textfuncs import * 语句时,所有以前在 dmTxt2Html 中定义的函数(如 Typographify() )都将由 t2h_textfuncs 版本的同名函数替换。当然,如果 t2h_textfuncs 的函数被注释掉了,则不会被替换。

有件小问题得注意,不同的系统以不同的方式处理写入 STDERR。在类 UNIX 系统中,运行脚本时可以重定向 STDERR;但是在当前 OS/2 外壳和 Windows/DOS 中,STDERR 消息将附加到控制台输出。您也许要将以上的错误/警告写到日志文件中,或者只习惯于将 STDOUT 定向到文件(可能会更有用)。例如:
'dyn_txt2html' 的命令行会话

G:\txt2html> python dyn_txt2html.py test.txt > test.html
Cannot currently download Txt2Html updates

错误转至控制台;经转换的输出转至文件。

一件更有趣的事情是 dyn_txt2html.py 为什么不下载整个 dmTxt2Html 模块,而仅下载支持模块。当然这是有理由的。 t2h_textfuncs 支持模块远远小于主 dmTxt2Html 模块,特别是因为大多数函数已经过删节/被注释掉。在调制解调器连接上,它的速度明显快很多。但下载大小并不是主要原因。

对于 Txt2Html,如果用户自动下载整个最新模块也没关系。但程序逻辑是 分布式 的系统(特别是维护责任也是分布式的)会发生什么情况呢?您也许会让 Alice、Bob 和 Charlie 分别负责模块 Funcs_A 、 Funcs_B 和 Funcs_C 。他们每个人都对他们负责的函数进行定期(且独立)更改,并将最新和最好的版本上传到他们自己的网站(如 http://alice.com/Funcs_A.py)。在这种情况下,让三个程序员都更改同一个主模块不太可行。但可以直接扩展类似于 dyn_txt2html.py 的脚本以在启动时尝试导入 Funcs_A 、 Funcs_B 和 Funcs_C (如果不能获取这些资源,则会退到 MainProg 版本)。

长期运行的动态进程

迄今为止,我们研究的工具已经通过在初始化时下载更新资源而获得了动态程序逻辑。这对于命令行处理或批处理很有意义,但对于长期运行的应用程序又会怎样。这种长期运行的应用程序最可能是一些不断响应客户机请求的服务器进程。但是在这个案例中,我们将使用为 以前的文章 开发的 curses_txt2html.py 来说明 Python 的 reload() 函数。程序 curses_txt2html 是 dmTxt2Html 本地副本的封装器。这里并不是第二次提到 curses 编程,谈一下 curses_txt2html 提供了一组交互式菜单以配置和运行多个连续的 Txt2Html 转换也足够了。

curses_txt2html 可以一直在后台运行,当切换到它的会话并运行转换时,我们希望它能够使用最新的程序逻辑。对于这个特定的简单示例,关闭和重新启动应用程序并不难,并不会带来特别的损害。但这很容易令人联想到其它一直运行着的进程(可能是说明会话中所执行操作状态的进程)。

在本文中,添加了新的 File/Update 子菜单。它被激活时只调用新的函数 update_txt2html() 。除了与提供发生的确认相关的 curses 调用之外,我们已经在本文的其它示例中看到过这些步骤:
'curses_txt2html.py' 动态更新函数

def update_txt2html 
    ():
  
    # Check for updated functions (fail gracefully if not fetchable) 
  s = curses.newwin(6, 60, 4, 5)
 s.box()
  s.addstr(1, 2, 
    "* PRESS ANY KEY TO CONTINUE *" , curses.A_BOLD)
 s.addstr(3,2, 
    "...downloading..." )
 s.refresh()
  
    try 
    :
    
    from 
     urllib 
    import 
     urlopen
    updates = urlopen(
    'http://gnosis.cx/download/dmTxt2Html.py' ).read()
    fh = open(
    'dmTxt2Html.py' , 
    'w' )
    fh.write(updates)
    fh.close()
 s.addstr(3,2, 
    "Module [dmTxt2Html] downloaded to current directory" )
  
    except 
    :
 s.addstr(3,2, 
    "Download of updated [dmTxt2Html] module failed!" )
  reload(dmTxt2Html)
  s.addstr(4, 2, 
    "Module [dmTxt2Html] reloaded from current directory " )
 s.refresh()
 c = s.getch()
   s.erase()

dyn_txthtml.py 和 update_txt2html() 函数之间有两个重要差异。其中一个差异是继续操作,并导入主 dmTxt2Html 模块而不只导入支持函数。这主要是简化了导入。这里的问题是我们使用 import dmTxt2Html 来访问模块,而不是 from dmTxt2Html import * 。从许多方面考虑,这是一个更安全的过程,但结果是使覆盖 dmTxt2Html 中的函数变得更困难(不论是无心地还是故意地)。如果我们要从 d2h_textfuncs 附加函数,则必须对导入的支持模块执行 dir() ,并将成员以属性形式附加到 "dmTxt2Html" 名称空间。执行这种样式的覆盖是留给读者的练习。

update_txt2html() 函数带来的最主要差异是 Python 的内置 reload() 函数的用法。只执行全新的 import dmTxt2Html 将 不 会覆盖以前导入的函数。请密切注意这一点!许多初学者认为重新导入模块将更新内存中的版本。这是错的。实际上,更新模块中函数的内存映像的方法是 reload() 模块。

以上示例中还执行了另一个小技巧。更新 dmTxt2Html 模块的下载位置是本地工作目录,而这个目录可能是(也可能不是)原来装入 dmTxt2Html 的目录。事实上,如果它在 Python 库目录中,那么您也许不在该目录中使用(也许对它没有用户许可权)。但 reload() 调用尝试先从当前目录装入,然后再尝试 Python 路径的其余部分。所以,不论下载是否成功, reload() 应该是一个安全的操作(虽然它可能装入新的模块,也可能不装入)。

Python 相关文章推荐
Ubuntu 下 vim 搭建python 环境 配置
Jun 12 Python
Python中Scrapy爬虫图片处理详解
Nov 29 Python
python数据抓取分析的示例代码(python + mongodb)
Dec 25 Python
python入门教程 python入门神图一张
Mar 05 Python
tensorflow构建BP神经网络的方法
Mar 12 Python
python根据url地址下载小文件的实例
Dec 18 Python
pandas 把数据写入txt文件每行固定写入一定数量的值方法
Dec 28 Python
在Pandas中处理NaN值的方法
Jun 25 Python
python实现kNN算法识别手写体数字的示例代码
Aug 16 Python
详解Python中打乱列表顺序random.shuffle()的使用方法
Nov 11 Python
Prometheus开发中间件Exporter过程详解
Nov 30 Python
python中HTMLParser模块知识点总结
Jan 25 Python
简单的Python的curses库使用教程
Apr 11 #Python
详解Python中的文本处理
Apr 11 #Python
状态机的概念和在Python下使用状态机的教程
Apr 11 #Python
在Python下使用Txt2Html实现网页过滤代理的教程
Apr 11 #Python
详解Python中DOM方法的动态性
Apr 11 #Python
将Python中的数据存储到系统本地的简单方法
Apr 11 #Python
Python中的进程分支fork和exec详解
Apr 11 #Python
You might like
手把手教你使用DedeCms的采集的图文教程
2007/03/11 PHP
用PHP读取超大文件的实例代码
2012/04/01 PHP
php变量作用域的深入解析
2013/06/03 PHP
使用 PHPStorm 开发 Laravel
2015/03/24 PHP
php实现通过ftp上传文件
2015/06/19 PHP
Iframe thickbox2.0使用的方法
2009/03/05 Javascript
JavaScript Scoping and Hoisting 翻译
2012/07/03 Javascript
利用JQuery和JS实现奇偶行背景颜色自定义效果
2012/11/19 Javascript
Javascript 按位取反运算符 (~)
2014/02/04 Javascript
JavaScript中document对象使用详解
2015/01/06 Javascript
举例讲解JavaScript substring()的使用方法
2015/11/09 Javascript
JS延时器提示框的应用实例代码解析
2016/04/27 Javascript
微信小程序http连接访问解决方案的示例
2018/11/05 Javascript
一些手写JavaScript常用的函数汇总
2019/04/16 Javascript
uni-app微信小程序登录授权的实现
2020/05/22 Javascript
javascript实现下拉菜单效果
2021/02/09 Javascript
用Python实现web端用户登录和注册功能的教程
2015/04/30 Python
基于Linux系统中python matplotlib画图的中文显示问题的解决方法
2017/06/15 Python
使用python实现http及ftp服务进行数据传输的方法
2018/10/26 Python
实时获取Python的print输出流方法
2019/01/07 Python
python导入pandas具体步骤方法
2019/06/23 Python
python各层级目录下import方法代码实例
2020/01/20 Python
python 使用tkinter+you-get实现视频下载器
2020/11/17 Python
css3 box-shadow阴影(外阴影与外发光)图示讲解
2017/08/11 HTML / CSS
Css3新特性应用之形状总结
2016/12/08 HTML / CSS
中邮全球便购:中国邮政速递物流
2017/03/04 全球购物
美国一家主打母婴用品的团购网站:zulily
2017/09/19 全球购物
《匆匆》教学反思
2014/02/22 职场文书
市场营销大学生职业规划书
2014/02/25 职场文书
电焊工岗位工作职责
2014/07/09 职场文书
担保书范文
2015/01/20 职场文书
上课迟到检讨书
2015/05/06 职场文书
企业催款函范本
2015/06/24 职场文书
Python趣味挑战之用pygame实现简单的金币旋转效果
2021/05/31 Python
Python办公自动化解决world文件批量转换
2021/09/15 Python
7个关于Python的经典基础案例
2021/11/07 Python