I am LAZY bones? AN ancient AND boring SITE

分类: '编程相关' 的归档

python代码风格检查工具──pylint

pylint是一个python代码检查工具,可以帮助python程序员方便地检查程序代码的语法和风格,通过这个工具,可以使你的python代码尽量保持完美,哈哈。
具体可以检查什么东西呢?
比如你写了 from XXX import * 了,它就会提示你这样import是不好的。
比如你操作符的前后没有空格,它也会提示你。
比如逗号后面没跟空格也会。
还有你import了没用到的模块,定义了没使用的变量等也会提示。
还有你的变量名是否符合规范也会提示。
总之它提示的内容很多很全面,而且它最后会给出一个所检查的代码的总体分数,如果能达到满分10分的话,简直就是神作了,因为pylint本身的代码也才9.5分左右,哈哈。
比较惨的是,TX发现这个工具以后,检查了几个python项目的分数,都不是很高,我那gmbox得了2分多点,还算是高的。不过,经过一番优化,现在的gmbox已经有6.64分,及格了,哈哈。。
值得一提的是,pylint不仅可以像默认那样输出字符结果,还可以彩色化输出,甚至还可以输出HTML和visual studio的格式。具体用法可以参见man页和这个文档

python程序打包工具 ── cx_Freeze

cx_Freeze是一个类似py2exe的工具,它们区别是py2exe是将python程序打包成windows下可以执行的exe文件的,而cx_Freeze则是将python程序打包为linux下可以直接执行的ELF格式的二进制可执行文件(看说明好像也能生成windows的可执行文件,号称跨平台)。
cx_Freeze的作用就是让你的python程序可以脱离python运行环境,在没有安装python的微型linux系统(例如cdlinux、tinycore等)里,方便地运行你的python程序。从功能上来说,也可以将其理解为一个python程序的编译器,将你的源码隐藏起来。
使用方法也很简单,下载以后,解压,如一般的python模块一样,cd到目录以后,

python setup.py install

就可以完成安装,这样你的系统里就会有cxfreeze命令了。
然后,cd到你的python程序的目录执行

cxfreeze 你的程序文件.py --target-dir dist

就会在当然目录生成一个dist的目录,里面就会有一堆so文件和可执行目标文件了,当然如果你还有图片或者其他数据文件的话,手工复制到相应目录,这时候运行那个可执行文件,就应该能看到效果了。
现在只需将dist目录打包,传到没有python的目标系统里,你程序也就能运行了。

PS: cx_Freeze还有另外两种使用方法,需要了解的话,可以自行查看随代码打包的html文档。

python 内建函数

说明:本文内容全部出自python官方文档,但是会有自己的理解,并非单纯的翻译。文章较长,如有错误之处,还请大家指正。

abs(x)
返回x的绝对值;当x是复数时,返回x的模。没错,python内建支持复数,见下面的complex()函数。

all(iterable)
iterable里的每项都为真时,才返回真,等效于:

def all(iterable):
    for element in iterable:
        if not element:
            return False
    return True

any(iterable)
只要iterable里有一项为真,就返回真,等效于:

def any(iterable):
    for element in iterable:
        if element:
            return True
    return False

basestring()
这是 str 和 unicode 的抽象类,它不能被调用也不能被实例化,但是可以用在 isinstance 函数里进行判断,isinstance(obj, basestring) 等效于 isinstance(obj, (str, unicode)).

>>> isinstance(123, basestring)
False
>>> isinstance("123", basestring)
True
>>> isinstance(u"一二三", basestring)
True

bin(x)
如果x是一个整数,则返回一个与x等值的二进制python表达式;如果x不是一个整数类型,则x的类需要有一个可以返回一个整数的__index__()函数。

bool([x])
返回一个布尔型的值,如果x为False或者没传x参数的时候返回False,否则返回True。

callable(object)
判断object是否可调用,如果object是 函数、类、或者含有__call__()的类对象的话,将返回True。

chr(i)
返回一个单个字符的字符串,此字符的ascii码值为i(0<=i<=255),此函数是ord函数的反函数。如果参数大于255而想得到一个unicode字符的话,需要使用unichr()

classmethod(function)
返回一个类的方法(类的方法有别于实例的方法,是不需要实例化也可以通过类名访问的方法),定义一个类的方法需要用这样的形式:

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...

cmp(x, y)
比较两个对象xy。如果x小于y,返回负数;大于返回正数;等于返回0。

compile(source, filename, mode[, flags[, dont_inherit]])
source字符串编译成一个AST对象,暂时用不到,先略过。

complex([real[, imag]])
用传入的实部和虚部创建一个复数对象。

delattr(object, name)
删除对象的属性,相当于 del object.name ,可以和setattr配合使用。

dict([arg])
建立一个新的字典型数据,可以从参数里获取数据。

>>> dict({"a":"b","c":"d"})
{'a': 'b', 'c': 'd'}

dir([object])
如果不加参数,返回当前执行环境下的变量名的列表。
如果加了object参数,则会根据复杂的规则得到object的属性名列表,需要注意的是,当object定义了__dir__()或者 __getattr__()方法时,返回的结果并不一定正确。
示例:

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> t=[1,2]
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 't']
>>> dir(t)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

divmod(a, b)
通常返回ab的商和余数组成的元组: (a // b, a % b)。参数不能是复数。

enumerate(sequence[, start=0])
返回一个列举后的对象,sequence要支持迭代。返回的对象支持next()方法,此方法依次返回一个从start开始增长的序数和sequence里的元素组成的元组。看以下的例子:

>>> enu=enumerate(['Spring', 'Summer', 'Fall', 'Winter'])
>>> enu.next()
(0, 'Spring')
>>> enu.next()
(1, 'Summer')
>>> for i, season in enu:
...     print i, season
... 
2 Fall
3 Winter

eval(expression[, globals[, locals]])
执行expression表达式,可以用globalslocals来限制expression能访问的变量。
值得注意的是,expression不仅可以是明文的字符串,还可以是compile()函数返回的代码对象。

execfile(filename[, globals[, locals]])
此函数类似exec表达式。只是从文件里读取表达式。它和import的区别在于,execfile会无条件地读取文件,而且不会生成新的模块。
globalslocals的用法和上面的eval同理。

file(filename[, mode[, bufsize]])
File类型的构造函数,参数的作用和下面提到的open()函数是一样的。
值得注意的是,open()函数更适合于打开一个文件,而file函数更适用于类型测试,例如: isinstance(f, file)

filter(function, iterable)
构造一个function(iterable)为true的list。当然iterable为字符串或者tuple的时候,返回的类型也是字符串或者tuple,否则返回list。

>>> filter(lambda c: c in 'abc', 'abcdcba')
'abccba'
>>> filter(lambda i: i < 3, (1, 2, 3, 2, 1))
(1, 2, 2, 1)
>>> filter(lambda i: i < 3, [1, 2, 4, 2, 1])
[1, 2, 2, 1]

如果function为None,则iterable为false的元素将被剔除。也就是说,function不为None的时候,filter(function, iterable)等效于[item for item in iterable if function(item)],否则等效于[item for item in iterable if item]

float([x])
传入一个字符串或者整数或者float,返回一个float数据。

format(value[, format_spec])
根据format_spec格式化输出value的值,实际上只是调用了value.__format__(format_spec),很多内建类型都有标准的输出函数

frozenset([iterable])
iterable创建一个frozenset对象,frozenset是set的一个子类,它和set的区别在于它不支持某些可以修改set的操作,例如:add、remove、pop、clear等。可以理解为一个set的常量。

getattr(object, name[, default])
获得对象的属性值,name必须是字符串,如果nameobject的属性,则getattr(x, 'foobar')相当于x.foobar,如果name不是object的属性,则返回default,如果没有default就会抛出AttributeError意外。

globals()
返回一个包含当前“全局符号表”的dict。

hasattr(object, name)
参数是一个对象和一个字符串,如果object对象有名为name的属性,则返回True,否则返回False。在执行getattr(object, name)之前,可以以此来检测属性的存在性。

hash(object)
如果可能的话,返回object的hash值,hash值是一个整型的数字,用于快速比较两个对象。两个相等的数字型对象将有相同的hash值,比如:

>>> hash(1) == hash(1.0)
True

help([object])
调用内建的帮助系统(交互式)。
如果省略参数,则会进入帮助控制台,出现help>的提示符,输入相应内容就可以查看相应的帮助。
如果参数是字符串,则在模块名、函数名、类名、方法名、关键字及文档和帮助主题里搜索此字符串,并显示。
如果参数是其他类型的对象,则显示此对象的帮助信息。

hex(x)
将任何长度的整型数字转化为16进制的字符串。
如果转换浮点数为16进制,则须使用float.hex()方法。

id(object)
返回一个整型(或者长整型)的object的唯一标识符。注意:两个生命周期没有交叉的对象,也许会返回同一个标识符。(在CPython里,其实就是返回object的地址)

input([prompt])
等效于 eval(raw_input(prompt))
返回用户输入的python表达式的值,一句话:注意安全。

int([x[, base]])
根据x的值返回一个整数,x可以是一个含有数字信息的字符串或者数字类型(整型/浮点型/长整型/复数)。可选的base参数,代表进制,可以是2~36之间的数字或者0。如果base的值为0,将会根据x的值选取适当的基数。如果不提供任何参数,将返回0。

isinstance(object, classinfo)
如果objectclassinfo或者classinfo的子类的实例,或者是和classinfo同类的对象,则返回True。classinfo也可以是类或者对象组成的tuple,这时候,object只要是classinfo里的一者就返回True:

>>> isinstance(1, (int,float) )
True
>>> isinstance(1.0, (int,float) )
True
>>> isinstance("1.0", (int,float) )
False

issubclass(class, classinfo)
如果classclassinfo的直接或者间接之类的话,就返回True。一个类也被视为自己的之类。同上例,classinfo也可以是tuple。

iter(o[, sentinel])
返回一个“迭代器”对象,根据sentinel的设置不停地对第一个参数进线取值。当忽略第二个参数时,o必须是一个支持__iter__()或者__getitem__()方法的对象,否则将会抛出TypeError例外。如果提供了sentinel参数,o必须是一个可调用的对象,这时将不停地调用此方法,并返回迭代器的项,知道返回的值等于sentinel为止,这时将抛出StopIteration。
第二种形式特别适用于打开一个文件,一行行处理文本,知道遇到特定的行:

with open("mydata.txt") as fp:
    for line in iter(fp.readline, "STOP"):
        process_line(line)

len(s)
返回s的长度,也就是项数。自建会调用__len__函数取值。

list([iterable])
返回一个含有所有iterable中的元素的list对象。如果参数为空,则返回空的list。

locals()
和上面的globals()对应,返回一个包含当前“局部符号表”的dict。在函数里调用的时候,将排除在类中声明的变量。

long([x[, base]])
根据字符串或者数字类型的参数,返回一个长整型的数字。参数的含义和上面的int类似。

map(function, iterable, )
iterable里的每项执行function函数,并把结果以一个list的形式返回。如果有3个以上的参数,则后面的参数也需要是可迭代的,map会把额外的参数传给function,例如,这样可以把两个tuple一一相加得到一个list:

>>> map(lambda x, add: x + add, (2, 4, 5), (1, 3, 6) )
[3, 7, 11]

如迭代器的长度不一致,缺失的项将用None代替:

>>> map(lambda x, add: x + add, (2, 4, 5), (1, 3))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

如果function为None,将用 identity function 代替(好像就是直入直出)。

max(iterable[, args…][, key])
如果只给一个参数,就返回iterable里最大的项;如果是多个参数的话,则返回参数里最大的项。

>>> max("abcd")
'd'
>>> max(1, 2, 3)
3

额外的key参数,是用于比较的函数,比如,下面这个可以得到各项除3的余数最大的一个:

>>> max([1, 2, 3, 4], key=lambda x: x % 3)
2

min(iterable[, args…][, key])
同上,求最小值。

next(iterator[, default])
依次返回迭代器iterator的项。当iterator没有更多的项时,如果有default参数,则返回default,否则抛出StopIteration例外。

>>> a = iter(range(3))
>>> next(a)
0
>>> next(a)
1
>>> next(a)
2
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> next(a, "No More Item...")
'No More Item...'

object()
返回一个空的对象,但是此对象会有一些公有的属性:

>>> o = object()
>>> dir(o)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

oct(x)
将任意精度的十进制整数x转换成八进制。

open(filename[, mode[, bufsize]])
打开文件,返回一个文件对象,如果文件打不开,将抛出IOError错误。
filename参数,是要打开的文件名。
mode参数是打开方式,通常是'r'表示读,'w'表示写(如果已存在则会覆盖),'a'表示追加。缺省为'r'。另外,缺省使用的是文本模式,会把'\n'转成系统相关的换行符,如果要避免这个引起的问题,需要在各个模式后面加一个'b'表示使用二进制模式。另外还有些’+uU’之类的模式,不常用,也就不介绍了吧。
可选的bufsize参数表示缓冲区的大小。0表示不缓冲,1表示行缓冲,其他正数表示近视的缓冲区字节数,负数表示使用系统默认值。默认是0。

ord(c)
给定一个长度为1的字符串或者unicode字符,返回该字符的ascii码或者unicode码,前一种情况是chr()的反函数,后一种情况是unichr()的反函数。

pow(x, y[, z])
返回xy次方,也就是x**y。如果有z的话,返回xy次方除z得到的余数(这个比pow(x, y) % z更高效,这点可以看我写的欧拉工程48题的代码,之前很慢,现在很快)。
如果第二个参数是负数的话,将返回浮点型的数据,而且这个时候不能有z

print([object, ][, sep=’ ‘][, end=’\n’][, file=sys.stdout])
输出一个或多个objectfile,中间用sep间隔,并在结尾加上end
后3个参数如果给出的话,必须用keyword arguments的形式,也就是必须指定参数名,否则将一概被视为object的一部分而被输出。
需要注意的是和python 2.6前的print关键字的区别。

property([fget[, fset[, fdel[, doc]]]])
返回一个属性,参数分别是获取、设置和删除的函数外加doc string,看例子吧:

>>> class C(object):
...     def __init__(self):
...         self._x = None
...     def getx(self):
...         print "OK. give you:", self._x
...         return self._x
...     def setx(self, value):
...         print "Now x is:", value
...         self._x = value
...     def delx(self):
...         del self._x
...     x = property(getx, setx, delx, "I'm the 'x' property.")
... 
>>> a = C()
>>> a.x = 123
Now x is: 123
>>> print a.x
OK. give you: 123
123
>>> help(a.x)
OK. give you: 123
 
>>> help(C.x)
#这里可以看到I'm the 'x' property.

range([start], stop[, step])
方便地产生一个包含等差数列的list,如果忽略start,则默认为0;如果忽略step,则默认为1。经常被用于for循环里。注意返回的结果并不包含stop

raw_input([prompt])
从输入读入一行字符串,结尾的回车将被去掉。如果提供了prompt参数,将做为输入的提示符。

reduce(function, iterable[, initializer])
将两个参数的function函数循环应用到迭代器的各项,例如reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])相当于((((1+2)+3)+4)+5)。如果提供了可选的initializer参数,则会将它放在迭代器的前面进行运算。

reload(module)
重新加载之前已经导入的模块。当你在设计一个模块,并用外部编辑器更新了它的代码时,可以用reload重新导入此模块,来验证模块的正确性。
reload执行时候的具体细节这里就不描述了。

repr(object)
返回一个尽量包含object的信息的字符串,其实交互式python解释器,在输入一个对象回车的时候,就是返回对象的repr值。
对于很多常见的对象,返回的值都尽可能地使得能够被eval解释并返回对象本身;另外的就尽量包含所在的域信息和类型或者地址等。
一个类可以通过__repr__()方法自定义repr的返回值。

reversed(seq)
返回一个倒序的迭代器。seq要么支持 __reversed__() 方法,要么支持取项的操作(也就是支持__len__()方法和从0开始的整数值的__getitem__()方法)。
例子:

>>> reversed(range(5))
<listreverseiterator object at 0x80a658c>
>>> [i for i in reversed(range(5))]
[4, 3, 2, 1, 0]

round(x[, n])
将浮点数x四舍五入取整到小数点后n位小数。n的默认值是0,也就是取整。

set([iterable])
由迭代器iterable返回一个集合对象,集合中的元素是随机顺序,但是不重复的。此函数在去掉列表的重复项的时候,特别有用:

>>> l = [1, 2, 3, 2, 4, 3]
>>> set(l)
set([1, 2, 3, 4])
>>> list(set(l))
[1, 2, 3, 4]
>>> ''.join(set("hello"))
'helo'

setattr(object, name, value)
此函数和getattr()配合使用,setattr(x, 'foobar', 123)相当于x.foobar = 123

slice([start], stop[, step])
返回一个分片对象,分片对象就只包含了start, stop, step这3个信息,它在python内部和一些第三方库中广泛被使用,其实类似a[1:3]这样的操作也会生成分片对象。如果省略startstep,将默认为None。
可以看到下面两者其实是等效的:

>>> range(5)[slice(1, 4, 2)]
[1, 3]
>>> range(5)[1:4:2]
[1, 3]

sorted(iterable[, cmp[, key[, reverse]]])
返回一个排序后的列表,用于排序的元素来自iterable,后面的参数控制排序的过程。
cmp是自定义的比较函数,接受两个参数,返回负数表示第一个参数较小,返回0表示两者一样大,返回正数表示第一个参数较大。
key可以理解为每个参数的求值函数。如果提供了key,则在比较前,先对每个先用key进线求职,对结果再进行排序,但是返回的排序后的结果还是之前的值。
reverse如果是True,则按降序排列,默认是从小到大的升序。
看例子:

#正常的排序
>>> sorted([1, 5, 3, 4, 6])
[1, 3, 4, 5, 6]
#倒序
>>> sorted([1, 5, 3, 4, 6], reverse=True)
[6, 5, 4, 3, 1]
#提供了key,结果是除3的余数谁最小,谁就排前
>>> sorted([1, 5, 3, 4, 6], key=lambda x: x%3)
[3, 6, 1, 4, 5]
#用cmp实现的版本
>>> sorted([1, 5, 3, 4, 6], cmp=lambda x,y: x%3 - y%3)
[3, 6, 1, 4, 5]

值得注意的是,虽然cmpkey都可以实现上面的除3余数排列,但是因为cmp要对每次比较的两个元素都调用一次函数,所以,效率不如key来得高。

staticmethod(function)
返回一个静态方法function
要声明一个静态方法,需要使用如下的语法:

class C:
    @staticmethod
    def f(arg1, arg2, ...): ...

静态方法可以被类本身调用(例如:C.f())也可以被类的对象调用(例如:C().f())。

str([object])
返回一个精确可打印的字符串,来说明object。和repr(object)不同,str(object)返回的字符串不一定能被eval()执行来得到对象本身,str(object)的目标只是可打印和可读。

sum(iterable[, start])
iterablestart做为初值的基础上进行累加。start的默认值为0。
注意此方法不能对字符串进行相加(连接)操作,连接字符串还是用''.join(sequence)好了。另外,sum(range(n), m)等价于reduce(operator.add, range(n), m),要更精确地对浮点数进行累加,请使用math.fsum()

super(type[, object-or-type])
返回一个指代type的父类或者兄弟类的对象,可以用这个对象间接地调用父类或者兄弟类的方法。在有复杂的类继承关系结构的时候,会很有用。用到的时候可以自行研究下这文章

tuple([iterable])
返回一个tuple对象(元组),元素来自iterable。如果省略参数,将返回空的元组。

type(object)
返回object的类型,返回值本身是个“类型对象”。注意,进行类型判断建议使用isinstance()函数。

>>> type(1)
<type 'int'>
>>> type(type(1))
<type 'type'>
>>> type(1) == int  #非常不建议这样的使用方法。
True
>>> isinstance(1,int)   #建议这样使用。
True

type(name, bases, dict)
不同于上面那个一个参数的type,这个方法用于快速构造一个类,传入的3个参数将分别转化为所得到的类的__name__,__bases__和__dict__。
例如,下面这两个X是等价的:

>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

unichr(i)
返回一个单个字符的unicode串,此字符的unicode码值为i。对于Unicode,此函数也是ord()的反函数。i的范围由python解释器的编译环境决定。

unicode([object[, encoding[, errors]]])
返回一个代表object的unicode字符串。
如果给定了encoding和/或errors,将用ascii或者encoding指定的编码对object进行解码,在遇到解码错误的时候,errors的值将影响函数的下一步动作:如果errors的值是'strict'(默认值),将会抛出ValueError错误;如果errors的值是'ignore'将会忽略错误,继续解码;如果errors'replace',将使用U+FFFD来替换当前字符。
看个例子,我的utf8环境下:

>>> unicode('我是bones7456', encoding='utf8')
u'\u6211\u662fbones7456'
>>> print unicode('我是bones7456', encoding='utf8') #可见解码成功
我是bones7456
>>> unicode('我是bones7456') #不指定编码方式,将默认使用ascii解码,失败了。
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)
>>> unicode('我是bones7456', errors='ignore') #忽略失败,可以得到英文数字部分
u'bones7456'
>>> unicode('我是bones7456', errors='replace') #替换的话,会加上一堆???哈哈。
u'\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdbones7456'
>>> print unicode('我是bones7456', errors='replace')
������bones7456

如果没有后面的俩参数,unicode()的行为类似于str(),只不过返回的unicode字符串而已。
如果,object对象提供了__unicode__()方法,将调用此方法来返回一个可被用户自定义的unicode串。

vars([object])
如果省略objectvars()和locals()类似,如果object是模块、类、类的对象或者其他还有__dict__属性的对象的话,就返回它的__dict__。

xrange([start], stop[, step])
此函数和range()非常类似,但是返回的不是一个列表,而是一个xrange对象。xrange对象在被引用时,也能生成列表的各项,但是这些项不是同时存在于内存里的。xrange和range比的优势是更小巧,更快。

zip([iterable, ])
哈,说到这个函数,我还给python官方文档提过一个bug,因为之前版本的文档的示例代码有点小问题,前因后果可以看这里
zip函数返回一个元组的列表,第i个元组,就包含了每个iterable的第i项。如果参数的各iterable不一样长,会别截取到最短的值,这个值也就是结果列表的长度。
然后,zip内如果有个 * 开头,将会执行逆运算(unzip),示例:

>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> zipped = zip(x, y)
>>> zipped
[(1, 4), (2, 5), (3, 6)]
>>> x2, y2 = zip(*zipped)
>>> x == list(x2) and y == list(y2)
True

__import__(name[, globals[, locals[, fromlist[, level]]]])
此函数被import语句调用。代码中很少会用到这个函数,除非你要import的模块名是运行时才可知的。就不详述了。

欧拉工程

咳咳,我做为非正常网民网瘾戒除中心砖家,正式给介绍介绍一下这个欧拉工程
呃。。。还是请我们的校长──lerosua──来介绍吧~下面引自这篇博文

欧拉工程是一个用编程来解决一连串数学问题的项目。发现它对训练数学及编程挺有作用的。网上也有许多人做上面的题目并发布自己的算法。
上面共有261题,分几个等级,一级比一级难。我看了第261题的提交,目前只是22个。完成它绝对是时间及精力及耐力及智力的较量。
于是本人决定将此作为一个项目来做,逐步进阶。
骨头与xiooli同学也并肩作战。
我们解决的问题的源码将会发布在以下svn中。欢迎审阅.
http://nrciz.googlecode.com/svn/trunk/projecteuler

其实,之前(几个月前吧)我就已经做过几题了。可以说,正是因为欧拉工程,我才喜欢上python的,当我看到第一题用一句

sum([n for n in range(1000) if n%3==0 or n%5==0])

就完美地搞定了,是多么地流畅,多么地自然,我就喜欢上了这门语言~
我们现在已经在逐步把很多题目翻译成中文了,所以,大家有空就去这里看看吧~最好是大家都能参与到其中来哦~

神一样的Quine

有个很牛B的小日本搞出来的东西

# ruby
l=92.chr;eval s="s=s.dump[r=1..-2].gsub(/("+l*4+"){4,}(?!\")/){|t|'\"+l*%d+\"'%(t
.size/2)};5.times{s=s.dump[r]};puts\"# python\\nprint(\\\"# perl\\\\nprint(\\\\\\
\"# lua"+l*4+"nprint("+l*7+"\"(* ocaml *)"+l*8+"nprint_endline"+l*15+"\"-- haskel
l"+l*16+"nimport Data.List;import Data.Bits;import Data.Char;main=putStrLn("+l*31
+"\"/* C */"+l*32+"n#include<stdio.h>"+l*32+"nint main(void){char*s[501]={"+l*31+
"\"++intercalate"+l*31+"\","+l*31+"\"(c(tail(init(show("+l*31+"\"/* Java */"+l*32
+"npublic class QuineRelay{public static void main(String[]a){String[]s={"+l*31+"
\"++intercalate"+l*31+"\","+l*31+"\"(c("+l*31+"\"brainfuck"+l*64+"n++++++++[>++++
<-]+++++++++>>++++++++++"+l*31+"\"++(concat(snd(mapAccumL h 2("+l*31+"\"110"+l*31
+"\"++g(length s)++"+l*31+"\"22111211100111112021111102011112120012"+l*31+"\"++co
ncatMap("+l*32+"c->let d=ord c in if d<11then"+l*31+"\"21002"+l*31+"\"else"+l*31+
"\"111"+l*31+"\"++g d++"+l*31+"\"22102"+l*31+"\")s++"+l*31+"\"2100211101012021122
2211211101000120211021120221102111000110120211202"+l*31+"\"))))))++"+l*31+"\","+l
*63+"\""+l*64+"n"+l*63+"\"};int i=0;for(;i<94;i++)System.out.print(s[i]);}}"+l*31
+"\")))))++"+l*31+"\",0};int i=0;for(;s[i];i++)printf("+l*63+"\"%s"+l*63+"\",s[i]
);puts("+l*63+"\""+l*63+"\");return 0;}"+l*31+"\");c s=map("+l*32+"s->"+l*31+"\""
+l*63+"\""+l*31+"\"++s++"+l*31+"\""+l*63+"\""+l*31+"\")(unfoldr t s);t[]=Nothing;
t s=Just(splitAt(if length s>w&&s!!w=='"+l*31+"\"'then 501else w)s);w=500;f 0=Not
hing;f x=Just((if x`mod`2>0then '0'else '1'),x`div`2);g x= reverse (unfoldr f x);
h p c=let d=ord c-48in(d,replicate(abs(p-d))(if d<p then '<'else '>')++"+l*31+"\"
."+l*31+"\");s="+l*31+"\"# ruby"+l*32+"n"+l*31+"\"++"+l*31+"\"l=92.chr;eval s=\"+
(z=l*31)+\"\\\"\"+s+z+\"\\\""+l*31+"\"++"+l*31+"\""+l*32+"n"+l*31+"\""+l*15+"\""+
l*7+"\")"+l*4+"n\\\\\\\")\\\")\"########### (c) Yusuke Endoh, 2009 ###########\n"

这段ruby代码,相当牛,如作者所说,运行这段ruby,生成一段python代码,再运行python代码,生成一段perl代码,再运行perl代码,生成一段lua代码。。。这样一直下去,经过11种语言,最后。。。居然又能重新得到之前的ruby代码。。
这11种语言是:ruby 1.8.7-p72、Python 2.5.2、perl v5.10.0、Lua 5.0.3、OCaml 3.10.2、ghc-6.8.2、gcc 4.3.2、java “1.5.0_17″、beef 0.0.6-2、whitespace 0.3-2、unlambda 2.0.0-5。
est这里看到了介绍,实在忍不住,把所有解释器都装上验证了一把,果然是可以的。彻底无语了,特此把中间结果都打包放上来,供大家瞻仰。
ubuntu下也想自己试试的话,得确保有这些包: ruby lunar lua50 ocaml-interp ghc6 sun-java5-jdk beef whitespace unlambda

PS: 我以为 brainfuck 就已经很BT了,没想到还有更BT的 whitespace ,彻底无语了。

Bash其实也可以做CGI用

标题其实是“废话”。因为,其实稍微熟悉web原理的都知道,理论上,所有可执行的程序/脚本,都可以被server程序(例如apache/mini_httpd/lighttpd/nginx等)调用,只要能够输出HTML,都可以成功。
而这个用bash做CGI的想法,纯属闲着无聊的时候,瞎想出来的。不过有这个想法的,我肯定不是第一人,这里这里都有类似的描述。
不过,我这个Web From Scratch的过程还颇有点波折,试了蛮久才成功,就在此记录一下。
其实,说白了都是也就一句话:扩展名不能随意取。当然这个只是针对我这个主机的apache来说的,其他的server可能有其他的规则,暂且不做定论。
之前我一直把我的脚本文件名写成index.sh,结果怎么试都不行,后来无意中改成inedx.py(虽然里面还是#!/bin/bash开头的)居然就可以了,于是我就找到原因了,现在我改成个更通用的index.cgi了,哈哈。
另外关于.htaccess,由于自己也是一知半解,就不讨论了,有兴趣的话,可以看看这里,这里只贴下我用到的代码,可以把对此域名的任何路径的请求都让index.cgi来处理。

RewriteEngine on
DirectoryIndex index.cgi
RewriteCond %{REQUEST_URI} !(index.cgi)
RewriteRule ^(.*)$ index.cgi/$1 [L]

来看看我的bash,就是简单地输出服务器信息:

#!/bin/bash
 
#下面这两行头一定要有,不然可是会引发 500 Internal Server Error 错误的。
echo "Content-type: text/html"
echo ""
 
echo "<h1>Bash CGI works!</h1>"
env | while read line;do
	echo "<b>"
	echo "$line" | cut -d= -f 1
	echo "</b>=<i>"
	echo "$line" | cut -d= -f 2-
	echo "</i><br>"
done

最近几天,可以点击 http://hzlug.org/abc?xxx=yyy 观察这个脚本运行的效果,以后可以把这个地址做它用,嘿嘿。
可以看到 REDIRECT_URL 和 QUERY_STRING 这两个环境变量分别是路径和参数,这样理论上就可以用一个脚本来完成很复杂的功能了~
至于HZLUG到底会做成什么样子,还请大家多多提意见哦~

用python来收发邮件

由于python的模块很强大,用python来收发邮件,就显得很轻松了。
python不仅有email模块,还有专门处理popsmtp甚至imap的模块。
下面就来演示一下这几个的用法,先看看怎么用pop3和email模块来收邮件。

>>> import poplib
>>> p=poplib.POP3_SSL('pop.gmail.com')  #如果服务器不需要启用SSL,那么只需要用 poplib.POP3('xxx.com') 就好了。
>>> p.user('linuxcn.ibot')
'+OK send PASS'
>>> p.pass_('密码')
'+OK Welcome.'
>>> p.stat() #统计状态,返回一个元组,第一个表示几封邮件,第二个表示一共几个字节
(2, 4543)
>>> p.list() #返回每个邮件的状态
('+OK 2 messages (4543 bytes)', ['1 3679', '2 864'], 15)
>>> p.retr(2) #获取某个邮件的全文,这个邮件就是我自己给另一个测试账号发的邮件,元组里包含了邮件头和邮件的正文。
('+OK message follows', ['Delivere
...为了排版方便,此处有删节...
e--'], 869)
>>> mailsrc='\n'.join([l for l in p.retr(2)[1]])
>>> print mailsrc #如果上面还看不出啥的话,这样看应该就明显了。
Delivered-To: linuxcn.ibot@gmail.com
Received: by 10.216.47.83 with SMTP id s61cs422102web;
        Sat, 15 Aug 2009 07:58:32 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.114.180.16 with SMTP id c16mr2819847waf.57.1250348310512; Sat, 
	15 Aug 2009 07:58:30 -0700 (PDT)
Date: Sat, 15 Aug 2009 22:58:30 +0800
Message-ID: <ae14629e0908150758n49649527xf326b8c1a60f36bf@mail.gmail.com>
Subject: =?UTF-8?B?5rWL6K+V5Li76aKY?=
From: LLY <bones7456@gmail.com>
To: linuxcn.ibot@gmail.com
Content-Type: multipart/alternative; boundary=001636417e2fe4c3a904712f669e
 
--001636417e2fe4c3a904712f669e
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: base64
 
5rWL6K+V5q2j5paHCg==
--001636417e2fe4c3a904712f669e
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: base64
 
5rWL6K+V5q2j5paHPGJyPgo=
--001636417e2fe4c3a904712f669e--
>>> p.quit() #收完邮件了,理论上是要退出一下的
'+OK Farewell.'
>>> import email #源码得到了,现在轮到email模块登场了
>>> e=email.message_from_string(mailsrc) #这个就可以把邮件源码转换成一个email的对象了。
>>> print e['subject'] #这个是编码后的主题
=?UTF-8?B?5rWL6K+V5Li76aKY?=
>>> print email.header.decode_header(e['subject'])[0][0] #这个是解码后的主题
测试主题
>>> e.get_payload() #获取当前email的有效部分,可以看到有这个邮件有两个部分,从上面的邮件源码,我们也可以得知,此邮件确实有text/plain 和 text/html 两部分
[<email.message.Message instance at 0xb751102c>, <email.message.Message instance at 0xb751172c>]
>>> e.get_payload(0) #获取有效部分的第一个,这个还是一个email对象的实例。
<email.message.Message instance at 0xb751102c>
>>> type(e)
<type 'instance'>
>>> type(e.get_payload(0))
<type 'instance'>
>>> import base64 #解码邮件正文还得用上base64模块
>>> print base64.decodestring(e.get_payload(0).get_payload()) #解码正文的第一部分
测试正文
 
>>> print base64.decodestring(e.get_payload(1).get_payload()) #解码正文的第二部分,是html,看来只多了个br而已,哈哈
测试正文<br>

当然,上面的例子只是最简单的,复杂的附件什么的,也可以用库里的相关函数一一搞定的。另外,看文档的意思,传了get_payload的第二个参数按理应该能自动调用base64模块解码的,但是不知道为什么,我没有试验成功,只能自己手工解了~
下面,再来看看怎么用smtp发邮件,如果是复杂的邮件,也需要建立一个email对象的实例,把正文/附件等一一放上去就好,但是作为演示,我们就用邮件的源码来发一个纯手工的,哈哈

>>> import smtplib #加载模块
>>> msg='To: linuxcn.ibot@gmail.com\r\nFrom: my@localhost\r\nSubject: test\r\n\r\nthis is msg body\r\n' #邮件的源码
>>> s=smtplib.SMTP('smtp.163.com') #同样这里也有SMTP_SSL,到底用什么要看服务器的设置
>>> s.login('bones7456','密码') #一般都要登录了,才能发邮件,login里提供两个参数,用户名和密码
(235, 'Authentication successful')
>>> s.sendmail('bones7456@163.com','linuxcn.ibot@gmail.com',msg) #发送邮件,三个参数分别是 from/to/msg,就这名简单就成功了。。
{}
>>> s.quit()
(421, 'closing transmission channel')

就这么几行,一封邮件就发出去了,够简单吧。。。更详细的内容就看官方文档吧~

JSON数据格式简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。这些特性使JSON成为理想的数据交换语言。
以上摘自JSON官方介绍,这个格式是如此地简单,以至于光看这篇官方文档就可以完全掌握了,而无须其他过多地介绍了。
最初看到这个JSON,是在twitter上,这种小交互量的场景很适合用JSON,比XML更轻巧些。今天,看到新闻,taobao也搞了个开放平台,里面也支持JSON和xml这两种格式,所以我觉得这个格式以后会越来越流行,特发此文关注下。

折腾py2exe

序: 恩,这个确实挺折腾的,几乎一个下午都在搞py2exe,加上晚上又搞了一会儿,总算勉强让gmbox的exe可执行文件运行起来了…

首先,为什么要折腾py2exe呢? 其实就是为了那个gmbox,在设计之初,我就让它打算跨平台支持windows的.为此我也折腾过windows的python运行环境.但是如果那样的话,有windows的朋友要用gmbox,就必须安装python、GTK+库、PyCairo、PyGObject和PyGTK,这可不是一般的烦.所以有市场就会有需求,像我这样的问题我肯定不是第一个遇到的.于是就有了py2exe.
py2exe实际上是python distutils的一个扩展,使用py2exe,你必需有一个windows的环境,安装了python和你程序用到的依赖包(例如gmbox的pygtk等),也就是说,在这个”开发环境”你要能够运行起你的程序,然后下载py2exe,安装到python的lib目录下,就可以用来生成目标exe文件了,以后你只要把生成的东西复制到其他windows的机器,就可以脱离python运行环境和库的依赖了.是不是很爽呢?
虽说网上都说py2exe用起来很简单,但是我还是折腾了不少时间,可能是因为gmbox比较特殊:同时有CLI和GUI,有图标和数据文件,有自己的lib库等,当然更可能是因为我太菜了…嘿嘿..
不管怎么说,在这里记一下折腾的步骤,也许可以让后人少走点弯路:

  1. 打包文件(gmbox的打包文件在这里,建议边参考代码,边看下面的内容)最好要和你的主程序文件放在同一目录下.我曾经试着把打包文件放在上级目录,结果在搜索依赖库的时候遇到了很多麻烦.
  2. 打包文件本身其实也是一个python的程序,虽然一般只有一个setup函数.此函数可以有 name,description,version,console,windows,options,data_files 等参数.
  3. 要生成一个或多个命令行的exe,setup需要一个List型的名为console参数,指名源py文件.
  4. 要生成一个或多个GUI的exe,setup需要一个List型的名为windows参数,指名源py文件.List的每项都可以有图标等选项,就是icon_resources哪里,图标也是个Tuple的List,但是windows一般只会显示出第一个图标,所以一个就够了.
  5. 如果你想用png文件直接当图标,那很可能会在生成exe的时候,引起python崩溃,所以还是赶紧转成ico吧
  6. 如果你和我一样,生成的GUI的exe文件,在执行的时候,报 ImportError: No module named cairo ,那最好在setup函数里加上options,里面有include cairo的选项.
  7. 如果你也有数据文件(比如glade文件,图片文件等),py2exe默认并不会帮你一起打包,你需要在setup函数中加传data_files参数.data_files需要是一个List,每项可以是直接指向数据文件的字符串,也可以是一个Tuple;如果是前者,数据文件会直接在生成的dist目录下面,如果是后者,Tuple的第一个元素是目标目录,第二个元素是是个源文件名的List,效果是把所有的源文件存放在dist下的指定的目标目录下.(呃,好长好拗口…)
  8. 在py2exe生成的环境中,__file__这个应用将失效,这个问题的解决方法可以参看这里.个人觉得Alternate Solution更加靠谱.
  9. 好了,现在如果你已经写好了打包文件(win_packer.py),也对源代码进行了应有的修改,你就可以执行 python win_packer.py py2exe 来生成exe了,执行此命令会在当前目录下多出一个 dist 目录,你只要把dist目录复制出来,运行里面的exe,就可以了
  10. 最后,如果你的GUI程序还是运行不了,你还需要手工将你的GTK+安装安装目录(默认是C:\GTK)下的etc和lib目录复制到dist目录

当然,由于包含了一堆的运行环境,生成的dist目录将会是比较巨大的,现在的gmbox的exe居然有18M多,嘿嘿,应该还有优化和压缩的空间,改天再研究了~

给python增加IPC模块

前天介绍了linux进程间通信──消息队列,由于消息队列是linux内核提供的功能,所以理所当然地,C语言用起来最为方便:直接include一个sys/msg.h就可以了,所以之前的文章,我也是用C语言来做演示的.
那么我喜欢的Python能不能用IPC呢?能不能像C一样用消息队列呢?
其实,Python的内置模块里,并没有对IPC的支持,但是好在python的可扩展性超强,我们可以用swig来为python增加一个IPC模块.方法如下:
下载这个tar包(修改自这里),解压到任意目录,得到 ipc.h ipc.i Makefile 3个文件,如果你不是用的python2.6,需要修改一下 Makefile 里的路径(python3系列未测试).然后

make && make install

这样,你就可以在python里 import ipc模块了.
还是贴示例代码,保存为ipc_msg.py,并加可执行权限:

#!/usr/bin/env python
import sys,ipc
 
if len(sys.argv)==5 and sys.argv[3][0]=='s':
    ipc_key=int(sys.argv[1])
    msg_id = ipc.msgget(ipc_key,0666|ipc.IPC_CREAT)
    if 0 > msg_id:
        sys.exit(1)
    mbuf = ipc.msgbuf()
    mbuf.mtype = int(sys.argv[2])
    mbuf.mtext = sys.argv[4]
    if 0 > ipc.msgsnd(msg_id,mbuf,len(mbuf.mtext),0):
        sys.exit(3)
    print 'Send Success.'
elif len(sys.argv)==4 and sys.argv[3][0]=='r':
    ipc_key=int(sys.argv[1])
    mbuf = ipc.msgbuf()
    msg_id = ipc.msgget(ipc_key,0666)
    if 0 > msg_id:
        sys.exit(1)
    msg_len=ipc.msgrcv(msg_id,mbuf,2048,int(sys.argv[2]),ipc.IPC_NOWAIT)
    if 0 > msg_len:
        print 'No message received.'
        sys.exit(3)
    else:
        print 'Recv Success.(%d bytes):'% msg_len
        print mbuf.mtext
elif len(sys.argv)==3 and sys.argv[2][0]=='c':
    ipc_key=int(sys.argv[1])
    id_dsp = ipc.msqid_ds()
    msg_id = ipc.msgget(ipc_key,0666)
    if 0 > msg_id:
        sys.exit(1)
    if 0 > ipc.msgctl(msg_id,ipc.IPC_RMID,id_dsp):
        sys.exit(2)
else:
    print "usage: \n%s key type s message --to send message\n\
%s key type r --to receive\n\
%s key c --to clear queue"\
        %(sys.argv[0],sys.argv[0],sys.argv[0])

运行结果:

lily@LLY:~/test/ipc$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
 
lily@LLY:~/test/ipc$ ./ipc_msg.py 1 2 s abc
Send Success.
lily@LLY:~/test/ipc$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000001 65536      lily       666        3            1           
 
lily@LLY:~/test/ipc$ ./ipc_msg.py 1 2 r
Recv Success.(3 bytes):
abc
lily@LLY:~/test/ipc$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x00000001 65536      lily       666        0            0           
 
lily@LLY:~/test/ipc$ ./ipc_msg.py 1 c
lily@LLY:~/test/ipc$ ipcs -q
 
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
 
lily@LLY:~/test/ipc$

怎么样?和之前的C语言版本一模一样吧?