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设置tmpfs来加速项目的教程
Apr 17 Python
介绍Python中的文档测试模块
Apr 28 Python
Python爬取三国演义的实现方法
Sep 12 Python
pyecharts绘制中国2020肺炎疫情地图的实例代码
Feb 12 Python
python+Selenium自动化测试——输入,点击操作
Mar 06 Python
使用Python将图片转正方形的两种方法实例代码详解
Apr 29 Python
Python  word实现读取及导出代码解析
Jul 09 Python
python一些性能分析的技巧
Aug 30 Python
Python模拟登录和登录跳转的参考示例
Oct 30 Python
python中turtle库的简单使用教程
Nov 11 Python
详解Pytorch显存动态分配规律探索
Nov 17 Python
python爬取招聘要求等信息实例
Nov 20 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
我的论坛源代码(九)
2006/10/09 PHP
PHP实现的简单网络硬盘
2015/07/29 PHP
php采用session实现防止页面重复刷新
2015/12/24 PHP
JS 拼图游戏 面向对象,注释完整。
2009/06/18 Javascript
Javascript数组的排序 sort()方法和reverse()方法
2012/06/04 Javascript
推荐一款jQuery插件模板
2015/01/09 Javascript
javascript表格的渲染组件
2015/07/03 Javascript
JS生成某个范围的随机数【四种情况详解】
2016/04/20 Javascript
easyui validatebox验证
2016/04/29 Javascript
基于Jquery插件Uploadify实现实时显示进度条上传图片
2020/03/26 Javascript
Bootstrap 最常用的JS插件系列总结(图片轮播、标签切换等)
2016/07/14 Javascript
微信小程序 石头剪刀布实例代码
2017/01/04 Javascript
JS中的多态实例详解
2017/10/15 Javascript
vue中实现methods一个方法调用另外一个方法
2018/02/08 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
通过javascript实现扫雷游戏代码实例
2020/02/09 Javascript
详解JS预解析原理
2020/06/16 Javascript
vue print.js打印支持Echarts图表操作
2020/11/13 Javascript
Python用threading实现多线程详解
2017/02/03 Python
使用tensorflow实现线性回归
2018/09/08 Python
python从list列表中选出一个数和其对应的坐标方法
2019/07/20 Python
解决Python设置函数调用超时,进程卡住的问题
2019/08/08 Python
Django 自定义权限管理系统详解(通过中间件认证)
2020/03/11 Python
美国指甲油品牌:Deco Miami
2017/01/30 全球购物
澳大利亚最受欢迎的美发和美容在线商店:Catwalk
2018/12/12 全球购物
戴森英国官网:Dyson英国
2019/05/07 全球购物
Timberland俄罗斯官方网上商店:全球领先的户外品牌
2020/03/15 全球购物
专营店会计助理岗位职责
2013/11/29 职场文书
互联网创业计划书的书写步骤
2014/01/28 职场文书
大学秋游活动方案
2014/02/11 职场文书
高三毕业寄语
2014/04/10 职场文书
我们的节日清明节活动总结
2014/04/30 职场文书
三孔导游词
2015/02/05 职场文书
2015年监理工作总结范文
2015/04/07 职场文书
收入证明怎么写
2015/06/12 职场文书
用Python爬取英雄联盟的皮肤详细示例
2021/12/06 Python