ChinesePython Wiki   Python FAQ4 用户设定
 
站内导航 Diffs Info Edit RAW Print
 ChinesePython Wiki   中蟒大杂院   最近修改   标题目录   看发表区   搜寻/发表   站内导航   求助 

  1. 4. Python 编程
    1. 4.1 有没有源码层次的除错器可用?
    2. 4.3. 有没有 curses/termcap 的包给 Python 用 ?
    3. 4.5 已删除
    4. 4.6. 我怎样从反方向步进一个序列?
    5. 4.8. 重新载入模组 reload() 的问题
    6. 4.9 怎样找到目前使用中的模块名称?
    7. 4.10. 模组以脚本执行时可否运行额外的程式码?
    8. 4.14. 有没有给 Python 用的资料库介面?
    9. 4.15. 用 Python 能不能写出那些故弄玄虚让人看不明白的一行过程式?
    10. 4.16. 是否可能模拟 C 的三位操作号 "?:" ?
    11. 4.29. Python 有哪些 WWW 的工具 ?
    12. 4.33. 有没有象 scanf(), sscanf() 这样的东西 ?
    13. 4.36. 请解释全局变数和本地变数
    14. 4.38. 如何复制对象?
    15. 4.41. 如果删除档案? 及其他有关档案的操作
    16. 4/42. 要让 urllib, httplib 支援 HTTP/1.1, 该做哪些修改 ?
    17. 4.43. compile() 及 exec 都出些无法解释的语法错误
    18. 4.44. 怎样把字串变成数字 ?
    19. 4.45. 如何把数字转换成字串 ?
    20. 4.52. 元组 tuples 和 列表 lists 如何互换?
    21. 4.56. 讯号处理机制 signal handlers 用不了
    22. 4.67. 我想用 Python 产生一些随机数, 怎办?
    23. 4.68. 我该怎样才能存取串接口 (RS232) ?
    24. 4.79. 如何读写二进位数据?
    25. 4.85. __import__('x.y.x') 传回 <module 'x'>; 我怎样才得到 z?
    26. 4.92. 有没有关于 Python 的指南级教程?
    27. 4.93. 已取消
    28. 4.94. 怎样才做到每次一有按键就读取, 不必等按 Enter ?
    29. 4.101. 有没有工具可以帮忙找出程式中的错误及做程式码分析?
    30. 4.104. 异常(exceptions) 够快吗?
    31. 4.105. 怎样在模块之间建立共享的全局变量?
    32. 4/106. 为何 cPickle 很慢?

4. Python 编程

4.1 有没有源码层次的除错器可用?

有模块 pdb 可用, 说明文件在 Library Reference Manual 中有。 此模块简单但足堪使用。 你也可以依它的写法来创造自己的除错器。

Pythonwin 有它自己一个图形接口的除错工具,是建基于 bdb 上的。 它有彩色的断点显示和其个挺不错的功能 (包括帮非 Pythonwin 的程序除错). 尽管它的接口还有需要改进的地方, 但它无疑是非常有趣的产品. 使用方法可参考 Pythonwin 自带的档: http://www.python.org/ftp/python/pythonwin/pwindex.html

在 ActivePython 中带有最新的 Pythonwin 版本: http://www.activestate.com/Products/ActivePython/index.html

Richard Wolff 写了一个改良版的 pdb, 叫做 Pydb. 它是和流行的 Data Display Debugger (DDD) 配合来用的. Pydb 在 http://daikon.tuc.noao.edu/pythyon/ , 而 DDD 则位于 http://www.cs.tu-bs.edu.softech/ddd/

Python 本身的 IDLE 编程环境中也有一个图形接口的除错器. 一般的下载版本中它位于 Tools/idle 目录中.

4.3. 有没有 curses/termcap 的包给 Python 用 ?

标准的源码发布中有一个 curses 模组, 它在 Modules/ 目录中. 不过预设的配置不会编译这个模组. (注意此它模组无法在 Windows 下使用)

Python2.0 以前只能用 plain curses; ncurses 的一些特殊功能如颜色等不能用.(虽然这个模组其实是使用 ncurses)

Python2.0 开始, 引入 Oliver Andrich 的新版本, curses 模组功能增强很多. 从 ncurses 和 SYSV curses 中引进了象颜色, 字符集, 鼠標等的支援. 这代表光有 BSD curses 的系统已不可使用这个模组了, 不过好象已没有任何"活"的系统还属于这个类别了.

== 4.4 在 Python 中有没有和 C 语言的 onexit() 相对应函数 ? 版本 2.0: 新加的 atexit 模姐提供了和 onexit 相似的功能. 详情请参阅函数库的参考手册. 在 2.0 版本中你不应该像 1.5.2 版那样覆写一个新的 sys.exitfunc !

版本 1.5.2: 你需要加载 sys 模块并且覆写一个新的 sys.exitfunc 函数. 这个函数会当程序正常终止, 因遇到无法处理的异常而退出, 或是在 UNIX 系统下接收到 SIGNTERM 或 SIGHUP 讯号.

4.5 已删除

旧版 Python 不能处理嵌套式的程序代码段, 这条问题本来是说明相关的原委的.

4.6. 我怎样从反方向步进一个序列?

如果序列是一个列表的话, 最快的办法是
list.reverse() 
  try: 
    for x in list: 
      "干活" 
  finally: 
    list.reverse()
不过这个方法不好的地方是,在你"干活"的过程中, list 被反过来了. 如果你不喜欢, 你也可以复制一份. 唯然看起来浪费时间来复制列表, 但其实它比下面的方法都要快.

如果序列并非列表, 上面有一个更普遍但较慢的方法:

for i in range(len(sequence)-1, -1, -1): 
   x = sequence[i] 
   <do something with x>

还有个更优雅的解决方案: 定义一个序列类, 它会接收要反序的序列然后从末端逐一给出序列中的内容(方案由 Steve Majewski 提出):

class Rev: 
  def __init__(self, seq): 
   self.forw = seq 
  def __len__(self): 
   return len(self.forw) 
  def __getitem__(self, i): 
   return self.forw[-(i + 1)]

用起来很简单:

for x in Rev(list): 
  <do something with x>
可惜的是, 这个办法是所有方法中最慢的, 因为调用类的方法需要一定的时间...

4.8. 重新载入模组 reload() 的问题

当我加载一个模块后, 我对它的源码做了一些修改, 于是又加载一次 (在同一个 Python 程序中), 我发现好像模块的新改动并没有反映出来 ? 出了什么问题 ?

因为效率和归一上的考虑, Python 只会在模块第一次被加载时读取在磁盘上的相关模块文件. (想象一个程序有后多个模块, 而每个模块又会加载相同的一个共享模块, 如果每次都读一次档案的话会很耗资源. 如果你要强迫 Python 重新去磁盘上去加载模块, 可以这样:

import modname 
... 干点什么 
reload(modname) 
声明: 这方法并不是百分之百绝对可行的. 特别是像下面那样:
from modname import some_objects 
如此的话, 无论你怎样 reload, 那些载入的类呀什么的依然是第一次载入的样子.

4.9 怎样找到目前使用中的模块名称?

模块的名称存敦在模块的 __name__ 变量中. 这是一个预先设定好了的全局变量. 不过如果模块是以主程序的方式运行而不是由其它程序加载的话, __name__ 中存放的会是 “__main__” 字符串.

4.10. 模组以脚本执行时可否运行额外的程式码?

我写了个模组. 但我想当它直接当作程式来运行时执行些额外的程式段落. 是不是有办法知道到底它是以脚本形式直接运行还是被别的程式加载? 可参考上一题. 如果你在模组中加入下面一句, 则 main() 只有在当模组以脚本形式运行时才会被调用.  if __name__ == '__main__': main() 

4.14. 有没有给 Python 用的资料库介面?

有!! 请参观 资料库专题指引: http://www.python.org/topics/database/

4.15. 用 Python 能不能写出那些故弄玄虚让人看不明白的一行过程式?

行. 下面有三个范例, 来自 Ulf Bartelt:
小于 1000 的质数 
print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0,  
map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,1000))) 

开头 10 个 Fibonacci 数 
print map(lambda x,f=lambda x,f:(x<=1) or (f(x-1,f)+f(x-2,f)): f(x,f), 
range(10)) 

Mandelbrot 集 
print (lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y, 
Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,Sy=Sy,L=lambda yc,Iu=Iu,Io=Io,Ru=Ru,Ro=Ro,i=IM, 
Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro, 
i=i,Sx=Sx,F=lambda xc,yc,x,y,k,f=lambda xc,yc,x,y,k,f:(k<=0)or (x*x+y*y 
>=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr( 
64+F(Ru+x*(Ro-Ru)/Sx,yc,0,0,i)),range(Sx))):L(Iu+y*(Io-Iu)/Sy),range(Sy 
))))(-2.1, 0.7, -1.2, 1.2, 30, 80, 24) 
#    \___ ___/  \___ ___/  |   |   |__ lines on screen 
#        V          V      |   |______ columns on screen 
#        |          |      |__________ maximum of "iterations" 
#        |          |_________________ range on y axis 
#        |____________________________ range on x axis 
小孩子千万别在自己家里乱试! _

4.16. 是否可能模拟 C 的三位操作号 "?:" ?

直接用不可以. 在多数场合你可以用 "a and b or c" 的形式模拟 "a?b:c". 但这方法有个缺点: 当 b 是 0 (或者是空的序列, 或是 None -- 总之使 a and b 这个测试得出假值), 那最后会得到 c 纵使 a 是逻辑真值. 大部份情况下你可以仔细验算一下程式段落确认这不会发生 (例如 b 是常数, 或者是一个永远不会传回逻辑假值的类型). 不过这个方法始终有潜在的危险.

Tim Perters (他希望这是 Steve Majewski) 建议这个方案:  (a and [b] or [c])[0]. 因为 [b] 永远是真值, 于是避开了错误, 然后 [0] 取出想得到的 b, 或是 c. 它看起来挺难看的, 但在某些特殊情况你觉得用 'if' 来写太不方便的话可以考虑用它. 最最最后还有个办法, 就是给 "?:" 操作符定义一个函数:

def q(cond,on_true,on_false): 
  from inspect import isfunction 
  if cond: 
    if not isfunction(on_true): return on_true 
   else: return apply(on_true) 
  else: 
    if not isfunction(on_false): return on_false 
    else: return apply(on_false) 
多数情况下你可直接把 b, c 交给这个函数: q(a,b,c). 如果要避免刚才所说的阱陷, 可以这样调用: q(a, lambda: b, lambda: c).

4.29. Python 有哪些 WWW 的工具 ?

可参阅标准库说明文件中的 "Internet Protocols and Support" 及 "Internet Data Hanglid" 两章. Python 有好多好东西帮你建立伺服端及客户端网络系统.

Paul Boddie 保有一个关于各种架构的综览: http://thor.prohosting.com/~pboddie/Python/web_modules.html

Cameron Laird 的站上则有关于 Python 网站技术的摘要文章: http://starbase.neosoft.com/~claird/comp.lang.python/web_python.html/

甚至有一个用 Python 写的网页浏览器, 叫做 Grail. http://sourceforge.net/project/grail 这个专案已终止了; http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/grail/grail/README 可以看到更多资料.

4.33. 有没有象 scanf(), sscanf() 这样的东西 ?

没有.

如果只是做简单的输入分析, 最方便是用 split() 方法把输入字串拆开然后用 int(), long(), float() 得回相对应的数值. (Python 的 int() 是 32-位元的, 而 long() 则支援任意大数.) string.split() 同时接受一个 'seq' 参数, 方便你用空格以外的符号作为分隔符.

要是更复杂些的分析, 可以用正则表达示 (参看 re 模组), 它比 C 的 sscanf() 更强.

有一个模拟 sscanf() 的模组由 Steve Clift 项献出来; 在 ftp 站 http://www.python.org/ftp/python/contrib-09-Dec-1999/Misc/ 的 contrib/Misc/sscanfmodule.c 找到.

4.36. 请解释全局变数和本地变数

Ken Manheimer: 在 Python 之中, 操作过程中用到的变数隐含为全局变数, 除非对其进行赋值, 这种情况下则该变数在整段程式码的作用区中隐含为本地变数. 如果被赋值的数的确是全局变数, 则你必需加上 global 来宣告它.

虽然有些意想不到, 只要小心思考一下就能明白. 一方面, 在赋值时强制使用 global 宣告可以避免不小心导致的错误. 另一方面, 如果所有的全局变数都一定要用 global 则会太罗嗦, 因为如此一来你就必需使用每个内建函数, 或是便用载入的模组时都要用 global 宣告一次. Python 的做法实为取两者之便.

4.38. 如何复制对象?

copy.copy() 或 copy.deepcopy() 能应付一般的要求. 不是所有对象都能复制, 但大多数能.

字典类有个 copy() 的方法可用, 至于序列, 可以用取片断的方法复制: new_lis = old_lis[:]

4.41. 如果删除档案? 及其他有关档案的操作

用 os.remove(filename) 或 os.unlink(filename); 请看库文档中 posic 的章节. 两个函数是一样的, unlink() 只不过是在 Unix 下的叫法. 在早前 Python 版本中更只有 unlink()

移除目录用 os.rmdir(); 创立目录用 os.mkdir()

改名有 os.rename().

要截短档案, 用 f = open(filename, "r+") 把档案开成读写模式, 然后用 f.truncate(offset); offset 的预设值就是 当前的位置(即档案末尾). 对于资深的 Unix 用户, 可以用 os.ftruncate(fd, offset) 来截切用 os.open() 打开的档案.

另外, shutil 模组中也有一堆操作档案的函数, 包括 copyfile, copytree, rmtree 等等.

4/42. 要让 urllib, httplib 支援 HTTP/1.1, 该做哪些修改 ?

新版 (2.0 开始) 已支援 HTTP/1.1.

4.43. compile() 及 exec 都出些无法解释的语法错误

当用 compile(), exec, execfile() 来解读一组程式码 (而不是一个单行), 最后的一行一定要有一个断行符号. 如果程式最尾是一个缩进了的码区, 好象要额外多加一个断行符号.

4.44. 怎样把字串变成数字 ?

整数可用内建的 int() 函数, int('144') == 144. long() 则转化成大整数, long('144') == 144L; 浮点数用 float(), float('144') == 144.0.

这些函数都只限于十进位数, 因此 int('0144') == 144, 而 int('0x144') 则报错. 在 Python 2.0 中, int() 可以额外加一个基底选项, 象这样: int('0x144', 16) == 324.

用 string 模组中的 atoi(), atol(), atof() 等都可以, 请看函数库参考手册.

不建议用 eval() 方法来做转换. 因为这样做不十分安全.

4.45. 如何把数字转换成字串 ?

把 144 变成 '144', 可以用内建的 repr() 或是 反引号 ` 来做 (两者是一样的). 如果要的是十六进位或八进位, 可用内建的 hex(), oct() 函数. 要看起来美些, 用字串的 '%' 代入符. 这和 C 上的 printf() 格式是一样的. 例如: "%04d"%144 等于 '0144', '%.3f"%(1/3.0) 给出 '0.333'. 详情看库参考手册.

4.52. 元组 tuples 和 列表 lists 如何互换?

tuple() 函数把任何序列类转为元组, 其中元素完全一样, 排列次序也保持不变. 如: tuple([1, 2, 3]) 得 (1, 2, 3), tuple('abc') 得 ('a', 'b', 'c'). 如果引数已经是 tuple 则函数传回该引数而不会另外建立一个新的元组. 因而如果你不肯定一个对象是不是元组时可随便调用 tuple(), 它并不浪费资源.

list() 函数则接收序列引数, 传回相对应的列表. list((1, 2, 3)) 得到 [1, 2, 3] 而 list('abc') 得到 ['a', 'b' 'c']. 如果引数已是列表, 则 list() 会传回一个新建立的列表, 就象用 [:] 那样

4.56. 讯号处理机制 signal handlers 用不了

最常见的是宣告了错误的引数项. 它被调用时是这个形式:  handler(signum, frame), 因此要这样定义:
def handler(signum, frame): 
  ... 

4.67. 我想用 Python 产生一些随机数, 怎办?

库参考手册中有一个 random 模组, 其中有随机数产生器. 用法异常简单:

import random 
random.random() 
传回一个 0 至 1 之间(不包括 1) 的小数 [0,1). 在该模组中还有许多特别订制的函数, 提供方便
randrange(a, b) 从 [a, b) 范围中随机挑选一个整数 
uniform(a, b) 在 [a, b) 中随机传回一个小数 
normalvariate(mean, sdev) 则随机自一个高斯分布中取样 
另外还有些函数直接对序列类进行操作:
choice(S) 自序列 S 中随机挑一个数 
shuffle(L) 则打散序列, 等于重新排列一个序列 (注意是直接操作 in-place) 

4.68. 我该怎样才能存取串接口 (RS232) ?

Windows 的话, 有一个模组可以做到: ftp://ftp.python.org/pub/python/contrib/sio-151.zip http://www.python.org/ftp/python/contrib/sio-151.zip

DOS 则可试试 Hans Nowak 的 Python-DX, 它支援 RS-232 功能: http://www.cuci.nl/~hnowak/

Unix, 看 Mitch Chapman 在 usenet 上的贴文: http://groups.google.com/groups?selm=34A04430.CF9@ohioee.com

Win32, POSIX(Linux, BSD, *), Jython, 上 Chris 的 http://pyserial.sourceforge.net

4.79. 如何读写二进位数据?

对付复杂的数据格式, 最好用 struct 模组. 它的使用方法在库参考手册中. 它容许你把从档案中读进来的字串 (字串内容为以二进位代表的数字)然后把它们转成 Python 的数字类型. 或者再转回来也行.

以上例程算档案中读取两个 2字元的整数和一个4字元的整数, 用大顺位 big-endian 格式.

import struct 
f = open(filename, "rb")  # Open in binary mode for portability 
s = f.read(8) 
x, y, z = struct.unpack(">hhl", s) 

其中 '>' 代表一律用大顺位格式来转换数值; 'h' 代表一个 "short integer" (2 字元); 'l' 代表一个 "long integer" (4 字元). 如果要读取的数据十分有规律的话 (比如说是一整串同格式的整数或是浮点数), 你也可以参考 array 模组中关于读取阵列的方法. 同样记载于库参考手册中.

4.85. __import__('x.y.x') 传回 <module 'x'>; 我怎样才得到 z?

试这样: {{ __import__('x.y.z').y.z }}}

在实际的埸合, 你也许应该这样写:

m = __import__(s) 
for i in string.split(s, ".")[1:]: 
  m = getattr(m, i) 

4.92. 有没有关于 Python 的指南级教程?

有. 看问题 1.20

4.93. 已取消

看 4.28

4.94. 怎样才做到每次一有按键就读取, 不必等按 Enter ?

方法有几个. 有的要用到 curses, 不过学用它要花些工夫. 下面的方法不需用到 curses. (也请看 4.74. Windows 用家请转到 8.2)

import termios, fcntl, sys, os 
fd = sys.stdin.fileno() 
oldterm = termios.tcgetattr(fd) 
newattr = termios.tcgetattr(fd) 
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO 
termios.tcsetattr(fd, termios.TCSANOW, newattr) 
oldflags = fcntl.fcntl(fd, fcntl.F_GETFL) 
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK) 
try: 
  while 1: 
    try: 
      c = sys.stdin.read(1) 
      print "Got character", `c` 
    except IOError: pass 
    finally: 
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm) 
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags) 

你要有 termios 及 fcntl 模组才能用到它. 本人只在 Linux 机上测试成功, 但应该别地方也行.

在程式段中, 每次只会读取一个字符. termios.tcsetattr() 把标准输入的 echo 功能关掉同时取消了 canonical 模式. fcntl.fcntl() 用来把标准输入设备的档案记述旗标修改为非等候态 (non-blocking). 另一方面, 读取空的标准输入会引发错误 IOError, 然而在程式段中这个错误指明忽略掉了.

4.101. 有没有工具可以帮忙找出程式中的错误及做程式码分析?

有的. PyChecker 是一个静态分析工具可以找出 Python 码中可能的错误, 并对程式的复杂度和书写风格给意见. 你可在在这儿找到 PyChecker http://pychecker.sf.net

4.104. 异常(exceptions) 够快吗?

try/except 形式的程序执行起来是极之快的, 但真出现异常状况时的处理工作就比较耗时. 在 2.0 以前的版本, 有一个这样的技巧:
    1 try:
    2     value = dict[key]
    3 except KeyError:
    4     dict[key] = getinput(key)
    5     value = dict[key]

如果你确信在百分之九十五的情况下 dict[key] 是找得到的, 这个技巧会令程序较快些; 不然的话, 经常处理找不到 key 的异常会影响程序的执行效率. 那时候你应该用下面的写法:

    1 if dict.has_key(key):
    2     value = dict[key]
    3 else:
    4     dict[key] = getinput(key)
    5     value = dict[key]
    6 
    7 ## In Python 2.0 and higher, of course, you code this as
    8 ## value = dict.setdefault(key, getinput(key))

4.105. 怎样在模块之间建立共享的全局变量?

正规的方法是写一个特别的模块 (一般命名为 config 或 cfg). 只要在你所有的模块中 import 这个特殊的模块即可. 它的巧妙之处是因为 python 中每个加载的模块只能有一个实体, 因此你在程序中对 config 模块中变量的改动会实时反映到所有的地方. 像这样:
#config.py: 
pass 
-------------  
#mod.py: 
import config 
config.x = 1 
-------------  
#main.py: 
import config 
import mod 
print config.x 
注意这也是实现 Singleton design pattern 的基本原则, 道理相似.

4/106. 为何 cPickle 很慢?

嫌慢的话请用 Binary 选项. 本来这个选项应该是作为预设项的, 但这样做会影响旧程序的兼容性, 我们只好把它收起来了.


PythonPowered
搜寻页面 或尝试以下动作: 附加档案, 删除页面, 本页连结图