Python学习

本文主要记录一下Python的学习。

学习资料来源于:廖雪峰的官方网站

迭代

【练习】汉诺塔

汉诺塔的移动可以用递归函数非常简单地实现。

请编写move(n, a, b, c)函数,它接收参数n,表示3个柱子A、B、C中第1个柱子A的盘子数量,然后打印出把所有盘子从A借助B移动到C的方法。

1
2
3
4
5
6
7
8
9
def move(n,a,b,c):
if n == 1:
print(a +" move to "+ c)
else:
move(n-1 , a,c,b)
print(a +" move to "+ c)
move(n-1 , b,a,c)

move(3, 'A', 'B', 'C')

输出:

A move to C

A move to B

C move to B

A move to C

B move to A

B move to C

A move to C

列表生成

[x * x for x in range(1, 11) if x % 2 == 0]

生成[22,44…10*10]这样的list

[m + n for m in 'ABC' for n in 'XYZ']

两层循环,生成[‘AX’, ‘AY’, ‘AZ’, ‘BX’, ‘BY’, ‘BZ’, ‘CX’, ‘CY’, ‘CZ’]

这样的语法不可谓不丧病,语法看起来越NB,越万能就越难用,就越难懂!

【练习】

根据 L1 = [‘Hello’, ‘World’, 18, ‘Apple’, None] 生成[‘hello’, ‘world’, ‘apple’]列表

1
2
L2 = [a1.lower() for a1 in L1 if(isinstance(a1,str))]
print(L2)

isinstance判断是否为字符串,否则调用lower会报错

生成器

###【练习】

杨辉三角定义如下:

1
2
3
4
5
6
          1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1

把每一行看做一个list,试写一个generator,不断输出下一行的list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def triangles():
n = 0;
L = [1];
while n >= 0:
yield L
L = [L[x] + L[x+1] for x in range(len(L)-1)]
L.insert(0,1)
L.append(1)

n1 = 0
for t1 in triangles():
print(t1)
n1 = n1 + 1
if n1 == 10:
break

函数中带了yield说明这个函数就是一个生成器

函数式编程

高阶函数

简单说就是函数传参进函数,python有一点很nb的地方就是函数相当于一个变量一样,也能传参,也能赋值,这样的用法想象空间感觉还是很大的。

【练习】map()

假设用户输入的英文名字不规范,没有按照首字母大写,后续字母小写的规则,请利用map()函数,把一个list(包含若干不规范的英文名字)变成一个包含规范英文名字的list:
输入:[‘adam’, ‘LISA’, ‘barT’]
输出:[‘Adam’, ‘Lisa’, ‘Bart’]

1
2
3
4
5
6
7
def format_name(s):
s = s[0].upper() + s[1:].lower()
return s

#print format_name('barT')

print map(format_name, ['adam', 'LISA', 'barT'])

这边结合了一部分函数切片的知识,当时没想到,用了range遍历s的方式,不过最后报错:list和str不能相加。

【练习】reduce()

Python内置了求和函数sum(),但没有求积的函数,请利用recude()来求积:
输入:[2, 4, 5, 7, 12]
输出:245712的结果

1
2
3
4
def prod(x, y):
return x*y

print reduce(prod, [2, 4, 5, 7, 12])

reduce()用法
例如,编写一个f函数,接收x和y,返回x和y的和:
def f(x, y):
return x + y
调用 reduce(f, [1, 3, 5, 7, 9])时,reduce函数将做如下计算:
先计算头两个元素:f(1, 3),结果为4;
再把结果和第3个元素计算:f(4, 5),结果为9;
再把结果和第4个元素计算:f(9, 7),结果为16;
再把结果和第5个元素计算:f(16, 9),结果为25;
由于没有更多的元素了,计算结束,返回结果25。

【练习】filter()

请利用filter()过滤出1~100中平方根是整数的数,即结果应该是:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

1
2
3
4
5
6
7
import math

def is_sqr(x):
a = int(math.sqrt(x))
return a*a == x

print filter(is_sqr, range(1,101))

起初我是想平方根然后通过isinstance()方法去判断是不是整数的,不过没出来结果。这个方式挺巧妙的。

【练习】sorted()

对字符串排序时,有时候忽略大小写排序更符合习惯。请利用sorted()高阶函数,实现忽略大小写排序的算法。
输入:[‘bob’, ‘about’, ‘Zoo’, ‘Credit’]
输出:[‘about’, ‘bob’, ‘Credit’, ‘Zoo’]

1
2
3
4
5
6
7
8
9
10
11
def cmp_ignore_case(s1, s2):
a1 = s1.lower();
a2 = s2.lower();
if a1 > a2:
return 1
elif a1 < a2:
return -1
else:
return 0

print sorted(['bob', 'about', 'Zoo', 'Credit'], cmp_ignore_case)

总是就是先转换为小写或者大写再统一比较。

这个函数和java的comparable接口倒是有点类似。

上面几个内置的高阶函数用法需要注意的是他们的参数个数。

返回函数

请编写一个函数calc_prod(lst),它接收一个list,返回一个函数,返回函数可以计算参数的乘积。

1
2
3
4
5
6
7
8
9
def calc_prod(lst):
def cal(x,y):
return x * y
def calc():
return reduce(cal,lst)
return calc

f = calc_prod([1, 2, 3, 4])
print f()

倒是刚好用到了reduce()函数,通过函数返回可以先返回一个函数变量,然后执行这个真正的函数。(感觉很高端,暂时想不到用处)

闭包

内层函数引用了外层函数的变量(参数也算变量),然后返回内层函数的情况,称为闭包(Closure)。

闭包的特点是返回的函数还引用了外层函数的局部变量,所以,要正确使用闭包,就要确保引用的局部变量在函数返回后不能变。

因此,返回函数不要引用任何循环变量,或者后续会发生变化的变量。

1
2
3
4
5
6
7
8
9
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs

f1, f2, f3 = count()

因为内层函数引用了外层会变的参数,所以结果全部都是9。

返回闭包不能引用循环变量,请改写count()函数,让它正确返回能计算1x1、2x2、3x3的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
def count():
fs = []
for i in range(1, 4):
def cal(j):
def ca():
return j*j
return ca
a = cal(i)
fs.append(a)
return fs

f1, f2, f3 = count()
print f1(), f2(), f3()

也就是再定义一个ca()函数,这个函数引用的是cal()函数的不变参数,fs列表的元素都是函数,再执行打印结果。

这东西真心很绕啊。感觉有难度。

装饰器

Python的装饰器竟然当做完整的一个知识点讲解的。要知道java中装饰器就是一个设计模式而已。

不过了解了之后发现确实是Python的一个特性,估计用的也会比较多,总的来说就是存在一个既有函数,现在又想添加点什么东西在里面,可以通过类似wrapper的装饰器函数扩展这个函数。不过实现起来这么个思想感觉有点难度,类似下面这样:

1
2
3
4
5
6
7
def f1(x):
return x*2
def new_fn(f):
def fn(x):
print "call sth"
return f(x)
return fn

f1()函数是已经存在了的函数,现在想要在f1()函数中添加打印一句话的功能,这时候定义一个包装函数:new_fn(f),这个函数接受一个函数变量作为参数,函数内部定义一个内部函数,该函数接受一个参数,执行添加的功能,返回具体的f()函数的结果,而new_fn(f)函数返回内部函数,这时候我们调用new_fn(f)函数就拿到了一个函数变量,执行这个函数变量就相当于执行了原函数,也就是f1(x)。

这段话是我看了代码之后写出来的理解,如果让我凭空写出这段代码,我觉得目前而言还是有困难。暂时记住吧,不出意外以后应该会经常有这样的模式。

【练习】

请编写一个@performance,它可以打印出函数调用的时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
import time

def performance(f):
def per(*args,**kw):
print time.time()
return f(*args,**kw)
return per

@performance
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))

print factorial(10)

上面代码我自己写的,执行没通过。下面是答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import time
def performance(f):
def fn(*args, **kw):
t1 = time.time()
r = f(*args, **kw)
t2 = time.time()
print 'call %s() in %fs' % (f.__name__, (t2 - t1))
return r
return fn

@performance
def factorial(n):
return reduce(lambda x,y: x*y, range(1, n+1))
print factorial(10)

其实是我以为需要打印函数执行的当前时间,应该是执行效率测试吧。

第三方库

使用pip或者easy_install命令来安装第三方库,pip貌似老版本是没有的,而且安装python的时候要选择安装pip才会有。比如下面这样安装:

pip install Gittle

这样就安装了一个git的第三方库。

事实上,为了这一句命令我真是折腾了一天,因为我装的是3.4版本的python,环境是windows64位的,执行这个命令之后告诉我失败,原因是

Unable to find vcvarsall.bat

然后各种百度google,最后说是没安装vs,然后我就找到了2012版本的VS安装了。

还是不行。

然后继续查资料,告诉我Python查找VS编译版本的时候没找到2012,我就找到/Lib/distutils目录下面的msvc9compiler.py,修改下面代码:vc_env = query_vcvarsall(VERSION, plat_spec)
为:vc_env = query_vcvarsall(11.0, plat_spec),然后继续运行这个命令。

还是不行!原因是

python link failed with exit status 1120

真是气死。仅仅是安装一个第三方库真的是浪费了大量的时间。那么,怎么解决这个问题呢?

就是重新安装Python,卸载3.4版本,安装2.7版本,这时候你会发现还是安装失败……

不急,继续找资料,发现微软提供了一个编译器,专门用于Python2.7的,地址是:http://www.microsoft.com/en-us/download/details.aspx?id=44266,下载安装,再执行pip install Gittle,奇迹终于出现了……

总之呢,这个过程我觉得还是很痛苦的,对于刚学Python的人来说遇到这些问题一部分人可能会直接放弃的。希望Python不要最后败在版本问题上才好。

编码

编码问题我觉得有必要写一篇独立的文章介绍,毕竟涉及的东西真的很多,这里先简单写几个注意点。

Python代码一般使用utf-8编码,这样代码中即使有中文也可以处理,怎么使用呢?就在文件开头写:

1
2
#!/usr/bin/env python
# -*- coding: utf-8 -*-

第一行是告诉系统我这个文件是python文件(windows下不需要此行,不过为了统一都加上吧)

第二行是文件编码为utf-8,# coding utf-8三个元素必写,-*-这种东西是美观用的(我三观都毁了,明明变难读了好吧)。

普通的字符串’ABC’在Python内部都是ASCII编码,所以运行

1
2
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
print "我是中文"

结果是乱码。

中文字符串必须加上u,以表示该字符串为unicode编码,代码如下:

1
2
3
#!/usr/bin/env python
# -*- coding: utf-8 -*-
print u"我是中文"

结果就正确了。

注:python3.x开始不需要加u了,统一为unicode编码