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 相关文章推荐
Python使用cx_Oracle模块将oracle中数据导出到csv文件的方法
May 16 Python
python简单实现基数排序算法
May 16 Python
Django中更新多个对象数据与删除对象的方法
Jul 17 Python
说一说Python logging
Apr 15 Python
python使用psutil模块获取系统状态
Aug 27 Python
对Python 简单串口收发GUI界面的实例详解
Jun 12 Python
Python获取好友地区分布及好友性别分布情况代码详解
Jul 10 Python
python 的 openpyxl模块 读取 Excel文件的方法
Sep 09 Python
Python 格式化输出_String Formatting_控制小数点位数的实例详解
Feb 04 Python
pytorch数据预处理错误的解决
Feb 20 Python
Python3实现打印任意宽度的菱形代码
Apr 12 Python
Python实现微信表情包炸群功能
Jan 28 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
php环境无法上传文件的解决方法
2014/04/30 PHP
php单例模式实现方法分析
2015/03/14 PHP
PHP缓存工具XCache安装与使用方法详解
2018/04/09 PHP
php+layui数据表格实现数据分页渲染代码
2019/10/26 PHP
php 命名空间(namespace)原理与用法实例小结
2019/11/13 PHP
详解PHP中curl_multi并发的实现
2020/06/08 PHP
CSS(js)限制页面显示的文本字符长度
2012/12/27 Javascript
javascript中打印当前的时间实现思路及代码
2013/12/18 Javascript
node.js中的console.warn方法使用说明
2014/12/09 Javascript
javascript实现画不相交的圆
2015/04/07 Javascript
基于jQuery滑动杆实现购买日期选择效果
2015/09/15 Javascript
js clearInterval()方法的定义和用法
2015/11/11 Javascript
bootstrap导航栏、下拉菜单、表单的简单应用实例解析
2017/01/06 Javascript
javaScript 逻辑运算符使用技巧整理
2017/05/03 Javascript
input type=file 选择图片并且实现预览效果的实例
2017/10/26 Javascript
Vue 电商后台管理项目阶段性总结(推荐)
2020/08/22 Javascript
Python制作数据导入导出工具
2015/07/31 Python
python绘制铅球的运行轨迹代码分享
2017/11/14 Python
Django添加KindEditor富文本编辑器的使用
2018/10/24 Python
numpy.linspace函数具体使用详解
2019/05/27 Python
解决python xx.py文件点击完之后一闪而过的问题
2019/06/24 Python
详解Python文件修改的两种方式
2019/08/22 Python
django 数据库连接模块解析及简单长连接改造方法
2019/08/29 Python
Python matplotlib绘制图形实例(包括点,曲线,注释和箭头)
2020/04/17 Python
解决python运行效率不高的问题
2020/07/20 Python
matplotlib subplot绘制多个子图的方法示例
2020/07/28 Python
俄罗斯披萨、寿司和面食送货到家服务:2 Берега
2019/12/15 全球购物
银行实习的自我鉴定
2013/12/10 职场文书
开展党的群众路线教育实践活动个人对照检查材料
2014/11/05 职场文书
2015年度村委会工作总结
2015/04/29 职场文书
商场圣诞节活动总结
2015/05/06 职场文书
初中运动会前导词
2015/07/20 职场文书
2016年秋季新学期致辞
2015/07/30 职场文书
基于Redis延迟队列的实现代码
2021/05/13 Redis
Beekeeper Studio开源数据库管理工具比Navicat更炫酷
2022/06/21 数据库
Docker安装MySql8并远程访问的实现
2022/07/07 Servers