目录
初识python(下)
5函数与应用
函数与问题分而治之
函数结构
def 函数名 ([参数列表]):
函数体
例子:
def prime(x):
if x<2:
return False
for k in range(2,int(math.sqrt(x)+1)):
if x%k == 0:
return False
return True
问题:文件中有多个日期格式的数据,一行一个,输出其中合法的日期数据。
读取文件
def read_file(fn):
f = open(fn,"rt") #打开文件
data = f.readlines() #读取文件
f.close() #关闭文件
return data
#判断日期合法性代码参考上一篇文章内容
输出
def display(data):
for item in data:
if legal_date(item): #判断日期是否合法函数
print(item, end="")
主体程序
if __name__ == '__main__':
display(read_file("test.txt"))
主体程序中的if语句的作用是: 当该python文件被其它文件import时不会执行,因为任意python文件中,__name__等于’__main__’。而被import时,该文件中输出的__name__则为文件名。
基于函数验证哥德巴赫猜想
def isPrime(n):
#.....参考上篇文章,或导入他人编写的高效算法
#import sympy
#sympy.isprime(123456761)
def goldbach(n):
if n < 2 or n % 2 == 1:
print(n, '不是一个大于2的偶数')
return
for p in range(2, n):
if isPrime(p) and isPrime(n - p):
print(n, '=', p, '+', n - p)
return
print('哥德巴赫猜想好像有问题!')
def test_goldbach():
n = int(input("请输入一个大于2的偶数,或者输入0退出:"))
while n != 0:
goldbach(n)
n = int(input("请输入一个大于2的偶数,或者输入0退出:"))
十进制转二进制
方法一:
#python内置的函数bin可以将参数转换为二进制并返回
bin(19) #0b10011
#将一个数值的字符串转换成一个十进制整数,第二个参数指定进制
int('10011',2) #19
方法二:
def my_bin(n):
L = []
if n < 0: #考虑负数情况
n *= -1
L.append('-')
v = 1
while v <= n // 2:
v *= 2
while v > 0:
if n < v:
L.append('0')
else:
L.append('1')
n -= v
v //= 2
return ''.join(L)
思考题:
- 编写函数my_hex(n),将十进制整数n转换成十六进制数
- 编写函数hex2bin(n),将十六进制数n转换成二进制数
- 思路1:先将十六进制转换成十进制,再调用my_bin转成二进制
- 思路2:直接将十六进制转换成二进制
多种方法求圆周率
第一种 莱布尼兹级数法
PI = 3.141592654
base = 1
for k in range(1, 8):
base *= 10
pi = 0
tm = 1
for i in range(0, base):
pi += tm / (2 * i + 1)
tm = -tm
pi *= 4
print("%d\tpi=%.7f\t误差率(x10000)=%.5f" % (k,pi,abs(pi - PI) / PI * 10000))
```s
第二种 蒙特卡罗法
```python
import random
PI = 3.141592654
def gen_pi():
count, max_c = 0, 10000000
for i in range(0, max_c):
x, y = random.random(), random.random()
if x*x + y*y <= 1:
count += 1
return (count / max_c * 4)
if __name__ == '__main__':
sum_dif, k = 0, 20
for i in range(k):
pi = gen_pi()
dif = abs(pi - PI) / PI * 10000
sum_dif += dif
print("%d\tpi=%.7f\t误差率(x10000)=%.3f" % (i+1,pi,abs(pi - PI) / PI * 10000))
print("平均误差率(x10000)=%.3f" % (sum_dif / k))
Base64编码
递归思想和递归函数
例:求阶层
def factorial(n):
if n == 1:
return 1
else :
return n * factorial(n - 1)
例:计算列表中最大值
def my_max(L):
if len(L) == 1:
return L[0]
else:
return max(L[0], my_max(L[1:]))
#L[x:]表示从前截掉x个元素
#L[:x]表示从前截取x个元素
#L[a:b]表示从前截取b个再截掉a个
#L[:-x]表示从后截掉x个元素
#L[-x:]表示从后截取x个元素
例:斐波那契数列
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
递归的不足:
效率低下:函数的多次调用
改进斐波那契的递归方法
思路:F(n-1)的计算过程中已经知道了F(n-2)的值,直接在F(n)的计算中利用该值,避免重复计算。
def good_fibonacci(n):
if n <= 1:
return (n, 0)
else:
(a, b) = good_fibonacci(n-1)
return (a+b, a)
车队的公共钥匙盒
解法1
• 首先对K条记录排序
第一排序条件:返还时刻
第二排序条件:钥匙编号
• 生成时刻列表
得到所有的借用时刻、返还时刻
将上述时刻放到一个列表中去重并排序
• 利用时刻列表构造循环
还钥匙,并发还,则按照钥匙编号从小到大还
借钥匙,并发借,不会影响最终结果
#获取输入函数
def getInput():
keyCount, rowCount = map(int, input().split())
#map(func, iterable, ...)
#第一个参数func以参数序列中的每一个元素调用func函数,返回包含每次func函数返回值的新列表。
keys = [i for i in range(1,keyCount+1)] #初始化钥匙盒
datas = []
for i in range(rowCount): #借还记录
datas.append(list(map(int,input().split())))
return keys,datas
#预处理函数
def preProcess(datas):
for item in datas: #得到返还时刻
item[2] = item[1] + item[2]
datas.sort(key = lambda temp : (temp[2],temp[0]))
times = sorted(set([item[1] for item in datas])
| set([item[2] for item in datas]))
return datas, times
#模拟借还的函数
def process(times,keys,datas):
for time in times:
for item in datas: #返还
if item[2] == time:
keys[keys.index(0)] = item[0]
item[2] = -1
elif item[2] > time:
break
for item in datas: #借
if item[1] == time:
keys[keys.index(item[0])] = 0
item[1] = -1
return keys
if __name__ == '__main__':
(keys, datas) = getInput()
(datas, times) = preProcess(datas)
print(process(times,keys,datas))
解法2
• 每一条借还记录包括借操作和还操作
借的时刻在前
还的时刻在后
因此模拟时一条数据要被处理两次
• 直接把一条记录拆分成两条记录
钥匙编号 时刻 借操作
钥匙编号 时刻 还操作
• 不再需要times列表
keyCount, rowCount = map(int,input().split())
keys = [i for i in range(1, keyCount + 1)]
datas = []
for i in range(rowCount):
temp = list(map(int, input().split()))
datas.append([temp[0],temp[1],1]) #借记录
datas.append([temp[0],temp[1]+temp[2],2]) #还记录
datas.sort(key = lambda temp : (temp[1], -temp[2], temp[0]))
for item in datas:
if item[2] == 1:
keys[keys.index(item[0])] = 0
else:
keys[keys.index(0)] = item[0]
for item in keys:
print(item, end=" ")
练习题
1.在函数中yield语句的作用和return完全一样。(×)
补充内容:return和yield的区别
2.每个return语句只能返回一个值。(×)
3.定义函数时,带有默认值的参数必须出现在参数列表的最右端,任何一个带有默认值的参数右边不允许出现没有默认值的参数。(√)
4.在定义函数时,某个参数名字前面带有一个*符号表示可变长度参数,可以接收任意多个普通实参并存放于一个元组之中。(√)
5.在定义函数时,某个参数名字前面带有两个*符号表示可变长度参数,可以接收任意多个关键参数并将其存放于一个字典之中。(×)
6.Python不仅允许函数嵌套调用,还允许函数嵌套定义。(√)
7.如下代码被执行,运行结果是语法错误
print(get_max(3,5)) #应当在函数定义后调用函数
def get_max(num1,num2):
return num1 if num1>num2 else num2
8.如下程序的运行结果是 [100,2,3]
def exchange(lst):
lst[0]=100
lst=[1,2,3]
exchange(lst)
print(lst)
9.如下程序的运行结果是 [1,2,3]
def exchange(lst):
lst=[4,2,3]
lst=[1,2,3]
exchange(lst)
print(lst)
10.在函数内没有办法定义全局变量。(×,global可以)
11.在函数内部直接修改形参的值并不影响外部实参的值。(×)
12.在同一个作用域内,局部变量会隐藏同名的全局变量。(√)
13.如下程序的输出结果是 50 100 100
def myfun(x,y=200,z=100):
print(x,y,z)
myfun(50,100)
14.如下程序的输出结果是 (1,2,3)
def myvar1(*t):
print(t)
myvar1(1,2,3)
15.如下程序的输出结果是 25
def fun():
return lambda x,y:x*x+y*y
fx=fun() #没有这句会报错
print(fx(3,4))
16.如下代码使用了math模块中的sqrt,为了使得该代码正确,需要使用import,请给出和本次调用相适配的import的完整指令。(from math import sqrt 或 from math import * )
print(sqrt(3.5))
17.如下函数的返回值是。([2,5,8])
def test():
temp=[1,2,3,4,5,6,7,8,9,10]
return [x for x in temp if x%3==2]
18.如下函数的返回值是。(
[(1, 4), (2, 5), (3, 6)])
def test():
l1=[1,2,3]
l2=[4,5,6]
return list(zip(l1,l2))
19.如下代码的输出结果是_________
def number1Bit(x):
count = 0
while x:
count = count + 1
x = x & (x-1)
return count
print(number1Bit(17))
答:2。
&运算,按位取值,同为1则取1,否则取0
^=,异或运算,按位取值,不同取1,否则取0
&=,与运算赋值,按位取值,同为1则取1,否则取0
|=,或运算,按位取值,只要有一方为1则取1,否则取0
20.如下代码的输出结果是。(3 5)
a,b=5,3
a ^= b
b ^= a
a ^= b
print(a,b)
21.如下程序的运行结果是 (olleh)
def func(str1):
if len(str1)==1 or len(str1)==0:
return str1
else:
return str1[-1]+func(str1[:-1])
print(func("hello"))
22.如下程序的运行结果是 (3 2 6)
def fact(num):
ret=0;
if(num<=1):
ret=1;
else:
print(num,end=' ')
ret=num*fact(num-1)
return ret
print(fact(3))
23.如下程序的运行结果是 (6)
def lengthOfLastWord(sstr):
for word in sstr.split()[::-1]:
return len(word)
return 0
print(lengthOfLastWord("i like python"))
#sstr.split()是将sstr按空格切割成元组
#words[::-1]是返回倒置元素顺序后的words
#该代码 sstr.split()[::-1]
#实际上等价于列表['python','like','i']
#而遇return结束函数运行
#所以仅返回第一个元素的长度,即len('python')
24.如下代码的运行结果是 ([[1, 4], [2, 5], [3, 6]])
def transpose(lst):
test = [[0 for i in range(len(lst))] for j in range(len(lst[0]))]
#test = [[0,0],[0,0],[0,0]]
#len(lst) = 2, len(lst[0]) = 3
for i in range(len(lst)):
for j in range(len(lst[i])):
test[j][i]=lst[i][j]
return test
print(transpose([[1,2,3],[4,5,6]]))
25.如下程序的运行结果是。((4,6,3,0))
def statistics(str1):
num = 0
isah = 0
kong = 0
other = 0
for i in str1 :
if i.isdigit(): #是否为数字
num +=1
elif i.isalpha(): #是否为字母
isah +=1
elif i.isspace(): #是否为空格
kong +=1
else:
other +=1
return num,isah,kong,other
print(statistics(' das1 32 a2da'))
26.如下程序的运行结果是 [2, 4, 3, 1]
def sortArrayByParity( lst):
lst.sort(key=lambda t: t%2)
return lst
lst=[3,1,2,4]
sortArrayByParity(lst)
print(lst)
27.x = {1:2, 2:3, 3:4},则sum(x)的值为。(6)
sum(x.values())的值为。(9)
28.x = {i: str(i+3) for i in range(3)},得到的x是什么?({0: ‘3’, 1: ‘4’, 2: ‘5’})
sum(item[0] for item in x.items())的值为。(3)
6文件
读文本文件
1)read([size]) size 未指定则返回整个文件
2)readline() 读取一行
3)readlines([size]) 返回包含size行的列表, size 未指定则返回全部行
写文本文件
1)write() 将字符串写入文件
2)writelines() 将列表种的字符串写入到文件
自动关闭文件的写法
with open(文件名,打开方式) as 别名:
别名.文件读写函数
其它
1)f.tell():返回一个整数,表示当前文件指针的位置(就是到文件头的字节数)。
2)f.seek(偏移量,[起始位置]):用来移动文件指针。
偏移量: 单位为字节,可正可负
起始位置: 0-文件头(默认);1-当前位置; 2-文件尾
3)for line in f: print line :通过迭代器访问。
一个例子:简易英汉字典
文件加密解密
1维吉尼亚加密法
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
def encrypt(msg, key):
return translate_msg(msg, key, 'encrypt')
def decrypt(msg, key):
return translate_msg(msg, key, 'decrypt')
def translate_msg(msg, key, mode):
cipher = []
key_index = 0
key = key.upper()
for c in msg:
num = LETTERS.find(c.upper())
if num != -1:
if mode == 'encrypt':
num += LETTERS.find(key[key_index])
elif mode == 'decrypt':
num -= LETTERS.find(key[key_index])
num %= len(LETTERS)
if c.isupper():
cipher.append(LETTERS[num])
else:
cipher.append(LETTERS[num].lower())
key_index += 1
if key_index == len(key):
key_index = 0
else:
cipher.append(c)
return ''.join(ciper)
加密文本文件
#加密
def encrypt_file(src_file, des_file, key):
src_file_obj = open(src_file)
msg = src_file_obj.read()
src_file_obj.close()
msg_en = encrypt(msg, key)
des_file_obj = open(des_file, 'w')
des_file_obj.write(msg_en)
des_file_obj.close()
#解密
def decrypt_file(src_file, des_file, key):
src_file_obj = open(src_file)
msg = src_file_obj.read()
src_file_obj.close()
msg_de = decrypt(msg, key) #与加密方法仅此一处不同
des_file_obj = open(des_file, 'w')
des_file_obj.write(msg_de)
des_file_obj.close()
练习题
1.使用函数open()打开文件时,如果没有在参数中指定模式,则默认的模式为 ‘a+’。(×,默认为’r’)
2.使用open()函数以’w’模式打开文件进行写入操作时,如果原文件中有内容,则会被新写入的内容覆盖掉。(√)
3.read方法只能一次性读取文件中的所有数据。(×)
4.在Linux系统中,用于读取当前目录下的data目录中的file.txt文件的是(with open(‘data/file.txt’, ‘r’) as f:)
5.在Windows系统中,用于读取当前目录下的data目录中的file.txt文件的是(with open(‘data\file.txt’, ‘r’) as f:)
6.如果文件不存在,哪一种打开方式会报错?(‘r’)
7.文件对象的____方法用于将缓冲区的内容写入文件,但是不关闭文件。(flush)
8.如果首先以读模式打开一个文件,然后用seek方法把文件指针定位到文件的最后,再通过tell方法就可以得到一个文件的长度。(√)
9.write方法用于把字符串写入文本文件并在最后添加换行符。(×)
10.使用函数open()打开文件时,如果没有指定编码方式(即encoding),则该参数默认值总为’utf-8’。(×,在 Windows 简体中文版中默认编码是 ‘cp936’ 或 ‘gbk’ (在 python 执行环境中,将 MS936 ,CP936,gbk 视为同一套编码))
11.以写模式(‘w’)打开的文件无法进行读操作。(√)
12.如果文件a.txt中内容为空,则下面程序输出内容为None。(×)
with open('a.txt', encoding='utf-8') as f:
if not f:
print('None')
7异常处理
异常处理与程序健壮性
Python用于异常处理的关键字
try 监视异常
raise 抛出异常
except 捕获异常 #可以有多个
else 异常没有发生时
finally 异常收尾
一个完备的异常处理器
try:
语句块
execept 异常类型1[ as 错误描述]: #例:except ZeroDivisionError:(除数为0异常)
异常处理语句块1
……
execept 异常类型n[ as 错误描述]:
异常处理语句块n
execept:
默认异常处理语句块
else:
语句块
finaly:
语句块
错误处理的几条原则
- 程序遇到错误情况时还能以某种方式执行下去
- 不随意终止执行
- 不崩溃
- 遇到错误时,不出现任意的非预期行为
- 任何时候的行为都符合预期
- 在某个局部发生错误时,尽可能局部处理
- 不处理的异常将触底给父函数(主调函数)
异常处理的实际应用
常见异常
NameError 标志名错误
SyntaxError 语法错误
IndexError 索引错误
TypeError 类型错误
KeyError 键错误
ValueError 值异常
值域异常
def get_age():
while True:
try:
num = int(input("请输入您的年龄(18-60)")
if num < 18 or num > 60:
raise -1 #抛出异常可被'except int:'捕获
else:
return num
except ValueError:
print("请输入一个整数!")
except int: #捕获raise -1抛出的异常
print("您的输入不合法!")
利用异常跳出多重循环
try:
for i in range(10):
for j in range(i):
if i + j > 5:
raise -1
except:
print(i, j)
八皇后问题
小结
• 良好的异常可以大大提高程序的健壮性和容错性
在涉及到申请和释放资源时一定要使用异常确保资源被释放(打开关闭文件、数据库)
• 异常是可以嵌套的
内部捕获以及处理的异常不会向外抛出
内部不捕获的异常会往外抛出
• Python各种异常的基类是BaseException
可以创建用户自定义异常类
练习题
1.Python内建异常类的基类是。(BaseException)
2.一条except语句可以包含多个异常。(√)
3.如果一个函数func中的代码引发了异常,且该函数无处理异常的代码,那么该异常将会传递到调用func函数的地方。(√)
4.一个except后面紧跟着冒号,表示可以捕获任意类型的异常。(√)
5.使用异常可以避免程序出现逻辑错误。(×)
6.如下程序运行后如果输入5,a ,那么运行结果是。(程序出错)
def main():
a,b=eval(input()) #此处出错
try:
s=a/b
print(s)
except:
print("Divide 0!")
main()
7.在捕获到异常时,如果不想处理异常,而是让程序继续运行下去,可以使用pass语句。(√)
8.Python中,对于代码中的每个try,必须至少有一个except与它匹配。(×)
9.使用________关键字,可以保证之后的代码块无论是否发生异常,一定会被执行。(finally)