Code Like a Pythonista: Idiomatic Python(像python高手一样编程:地道的python)

Python 的禅(1) 这些是python的指导原则,但是诠释是开放的。对这些原则合适的解释是需要一些幽默感的。如果你正在使用以小品喜剧艺术团的名字命名的一种编程语言,你最好具有幽默感。 Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. ... Python 的禅(2) In the face of ambiguity, refuse the temptation to guess. There should be one—and preferably only one—obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than right now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea—let's do more of those! 这首特别的“诗”开始作为一个笑话,但它确实包含了很多关于Python背后的哲学真理。 Python的禅已经正式成文PEP 20,它的摘要是: Python老手Tim Peters 把BDFL(http://en.wikipedia.org/wiki/BDFL)的python代码设计的指导原则简洁地总结成20条格言,只有19条被记录下来。 如果你是一个“Pythoneer”或“Pythonista”,你可以自己决定,python的禅有不同的内涵。 如果存在疑问: Import this 尝试在Python交互式解释: import this Python中还有一个复活节彩蛋: from __future__ import braces   File " stdin ", line 1 SyntaxError: not a chance 一群喜剧演员! 编程风格:读胜于写 程序必须是写来给人读得,只是顺带让机器执行。 —Abelson Sussman, 计算机 程序的构造 和解释 尽量让你的程序易于阅读和明显。 PEP 8 :Python 代码风格指南 值得阅读: http://www.python.org/dev/peps/pep-0008/ PEP=Python的增强建议 一个PEP是向python社区提供信息的设计文档,或描述Python或过程或环境的新功能。 Python社区有着自己的代码风格的标准,这些标准编纂在PEP 8中。这些标准是与其他社区的不同,如C,C +,C#,Java,VisualBasic等等。 因为缩进和空格在python中至关重要,Python代码风格指南几乎就是标准。遵循风格指南是将是十分明智。大多数开源项目和内部项目紧紧地遵循风格指南。 空格1 4个空格一个缩进层次; 最好不要使用制表符; 永远不要混用制表符和空格;( 这确实是IDLE和Emacs的Python模式的支持。 其他编辑器也可以提供这种支持。) 函数之间使用一个空行分割; 类之间使用两个空行分割 空格2 添加一个空格在字典、列表、序列、参数列表中的“,“后,以及在字典中的”:“之后,而不是之前; 在赋值和比较两边放置一个空格(参数列表中除外); 紧随括号后面或者参数列表前一个字符不要存在空格; (a, b) yes   (  a, b)    no Def test(a, b, c): pass   yes Def test(  a, b, c)      no 紧随文档字符串的后面不要存在空格; “””Return a”””  yes    “”” Return a””” no def make_squares(key, value=0):     """Return a dictionary and a list..."""     d = {key: value}     l = [key, value]     return d, l 命名 l  应避免的名字 永远不要用字符`l'(小写字母el(就是读音,下同)), O'(大写字母oh),或I'(大写字母eye)作为单字符的变量名. 在某些字体中,这些字符不能与数字1和0分开.当想要使用'l'时,用'L'代替它. l  模块名 模块应该是不含下划线的,简短的,小写的名字. 因为模块名被映射到文件名, 有些文件系统大小写不敏感并且截短长名字, 模块名被选为相当短是重要的---这在Unix上不是问题, 但当代码传到Mac 或Windows上就可能是个问题了. 当一个用C或C++写的扩展模块有一个伴随的Python模块,这个Python模块提供了 一个更高层(例如,更面向对象)的接口时,C/C++模块有一个前导下划线(如:_socket) Python包应该是不含下划线的,简短的,全小写的名字. l  类名 几乎没有例外,类名总是使用首字母大写单词串(CapWords)的约定. l  异常名 如果模块对所有情况定义了单个异常,它通常被叫做"error"或"Error". 似乎内建(扩展)的模块使用"error"(例如:os.error), 而Python模块通常用"Error" (例如: xdrlib.Error). 趋势似乎是倾向使用CapWords异常名. l  全局变量名 (让我们希望这些变量打算只被用于模块内部) 这些约定与那些用于函数的约定差不多.被设计可以通过"from M import *"来使用的 那些模块,应该在那些不想被导入的全局变量(还有内部函数和类)前加一个下划线). l  函数名 函数名应该为小写,可能用下划线风格单词以增加可读性. mixedCase仅被允许用于这种风格已经占优势的上下文(如: threading.py) 以便保持向后兼容. l  ...

阅读全文 »

Python装饰器的理解 (转载)

装饰器一直不是很容易理解,在网上找了一篇文章对装饰器的解释是最好的。转给大家以共享。 Python中函数有一个装饰器的概念,今天,看核心编程中的函数一章的时候接触到了这个概念,炸一看来,讲的说明真实不好明白。于是写下本篇以示说明,提供给迷糊者。希望能对一些人起到一定的帮助 装饰器的语法以@开头,接着是装饰器要装饰的函数的申明等。 其实总体说起来,装饰器其实也就是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后申明的函数被换成一个被装饰器装饰过后的函数。 装饰器分为无参装饰和有参装饰 无参装饰很简单 定义方法如下: 比如先定义一个装饰方法: def FirstDeco(func): print '第一个装饰器' return func @FirstDeco def test(): print 'asdf' .... 申明完成之后显示 ... 第一个装饰器 可见装饰器在函数定义完成的时候被触发 然后,咱们运行test 获得asdf 多参装饰: 多参装饰复杂一点,多参装饰的时候,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰 具体代码如下: def deco(x): ... print '%s 开始新装饰' ... def newDeco(func): ... def test(a,b): ... print 'begin' ... returnv = func(a,b) ... print 'end' ... return returnv ... return test ... return newDeco ... 这里定义了一个装饰其函数deco,里面有一个参数x,这个时候,我们没有直接使用func作为装饰函数的参数,而是只用了参数x作为参数,之后定义一个新的装饰函数,newdeco,该函数才装饰 然后定义如下: @deco(3) ... def mytest(x,y): ... if x y: ... print x ... else: ... print y ... %s 开始新装饰 运行之后的结果为 %s 开始新装饰 mytest(3,4) begin 4 end 参考资料: 装饰方法的产生: Python2.2通过增加静态方法和类方法扩展了Python的对象模型。但是当时没有提供一个简化的语法去定义static/class方法,只得在定义好的方法尾部去调用staticmethod()/classmethod()方法达到目的。例如: class C: def meth (cls):meth = classmethod(meth) # 使meth方法成为类方法 但是这样会造成一个问题:当一个方法比较长时,很容易忘记尾部的调用。为了简化这个操作一个新的语法被加了进来:方法装饰,以@开头后跟装饰方法 名,如@staticmethod/@classmethod,由此产生出decorator方法及decorator模式。现在我们可以这样写: class C: @classmethod def meth (cls): 可以对一个方法应用多个装饰方法: @A @B @C def f ():#等价于下面的形式,Python会按照应用次序依次调用装饰方法(最近的先调用) def f(): f = A(B(C(f))) 装饰方法解析: 每个decorator只是一个方法, 可以是自定义的或者内置的(如内置的@staticmethod/@classmethod)。decorator方法把要装饰的方法作为输入参数,在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景), 只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)。decorator的作用对象可以是模块级的方法或者类方法。decorator根据应用时的参数个数不同分为两类:无参数 decorator,有参数decorator。下面分别介绍。 无参数decorator: def deco(func): """无参数调用decorator声明时必须有一个参数,这个参数将接收要装饰的方法""" print "Enter decorator" #进行额外操作 func.attr = 'decorated' #对函数进行操作,增加一个函数属性 return func #返回一个可调用对象(此例还是返回作为输入参数的方法) #返回一个新函数时,新函数可以是一个全局方法或者decorator函数的内嵌函数, #只要函数的签名和被装饰的函数相同@deco def MyFunc(): #应用@deco修饰的方法 print "Enter MyFunc" MyFunc() #调用被装饰的函数 注意:当使用上述方法定义一个decorator方法时,函数体内的额外操作只在被装饰的函数首次调用时执行,如果要保证额外操作在每次调用被装饰的函数时都执行,需要换成如下的写法: def deco(func): def replaceFunc(): #定义一个内嵌函数,此函数包装了被装饰的函数,并提供额外操作的代码 print "Enter decorator" #进行额外操作 return func() #产生对被装饰函数的调用 return replaceFunc #由于返回的是这个新的内嵌函数,所以确保额外操作每次调用得以运行@deco def MyFunc(): #应用@deco修饰的方法 print "Enter MyFunc" MyFunc() #调用被装饰的函数 有参数decorator: def decoWithArgs(arg): """由于有参数的decorator函数在调用时只会使用应用时的参数而不接收被装饰的函数做为参数, 所以必须返回一个decorator函数, 由它对被装饰的函数进行封装处理""" def newDeco(func): #定义一个新的decorator函数 def replaceFunc(): #在decorator函数里面再定义一个内嵌函数,由它封装具体的操作 print "Enter decorator" #进行额外操作 return func() #对被装饰函数进行调用 return replaceFunc return newDeco #返回一个新的decorator函数@decoWithArgs("demo") def MyFunc(): #应用@decoWithArgs修饰的方法 print "Enter MyFunc" MyFunc() #调用被装饰的函数 当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现。 ...

阅读全文 »

python challenge 0,1,2解题以及总结

level 0:

2**38=274877906944 change the url to http://www.pythonchallenge.com/pc/def/274877906944.html

level 1:

     代码如下:

import string
 
def change(inputstr,fromtable,totable):
       newtable=string.maketrans(fromtable,totable)
       hintstring=inputstr.translate(newtable)
       return hintstring
 
if __name__=='__main__':
     a="g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq()
           gq pcamkkclbcb. lmu ynnjw ml rfc spj"
        table="'().abcdefghijklmnopqrstuvwxyz"
        newtable="'().cdefghijklmnopqrstuvwxyzab"
        result=change(a,table,newtable)
        url=change('map',table,newtable)
        print result
        print url



这个主要是考察string.maketrans和str.translate的作用,translate除了本代码里面的作用外还可以起到过滤的作用,如 >>> 'this0not1a2stroy!3333'.translate(None,'0123') 'thisnotastroy!'

level 2:

这个主要是考察正则表达式和re。 首先获取网页的源码,会得到如下信息: source 简单的代码如下:

import re
pattern='[A-Za-z0-9]+'
result=''
with open('D:/level2.txt','r') as f:
        for eachline in f:
              m=re.search(pattern,eachline)
              if m is not None:
                  result+=m.group()
print result


阅读全文 »

python challenge 5解题总结

python challenge 5 网址:www.pythonchallenge.com/pc/def/peak.html

 

网页上只有两个英语单词pronounce it,再看图片上的画面,上面显示的是山顶,山顶的英文单词为peak,这与python的模块pickle音相同,于是想到这是一个序列化问题,看网页的源码会得到banner.p这么一个信息,于是利用urllib2打开,用pickle进行反序列化。


源代码如下:

import urllib2, pickle
url="http://www.pythonchallenge.com/pc/def/banner.p"
result=''
tmp=urllib2.urlopen(url)
fileobj=pickle.load(tmp)
for i in fileobj:
    for j in i:
        result=result+j[0]*j[1]
    result=result+'n'
print result


阅读全文 »

python challenge 3解题总结

python challenge 3网址:www.pythonchallenge.com/pc/def/equality.html
 

图片下面显示一句话:One small letter, surrounded by EXACTLY three big bodyguards on each of its sides.


从提示的话语里面可以看出需要寻找一个小写字母左右各有三个大写字母的形式,例如:1ASDaFGH9.


接着查看网页源码,能够发现一大堆类似“kAewtloYgcFQaJNhHVGxXDiQmzjfcpYbzxlWrVcqsmUbCunkfxZWDZjUZMiGqhRRiUvGmYmvnJIHEmbT”这样的字符串,联想到提示,就知道需要寻找一些小写字符,小写字符的形式是左右各三个大写字母。


源代码如下:

import urllib
import re
urldata=urllib.urlopen("http://www.pythonchallenge.com/pc/def/equality.html")
geturldata=urldata.read()
getdata=re.findall(r'',geturldata,re.S)
charlist =''.join(re.findall(r'[^A-Z][A-Z]{3}([a-z])[A-Z]{3}[^A-Z]', getdata[0], re.S)) 
print charlist

输出linkedlist
也可以把字符串拷贝进一个本地文档,直接读取文档,用正则表达式匹配出符合要求的字符。


 

阅读全文 »

python challenge 4解题总结


 python challenge 4网址:www.pythonchallenge.com/pc/def/linkedlist.php

打开这一关的页面只显示一个图片,按照惯例,先查看页面源码,会得出如下信息:
urllib may help. DON'T TRY ALL NOTHINGS, since it will never end. 400 times is more than enough
并且会得到一个网址linkedlist.php?nothing=12345,打开http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing=12345后会得到如下信息:
and the next nothing is 44827
接着把nothing=12345换成nothing=44827,会得出如下信息:
and the next nothing is 45439
看到这里,并且联想提示:urllib may help. DON'T TRY ALL NOTHINGS, since it will never end. 400 times is more than enough,直接读取网页信息,利用正则表达式取出网页信息中的数字,填入nothing=*****中,直到得出结果。
(这里需要注意的是中间有部分网页会出现Yes. Divide by two and keep going.这样的信息,所以需要程序里面进行判断。)


源代码如下:

import re   
import urllib   
url="http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="  
nothing = "12345"  
search = re.compile(" (d*)$")   
search_html = re.compile(".html$")   
  
for i in xrange(300):    
    print "%s: " % nothing,    
    line = urllib.urlopen( "%s%s" % (url,nothing) ).read()   
    print line     
    if search_html.findall (line):   
        break             
    match = search.findall (line)   
    if match:   
        nothing = match [0]   
    else:   
        nothing = str (int (nothing) / 2 )  


得出的结果是:peak.html,改变url进入下一关。
 

阅读全文 »

python challenge 6解题总结

python challenge 6的网址:www.pythonchallenge.com/pc/def/channel.html

这一关只有这一张图片,并且从源码里面也没有得出什么具体的信息,于是突然想到zip可以表示图片里面的拉链的意思,于是打开www.pythonchallenge.com/pc/def/channel.zip, 原来玄机就在这里。对付ZIP,python模块里面有zipfile

解题代码如下:

import zipfile, re
start = '90052'
r = 'Next nothing is (d+)'
file = zipfile.ZipFile('D:channel.zip')
f = "%s.txt"
c = []
while True:
      try:
          start = re.search(r, file.read(f % start)).group(1)
     except:
         print file.read(f % start)
         break
     c.append(file.getinfo(f % start).comment)
print "".join(c)


阅读全文 »

python challenge 7解题总结

 

python challenge 7网址:www.pythonchallenge.com/pc/def/oxygen.html 

从图片一看就发现图片中间有灰色的东西,这时候就想到是不是需要用python的图片模块来出来这段灰色的地方,这里可能是解题的关键,python处理图片比较好的模块是PIL。将全幅图像转换为灰度值后,对中间的那条带子隔7取1。

代码如下:

import PIL.Image
temp=PIL.Image.open('D:oxygen.png')           #把图片拷贝到本地
temp2=temp.convert('L').getdata()
beginpoint=temp.size[0]*50
endpoint=beginpoint+608
ra=range(beginpoint,endpoint,7)
temp3=[]
for v in ra:
        temp3.append(chr(temp2[v]))

print ''.join(temp3)
test=[105, 110, 116, 101, 103, 114, 105, 116, 121]
answer=''
for line in test:
    answer=answer+chr(line)
print answer


阅读全文 »

python challenge 8解题总结

 

python challenge 8网址:www.pythonchallenge.com/pc/def/integrity.html

 

 

这个图片以及后面的源码初看起来没有什么思路,后来突然发现如下的文字:

un: 'BZh91AY&SYAxafx82rx00x00x01x01x80x02xc0x02x00 x00!x9ah3Mx07<]xc9x14xe1BAx06xbex084'
pw: 'BZh91AY&SYx94$|x0ex00x00x00x81x00x03$ x00!x9ah3Mx13<]xc9x14xe1BBPx91xf08'

在google里面搜索一下BZh91AY,就发现原来需要用python的bz2模块来处理。

源代码如下:

import bz2

un= 'BZh91AY&SYAxafx82rx00x00x01x01x80x02xc0x02x00 x00!x9ah3Mx07<]xc9x14xe1BAx06xbex084'
pw= 'BZh91AY&SYx94$|x0ex00x00x00x81x00x03$ x00!x9ah3Mx13<]xc9x14xe1BBPx91xf08'

username=bz2.decompress(un)

password=bz2.decompress(pw)

阅读全文 »

python challenge 9解题总结

 

python challenge 9 网址:www.pythonchallenge.com/pc/return/good.html

从网页的源代码可以得出两个数字列表:

first=[146,399,163,403,170,393,169,391,166,386,170,381,170,371,170,355,169,346,167,335,170,329,170,320,170,
310,171,301,173,290,178,289,182,287,188,286,190,286,192,291,194,296,195,305,194,307,191,312,190,316,
190,321,192,331,193,338,196,341,197,346,199,352,198,360,197,366,197,373,196,380,197,383,196,387,192,
389,191,392,190,396,189,400,194,401,201,402,208,403,213,402,216,401,219,397,219,393,216,390,215,385,
215,379,213,373,213,365,212,360,210,353,210,347,212,338,213,329,214,319,215,311,215,306,216,296,218,
290,221,283,225,282,233,284,238,287,243,290,250,291,255,294,261,293,265,291,271,291,273,289,278,287,
279,285,281,280,284,278,284,276,287,277,289,283,291,286,294,291,296,295,299,300,301,304,304,320,305,
327,306,332,307,341,306,349,303,354,301,364,301,371,297,375,292,384,291,386,302,393,324,391,333,387,
328,375,329,367,329,353,330,341,331,328,336,319,338,310,341,304,341,285,341,278,343,269,344,262,346,
259,346,251,349,259,349,264,349,273,349,280,349,288,349,295,349,298,354,293,356,286,354,279,352,268,
352,257,351,249,350,234,351,211,352,197,354,185,353,171,351,154,348,147,342,137,339,132,330,122,327,
120,314,116,304,117,293,118,284,118,281,122,275,128,265,129,257,131,244,133,239,134,228,136,221,137,
214,138,209,135,201,132,192,130,184,131,175,129,170,131,159,134,157,134,160,130,170,125,176,114,176,
102,173,103,172,108,171,111,163,115,156,116,149,117,142,116,136,115,129,115,124,115,120,115,115,117,
113,120,109,122,102,122,100,121,95,121,89,115,87,110,82,109,84,118,89,123,93,129,100,130,108,132,110,
133,110,136,107,138,105,140,95,138,86,141,79,149,77,155,81,162,90,165,97,167,99,171,109,171,107,161,
111,156,113,170,115,185,118,208,117,223,121,239,128,251,133,259,136,266,139,276,143,290,148,310,151,
332,155,348,156,353,153,366,149,379,147,394,146,399]

second=[156,141,165,135,169,131,176,130,187,134,191,140,191,146,186,150,179,155,175,157,168,157,163,157,159,
157,158,164,159,175,159,181,157,191,154,197,153,205,153,210,152,212,147,215,146,218,143,220,132,220,
125,217,119,209,116,196,115,185,114,172,114,167,112,161,109,165,107,170,99,171,97,167,89,164,81,162,
77,155,81,148,87,140,96,138,105,141,110,136,111,126,113,129,118,117,128,114,137,115,146,114,155,115,
158,121,157,128,156,134,157,136,156,136]

于是想到利用python的图片处理模块PIL来对这两个数字字符串位置进行填充,于是就有源码如下:

from PIL import Image
from PIL import ImageDraw
temp=Image.open('good.jpg')
tempdraw=ImageDraw.Draw(temp)
tempdraw.line(first,fill=255)
tempdraw.line(second,fill=0)
temp.save('good.jpg')


如果需要用户名和密码的话,用户名为huge,密码为file。

阅读全文 »