@ -0,0 +1,962 @@ | |||
{ | |||
"cells": [ | |||
{ | |||
"cell_type": "markdown", | |||
"id": "2b789064", | |||
"metadata": {}, | |||
"source": [ | |||
"# 实验9~实践12 参考答案" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "e1fa4e6d", | |||
"metadata": {}, | |||
"source": [ | |||
"## 实践9 元组和列表 \n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "d6b5ae4e", | |||
"metadata": {}, | |||
"source": [ | |||
"### 小试身手1\n", | |||
"(1)\t已知列表 L1 和L2,由 L1 和 L2 构造L3,并回答问题。 \n", | |||
"```python\n", | |||
" >>> L1=[1,2,3,4,5]\n", | |||
" >>> L2=[\"one\",\"two\",\"three\",\"four\",\"five\"]\n", | |||
" >>> L3=[[L1[1],L2[1]],[L1[2],L2[2]],[L1[3],L2[3]]]\n", | |||
"L3 的值是__________\n", | |||
"L3[1][1]的值是__________\n", | |||
"执行 L4=L3.pop(2)后,列表L3的值是__________,L4的值是__________。\n", | |||
"再执行L3.extend(L4),列表L3的值是__________。\n", | |||
"```" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 8, | |||
"id": "ee0db3a0", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"L1=[1,2,3,4,5]\n", | |||
"L2=[\"one\",\"two\",\"three\",\"four\",\"five\"]\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 11, | |||
"id": "61ec7df3", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"[[2, 'two'], [3, 'three'], [4, 'four']]" | |||
] | |||
}, | |||
"execution_count": 11, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"L3=[[L1[1],L2[1]],[L1[2],L2[2]],[L1[3],L2[3]]]\n", | |||
"L3" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 13, | |||
"id": "873f2572", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"'three'" | |||
] | |||
}, | |||
"execution_count": 13, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"L3[1][1]" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 14, | |||
"id": "d3ca6442", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"[4, 'four']" | |||
] | |||
}, | |||
"execution_count": 14, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"L4=L3.pop(2)\n", | |||
"L4" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 20, | |||
"id": "7910a864", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"[[2, 'two'], [3, 'three'], 4, 'four']" | |||
] | |||
}, | |||
"execution_count": 20, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"L3" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 16, | |||
"id": "f2a4aaa7", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"[[2, 'two'], [3, 'three'], 4, 'four']" | |||
] | |||
}, | |||
"execution_count": 16, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"L3.extend(L4)\n", | |||
"L3" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "a6e368db", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "de8197bc", | |||
"metadata": {}, | |||
"source": [ | |||
"(2)假设执行了如下语句\n", | |||
"```python\n", | |||
">>> s1=[0,1,2,3,4,5,6]\n", | |||
">>> s2=['SUN','MON','TUE','WED','THU','FRI','SAT']\n", | |||
"利用 s1、s2 和列表操作,创建下列结果的序列对象。\n", | |||
"```" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 18, | |||
"id": "d985454f", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"s1=[0,1,2,3,4,5,6]\n", | |||
"s2=['SUN','MON','TUE','WED','THU','FRI','SAT']" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 19, | |||
"id": "a5a002be", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"'SUN|MON|TUE|WED|THU|FRI|SAT'" | |||
] | |||
}, | |||
"execution_count": 19, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"#s3: 'SUN|MON|TUE|WED|THU|FRI|SAT'\n", | |||
"s3=\"|\".join(s2)\n", | |||
"s3" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 21, | |||
"id": "f8bb4c02", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"[3, 4, 3, 4, 3, 4]" | |||
] | |||
}, | |||
"execution_count": 21, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"#s4:[3,4,3,4,3,4]\n", | |||
"s4=s1[3:5]*3\n", | |||
"s4" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 23, | |||
"id": "bea9fa4d", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"[(0, 'SUN'), (1, 'MON'), (2, 'TUE'), (3, 'WED'), (4, 'THU'), (5, 'FRI'), (6, 'SAT')]\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"#s5: [(0,'SUN'),(1,'MON'),(2,'TUE'),(3,'WED'),(4,'THU'),(5,'FRI'),(6,'SAT')]\n", | |||
"s5=list(zip(s1,s2))\n", | |||
"print(s5)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "b7e2a784", | |||
"metadata": {}, | |||
"source": [ | |||
"## 小试身手2\n", | |||
"### (1)合并列表 \n", | |||
"使用随机函数randint(),建立两个长度为5的二维列表A和B。包含的数据如下:\n", | |||
"\n", | |||
"A列表:包含子列表 [学号,p成绩,m成绩],学号为从1开始递增的正整数,成绩为0-100之间的随机整数。\n", | |||
"\n", | |||
"B列表:包含子列表 [学号,q成绩],学号为从1开始递增的正整数,成绩为0-100之间的随机整数\n", | |||
"\n", | |||
"先需要将A,B两个列表数据进行合并,即将B列表中的‘q成绩’添加到A列表中的相同学号的子列表中。\n", | |||
"\n", | |||
"输入一个整数k,将B列表中的'q成绩'插入到A列表相同学号的子列表的k位置上。\n", | |||
"\n", | |||
"合并完成后,输出合并后的A列表。\n", | |||
"\n", | |||
"注意:为保证生成固定序列,本题需要使用同一个循环结构生成两个随机列表A和B,类似:\n", | |||
"```python\n", | |||
"for #######:\n", | |||
" #A列表添加 学号,p成绩,m成绩\n", | |||
" #B列表添加 学号,q成绩\n", | |||
"运行示例:\n", | |||
"A: [[1, 94, 58], [2, 6, 84], [3, 26, 42], [4, 39, 98], [5, 22, 18]]\n", | |||
"B: [[1, 33], [2, 82], [3, 29], [4, 26], [5, 24]]\n", | |||
"3\n", | |||
"[[1, 94, 58, 33], [2, 6, 84, 82], [3, 26, 42, 29], [4, 39, 98, 26], [5, 22, 18, 24]]\n", | |||
"```" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "c4adadb0", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"import random\n", | |||
"a=[]\n", | |||
"b=[]\n", | |||
"\n", | |||
"for i in range(1,6): \n", | |||
" a.append([i,random.randint(0,100),random.randint(0,100)]) \n", | |||
" b.append([i,random.randint(0,100)])\n", | |||
"print(\"A:\",a)\n", | |||
"print(\"B:\",b)\n", | |||
"p=int(input()) \n", | |||
"for i in range(len(a)):\n", | |||
" a[i].insert(p,b[i][1]) \n", | |||
"print(a) " | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "f9dd4a7a", | |||
"metadata": {}, | |||
"source": [ | |||
"### (2)成绩统计\n", | |||
"输入用空格分隔的两个正整数n和m,表示后面要接收m个人的n门课的成绩的输入。\n", | |||
"随后的m行,每行输入用空格分隔的n个成绩。题目保证m和n都在(0-100]之间。\n", | |||
"请统计\n", | |||
"(1)每门课的平均成绩。\n", | |||
"(2)每个学生的平均成绩\n", | |||
"(3)每门课的最高分" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 31, | |||
"id": "23a32ac5", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"n=3\n", | |||
"m=5\n", | |||
"63 87 76\n", | |||
"98 92 90\n", | |||
"86 83 82\n", | |||
"69 98 71\n", | |||
"77 74 92\n", | |||
"[[63, 87, 76], [98, 92, 90], [86, 83, 82], [69, 98, 71], [77, 74, 92]]\n", | |||
"每个学生的平均成绩: [75.33, 93.33, 83.67, 79.33, 81.0]\n", | |||
"每门课的平均成绩: [78.6, 86.8, 82.2]\n", | |||
"每门课的最高分: [98, 98, 92]\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"n,m = int(input(\"n=\")),int(input(\"m=\"))\n", | |||
"Scores = []\n", | |||
"for i in range(m):\n", | |||
" Scores.append([int(x) for x in input().split()])\n", | |||
"print(Scores)\n", | |||
"avgs=[]#每个学生的平均成绩\n", | |||
"for stu in Scores:\n", | |||
" avgs.append(round(sum(stu)/len(stu),2))\n", | |||
"print(\"每个学生的平均成绩:\",avgs)\n", | |||
"\n", | |||
"ksums=[0]*n #每门课的平均成绩\n", | |||
"kmaxs=[0]*n #每门课的最高分\n", | |||
"for stu in Scores:\n", | |||
" for i in range(n):\n", | |||
" ksums[i]+=stu[i]\n", | |||
" if stu[i]>kmaxs[i]:\n", | |||
" kmaxs[i] = stu[i]\n", | |||
"kavgs = [ round(x/m,2) for x in ksums]\n", | |||
"print(\"每门课的平均成绩:\",kavgs)\n", | |||
"print(\"每门课的最高分:\",kmaxs)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "84af1780", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "5262941a", | |||
"metadata": {}, | |||
"source": [ | |||
"## 实践10 集合和字典\n", | |||
"小试身手1 (略)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "471a3f13", | |||
"metadata": {}, | |||
"source": [ | |||
"### 小试身手2\n", | |||
"输入两个整数,在这两个整数组成的闭区间范围内生成100个随机整数,并统计出现数据的次数,按照生成随机数从小到大的顺序,每行输出一个生成的整数以及其出现的次数,以空格间隔。 \n", | |||
"```python\n", | |||
"例如:\n", | |||
"输入:\n", | |||
"3 5\n", | |||
"输出:\n", | |||
"3 36\n", | |||
"4 39\n", | |||
"5 25\n", | |||
"```" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 32, | |||
"id": "c82977a4", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"3 5\n", | |||
"3 36\n", | |||
"4 39\n", | |||
"5 25\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"import random\n", | |||
"dic = {}\n", | |||
"m,n=map(int,input().split())\n", | |||
"random.seed(10)\n", | |||
"\n", | |||
"for i in range (100):\n", | |||
" key = random.randint(m,n)\n", | |||
" if key in dic:\n", | |||
" dic[key] += 1\n", | |||
" else:\n", | |||
" dic[key] = 1\n", | |||
"for i in sorted(dic.keys()):\n", | |||
" print('{} {}'.format(i,dic[i]))" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "9d5b06fc", | |||
"metadata": {}, | |||
"source": [ | |||
"## 实践11 模块化程序设计 \n", | |||
"1.完整程序,程序功能实现统计1000以内每100个整数中素数的个数。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 2, | |||
"id": "dcba82ab", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
" 1- 100 有26个素数\n", | |||
"101- 200 有21个素数\n", | |||
"201- 300 有16个素数\n", | |||
"301- 400 有16个素数\n", | |||
"401- 500 有17个素数\n", | |||
"501- 600 有14个素数\n", | |||
"601- 700 有16个素数\n", | |||
"701- 800 有14个素数\n", | |||
"801- 900 有15个素数\n", | |||
"901-1000 有14个素数\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"from math import sqrt\n", | |||
"def isPrime(n):\n", | |||
" m=int(sqrt(n))\n", | |||
" for i in range(2,m+1):\n", | |||
" if n%i ==0:\n", | |||
" return False\n", | |||
" return True\n", | |||
"def countPrimes(start,end):\n", | |||
" c=0\n", | |||
" for n in range(start,end+1):\n", | |||
" if isPrime(n):\n", | |||
" c=c+1\n", | |||
" return c \n", | |||
"\n", | |||
"#主程序\n", | |||
"for start in range(1,1000,100):\n", | |||
" print(f\"{start:3d}-{start+99:4d} 有{countPrimes(start,start+99)}个素数\")\n", | |||
" " | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "e33847ab", | |||
"metadata": {}, | |||
"source": [ | |||
" ## 3.斐波那契数列\n", | |||
" ### (1) 单变量实现" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "efa91d0c", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def fib(n):\n", | |||
" if n<=2:\n", | |||
" return 1\n", | |||
" f1=f2=1\n", | |||
" while n>=3:\n", | |||
" f3=f1+f2\n", | |||
" f1,f2=f2,f3\n", | |||
" n=n-1\n", | |||
" return f3\n", | |||
"\n", | |||
"\n", | |||
"n=int(input(\"n=\"))\n", | |||
"print(fib(n))\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "4d0b3b92", | |||
"metadata": {}, | |||
"source": [ | |||
"## (2) 列表实现" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "48aa767e", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def fib( n ):\n", | |||
" if n<=2:\n", | |||
" return 1\n", | |||
" FL=[1,1]\n", | |||
" i=2\n", | |||
" while i<n:\n", | |||
" FL.append(FL[i-1]+FL[i-2])\n", | |||
" i=i+1\n", | |||
" return FL[-1] \n", | |||
" \n", | |||
"n=int(input(\"n=\"))\n", | |||
"print(fib(n))\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "5dee8615", | |||
"metadata": {}, | |||
"source": [ | |||
"## (3) 字典实现" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "4cde2e34", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def fib(n): \n", | |||
" a = 0\n", | |||
" b = 1\n", | |||
" dic = {} ## {} or dict() \n", | |||
" for i in range(n): ## range \n", | |||
" dic[i]=a ## a \n", | |||
" a,b=b,a+b\n", | |||
" \n", | |||
" return dic ## dic \n", | |||
" \n", | |||
"#调用函数生成斐波那契数列中的前20个斐波那契数\n", | |||
"fibonac = fib(20) ## fib(20) 调用函数 \n", | |||
" print(fibonac[key], end=\",\")\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "7494690d", | |||
"metadata": {}, | |||
"source": [ | |||
"## (4)递归函数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "5da061f6", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def Fib(n):\n", | |||
" if n==1 or n==2 :\n", | |||
" return 1\n", | |||
" return Fib(n-1)+Fib(n-2)\n", | |||
"\n", | |||
"n=int(input(\"n=\"))\n", | |||
"print(Fib(n))\n", | |||
"\n", | |||
"\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "fdcc945d", | |||
"metadata": {}, | |||
"source": [ | |||
"## (5) 优化的递归函数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "46f27dea", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"d_fib={1:1,2:1}\n", | |||
"def Fib(n):\n", | |||
" if n in d_fib :\n", | |||
" return d_fib[n]\n", | |||
" m=Fib(n-1)+Fib(n-2)\n", | |||
" d_fib[n]= m\n", | |||
" return m\n", | |||
"\n", | |||
"n=int(input(\"n=\"))\n", | |||
"print(Fib(n))" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "eecb1ba6", | |||
"metadata": {}, | |||
"source": [ | |||
"## 实践12 再谈Python函数 \n", | |||
"### 小试身手(1) \n", | |||
"阅读可变参数的vfunc函数,仿写milti函数实现返回所有参数的乘积" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 33, | |||
"id": "b4245d1d", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"720" | |||
] | |||
}, | |||
"execution_count": 33, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"def milti(*num):\n", | |||
" p=1\n", | |||
" for x in num:\n", | |||
" p=p*x\n", | |||
" return p\n", | |||
"milti(1,2,3,4,5,6)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "e2c1ee22", | |||
"metadata": {}, | |||
"source": [ | |||
"### 小试身手(2) \n", | |||
"1.判断奇数 \n", | |||
"判断奇数的函数lambda函数的定义和使用 \n", | |||
"(1)编写一个函数isOdd,能够判断一个整数是否是奇数,返回True或False。 \n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 35, | |||
"id": "af90b317", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"\n", | |||
"isodd = lambda x: x%2 == 1" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "519d41a6", | |||
"metadata": {}, | |||
"source": [ | |||
"(2)请写出输入一个数x,调用isOdd函数判断x是否是奇数的程序段" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 36, | |||
"id": "b75e2c05", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"x=8\n", | |||
"False\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"x = int(input(\"x=\"))\n", | |||
"print(isodd(x))" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "71637790", | |||
"metadata": {}, | |||
"source": [ | |||
"(3)请写出求50-100之间所有奇数的和的程序段,(使用isOdd实现奇数判断)。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 43, | |||
"id": "22543b83", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"s= 1875\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"s=0\n", | |||
"for x in range(50,100):\n", | |||
" if isodd(x):\n", | |||
" # print(x)\n", | |||
" s+=x\n", | |||
"print(\"s=\",s)\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "3ad734be", | |||
"metadata": {}, | |||
"source": [ | |||
"(4)请写出求50-100之间所有偶数的和的程序段(使用isOdd实现偶数判断)。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 41, | |||
"id": "23d97c6a", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"s= 1950\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"s=0\n", | |||
"for x in range(50,101):\n", | |||
" if not isodd(x):\n", | |||
" # print(x)\n", | |||
" s+=x\n", | |||
"print(\"s=\",s)\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "71f64423", | |||
"metadata": {}, | |||
"source": [ | |||
"2.构造整数:求由任意个整数上的个位数构造的新整数并输出。 \n", | |||
"具体要求 \n", | |||
"(1)编写lambda函数getLastBit,该函数返回正整数number的个位数,例如正整数1234,则返回4。 \n", | |||
"(2)编写可变参数的函数makeNumber,求任意个整数的个位数构成的一个新整数。 \n", | |||
"(3)输出(45、81、673、938)4个数的个位数构成的新整数,得到的新的整数为:5138。请填空完整程序。 \n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 45, | |||
"id": "2b89059b", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"新整数是5138\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"#定义lambda函数getLastBit返回一个数的个位数\n", | |||
"getLastBit=lambda number:number%10\n", | |||
"\n", | |||
"#定义一个可变参数的函数makeNumber,求任意个整数的个位数构成的一个新整数\n", | |||
"def makeNumber(*b):\n", | |||
" s=0\n", | |||
" for i in b:\n", | |||
" s=s*10+getLastBit(i)\n", | |||
" return s\n", | |||
"\n", | |||
"#输出(45、81、673、938)4个数的个位数构成的新整数。\n", | |||
"\n", | |||
"print(f\"新整数是{makeNumber(45,81,673,938)}\")\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "cf9617cc", | |||
"metadata": {}, | |||
"source": [ | |||
"## 实践13 递归函数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "b26582d3", | |||
"metadata": {}, | |||
"source": [ | |||
"### 小试身手" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "0bff8c63", | |||
"metadata": {}, | |||
"source": [ | |||
"1.使用递归求最大公约数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "0bd1eb79", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def gcd(m,n):\n", | |||
" if m%n == 0:\n", | |||
" return n\n", | |||
" else:\n", | |||
" return gcd(n,m%n)\n", | |||
"print(gcd(15,25))" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "d8006798", | |||
"metadata": {}, | |||
"source": [ | |||
"2.使用递归方法逆序输出一个整数中的数字" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 3, | |||
"id": "099d4634", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
" def reverseDisplay(value):\n", | |||
" if value>0:\n", | |||
" print(value%10,end=\"\")\n", | |||
" reverseDisplay(value//10)\n", | |||
" return" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 4, | |||
"id": "cc25a415", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"12345" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"reverseDisplay(54321)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"id": "0e80ecad", | |||
"metadata": {}, | |||
"source": [ | |||
"3.使用递归方法逆序输出一个字符串中的字母 " | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 6, | |||
"id": "a2ff79ad", | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"edcba" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"def reverseDisplay(s):\n", | |||
" if len(s)!=1:\n", | |||
" reverseDisplay(s[1:])\n", | |||
" print(s[0],end=\"\")\n", | |||
"reverseDisplay(\"abcde\")" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"id": "fcc70358", | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [] | |||
} | |||
], | |||
"metadata": { | |||
"kernelspec": { | |||
"display_name": "Python 3", | |||
"language": "python", | |||
"name": "python3" | |||
}, | |||
"language_info": { | |||
"codemirror_mode": { | |||
"name": "ipython", | |||
"version": 3 | |||
}, | |||
"file_extension": ".py", | |||
"mimetype": "text/x-python", | |||
"name": "python", | |||
"nbconvert_exporter": "python", | |||
"pygments_lexer": "ipython3", | |||
"version": "3.7.10" | |||
} | |||
}, | |||
"nbformat": 4, | |||
"nbformat_minor": 5 | |||
} |
@ -0,0 +1,988 @@ | |||
{ | |||
"cells": [ | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"# 实践12 再谈python函数\n", | |||
"\n", | |||
"1. 理解函数定义四要素:函数名、参数表、函数体和返回值,本章对每一个部分都进行了更深入的说明,尤其是一些特殊的用法;\n", | |||
"2. 理解 函数定义内外是两个不同的“**作用域**(*scope*)”,区分出全局变量和局部变量,需要充分理解其运作原理;\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"我们已经见过不少函数,也自己写过一些函数。我们已经理解函数的概念来自代数:从**输入参数**出发,**计算**出函数的**返回值**;我们也知道可以用 `def foo():` 来定义函数。其实函数的定义非常复杂,我们不太能够在第一次介绍时就讲清楚,所以之前我们就采取“先引入用起来”的方法,这也是一种知识上的“提前引用”。\n", | |||
"\n", | |||
"本章将围绕函数几个重要的要素深入看看。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 1. 为函数命名" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"哪怕一个函数内部什么都不干,它也得有个名字,然后名字后面要加上圆括号 `()`,以明示它是个函数,而不是某个变量。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 1, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def do_nothing():\n", | |||
" pass\n", | |||
"\n", | |||
"do_nothing()" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"这就是个“什么也不干”的函数,关键字 `pass` 就是什么也不干的意思。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"给函数命名(给变量命名也一样)需要遵循的一些规则如下:\n", | |||
"* 首先,名称不能以数字开头,能用在名称开头的只有大小写字母和下划线 `_`;\n", | |||
"* 其次,名称中不能有空格,如果一个名字里有好几个词汇,可以用下划线来分割(`do_nothing`),也可以用所谓 *Camel Case* 风格(*doNothing*),习惯上更推荐使用下划线;\n", | |||
"* 最后,绝对不能与 Python 语言的**关键字**(*keyword*)重复。\n", | |||
"\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"在程序里给变量、函数命名是个挺重要的事情,影响到程序的可读性,就像小说的语言,最好能有一种流畅清晰、又始终一致的**风格**(*style*)。为了让全世界的 Python 程序员都有相对一致的风格,Python 社区有专门的一套建议规范,放在专门维护 Python 语言特性的社区 [PEP](https://www.python.org/dev/peps/) 上:\n", | |||
"\n", | |||
"* [PEP 8 -- Style Guide for Python Code: Naming Conventions](https://www.python.org/dev/peps/pep-0008/#naming-conventions)\n", | |||
"\n", | |||
"> PEP,是 *Python enhancement proposal* 的缩写,每当有重要的语言特性新需求新想法,就放在这里,经过广大 Python 用户和开发者的讨论完善,在某个版本放进 Python 中。很多 PEP 早已从 *proposal* 毕业变成官方特性,但也还在这里保留着。PEP 8 就是一个古老的 *proposal*,现在已为大多数 Python 用户采纳。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 2. 没有、一个和多个参数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"函数可以没有参数,也可以有一个或者多个参数。\n", | |||
"\n", | |||
"没有参数就意味着,这个函数执行不依赖于输入,比如我们定义一个函数来在程序结束时打印一句退出提示:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 3, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Program exits. Bye.\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"def exit_info():\n", | |||
" print('Program exits. Bye.')\n", | |||
" \n", | |||
"exit_info()" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"注意即使没有参数,无论定义还是调用时,函数名后面的括号都是不可省略的,这是函数身份的标志。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"函数也可以有多个参数,调用时输入参数的值是严格按照参数的顺序去匹配的。比如我们写一个函数输出某年到某年之间的所有闰年:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 4, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"2000\n", | |||
"2004\n", | |||
"2008\n", | |||
"2012\n", | |||
"2016\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"def leap_years(begin, end):\n", | |||
" year = begin\n", | |||
" while year < end:\n", | |||
" if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:\n", | |||
" print(year)\n", | |||
" year += 1\n", | |||
" \n", | |||
"leap_years(2000, 2020)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"当我们调用 `leap_years(2000, 2020)` 时,输入两个参数值 2000 和 2020,按照顺序匹配函数定义 `leap_years(begin, end)`,于是 `begin = 2000` `end = 2020`。所以参数的顺序是不能搞错的,有些函数参数很多,要是开发过程中还调整过顺序的话,那简直就是灾难,所以一般情况下还是保持函数参数不要乱动为好。\n", | |||
"\n", | |||
"顺便说一句,判断闰年的算法虽然不难,但要写的简洁也不容易。建议你可以先自己思考和实现一遍,然后尝试搞清楚为啥上面代码里的那行 `if` 是对的。实际上闰年的判断有很多正确的写法,你应该尝试写出自己的版本并确认它的正确性。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 3. 没有、一个和多个返回值" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"和参数一样,Python 的函数可以没有返回值,也可以有一个或者多个返回值。\n", | |||
"\n", | |||
"上面的 `exit_info` 和 `leap_year` 也是没有返回值的例子,它们的效果都通过 `print` 函数来体现。实际上没有返回语句的函数,等价于在其最后有一句 `return None`,表示函数返回了一个空值 `None`,`None` 在 Python 中是一个合法的值,表示什么都没有,它在逻辑上等价于 `False`:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 5, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"False" | |||
] | |||
}, | |||
"execution_count": 5, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"bool(None)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"所以即使没有返回值的函数,也可以用在 `if` 后面做逻辑表达式,不过我们并不推荐这么做,因为可读性很差。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"大部分情况下函数是有返回值的,因为绝大部分情况下函数的作用都是做“数据处理”,从输入出发得到输出。\n", | |||
"\n", | |||
"一般情况下函数都只有一个返回值,我们已经见过不少例子;但 Python 也允许多返回值,比如我们想用一个函数来计算两个整数相除的商和余数,可以这么写:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 6, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"8 2\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"def idiv(a, b):\n", | |||
" quotient = a // b\n", | |||
" remainder = a % b\n", | |||
" return quotient, remainder\n", | |||
"\n", | |||
"q, r = idiv(50, 6)\n", | |||
"print(q, r)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"和多参数的情况类似,多返回值的情况下,赋值也是按照顺序匹配的,上面的代码中赋值语句左边的 `q` 匹配到第一个返回值,`r` 匹配第二个。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 4. 函数内与函数外:变量的作用域" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"下面的代码经常会把人搞晕:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 7, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"2\n", | |||
"1\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"def increase_one(n):\n", | |||
" n += 1\n", | |||
" return n\n", | |||
"\n", | |||
"n = 1\n", | |||
"print(increase_one(n))\n", | |||
"print(n)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"请你思考一下,为什么这段代码里的两个 `print` 函数输出分别是 2 和 1。\n", | |||
"\n", | |||
"这个问题就涉及到变量的**作用域**(*scope*)问题,也就是说在不同地方出现的同名变量和函数,可能是完全不同的两个东西:\n", | |||
"* 函数定义体中的变量的作用域是该函数内,程序的其他部分不知道其存在,这种变量叫**局部变量**(*local variable*);函数的输入参数也是局部变量,也只在函数定义体中有效;\n", | |||
"* 不在任何函数、类定义体中的变量的作用域是全局的,在任何地方都可以访问,这种变量称为**全局变量**(*global variable*);\n", | |||
"* 如果局部变量和全局变量同名,函数定义体内会优先局部变量,不会把它当做全局变量。\n", | |||
"\n", | |||
"这样我们就能理解上面代码输出的 2 和 1 了:\n", | |||
"* 第一个 `print()` 打印的是函数调用 `increase_one(n)` 的返回值,这个语句不在任何函数定义体中,所以它里面用到的变量都是全局变量:\n", | |||
" * 在调用 `increase_one()` 时参数 `n`,按照作用域原理,是全局变量 `n` 当时的值,也就是 1;\n", | |||
" * 在 `increase_one()` 函数定义内,参数 `n` 是输入参数即局部变量,带着传进来的值 1,经过加一之后返回,返回值是 2;\n", | |||
" * `print` 打印这个返回值,输出 2;\n", | |||
" * 这个过程中处理的都是局部变量,完全不影响全局变量 `n` 的值;\n", | |||
"* 第二个 `print()` 打印的是全局变量 `n` 的值,输出出 1。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"以上的文字,可能需要反复阅读若干遍;几遍下来,消除了疑惑,以后就彻底没问题了;若是这个疑惑并未消除,或者关键点并未消化,以后则会反复被这个疑惑所坑害,浪费无数时间。\n", | |||
"\n", | |||
"顺便说一句,上面这个例子用来说明作用域的概念很有用,但是平时写程序最好别这么写,减少重名的变量可以提升代码的清晰度和可读性。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"与此相关的,我们在介绍列表等数据容器时,会为上面的规则作出重要的补充,这里先留一个伏笔。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 5. 带缺省值的参数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"我们其实已经见过带缺省值的参数(*argument with default value*),这里我们更细致的看看这个特性。\n", | |||
"\n", | |||
"在函数定义中可以在某个参数后面用等号 `=` 给它一个缺省值,调用时可以省略传入这个参数的值,直接采用缺省值;当然也可以在调用时传入这个参数的值来覆盖掉缺省值。这种特性相当于给了这个函数两个版本,一个带某个参数,一个不带,不带的版本就当该参数是某个缺省值。看看下面的例子:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 8, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def greeting(name, msg='Hi'):\n", | |||
" print(f'{msg}, {name}!')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 9, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo!\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"greeting('Neo')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 10, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Good morning, Neo!\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"greeting('Neo', 'Good morning')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"一个函数可以有多个带缺省值的参数,但有一个限制:所有这些带缺省值的参数只能堆在参数表的最后,也就是说你定义的参数表里,出现一个带缺省值的参数,则它后面的都必须带缺省值。如果把上面的 `greeting()` 函数的两个参数调换一下,会扔出一个 `SyntaxError: non-default argument follows default argument` 的异常。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 6. 指定参数名来调用函数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"我们前面说过,调用函数时传入的参数值会严格按照顺序去匹配参数变量,第一个输入值赋给第一个参数变量,第二个值赋给第二个参数变量,依此类推。因为有了上面说的带缺省值参数,这个规则出现了变通的可能。\n", | |||
"\n", | |||
"如果一个函数有多个带缺省值的参数,我们想忽略掉某几个参数(就用其缺省值),但指定后面某一个参数的值(覆盖缺省值),例如下面这个函数:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 11, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def greeting(name, msg='Hi', punc='!'):\n", | |||
" print(f'{msg}, {name}{punc}')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"在这个版本的 `greeting()` 函数中,包含一个普通参数 `name` 和两个带缺省值的参数 `msg` `punc`,如果我们想跳过 `msg` 只传入 `name`(这个是必须的,因为没有缺省值)和 `punc` 的值,那么就可用下面的语法:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 12, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo.\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"greeting('Neo', punc='.')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"这里第一个值按照顺序位置匹配到参数变量 `name`,这叫 *positional argument*(即“按照位置顺序匹配的参数”),而按照位置下一个是 `msg`,是我们想跳过的,所以要注明参数变量名,说明下一个传入的值 `'.'` 是给 `punc` 参数变量的,这叫 *keyword argument*(即“按照参数名匹配的参数”)。\n", | |||
"\n", | |||
"由于所有带缺省值的参数都在普通参数的后面,所以我们只要记住:\n", | |||
"* 调用函数时先传入所有不带缺省值的参数的值,严格按照函数定义的位置顺序(*positional*);\n", | |||
"* 然后想指定哪些带缺省值参数的值,就用 `变量名=值` 这样的格式在后面列出(*keyword*),未列出的就还用缺省值了。\n", | |||
"\n", | |||
"在后半部分,顺序就无所谓了,可以和定义时不一样,反正是用名字指定的(*keyword*),比如我们完全可以这么干:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 13, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Good nite, Neo.\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"greeting('Neo', punc='.', msg='Good nite')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"### 7. 变长参数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"到目前为止,Python 的函数定义还是很简单清晰的,无论参数还是返回值,都没什么难懂的。下面开始就要进入比较混沌的领域了。\n", | |||
"\n", | |||
"所谓变长参数就是函数定义时名字前面带个星号 `*` 的参数变量,这表示这个变量其实是一组值,多少个都可以。我们先来看个简单的例子:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 14, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def say_hi(*names):\n", | |||
" for name in names:\n", | |||
" print('Hi,', name)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 15, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"say_hi('Neo')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 16, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo\n", | |||
"Hi, Trinity\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"say_hi('Neo', 'Trinity')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 17, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo\n", | |||
"Hi, Trinity\n", | |||
"Hi, Morpheus\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"say_hi('Neo', 'Trinity', 'Morpheus')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"在这个例子里,`*names` 是一个变长参数(*arbitrary argument*),调用时可以传入一个或者多个值,函数会把这些值看做一个列表,赋给局部变量 `names`——后面我们会知道,其实不是**列表**(*list*),而是一个**元组**(*tuple*)——然后我们在函数体中可以用 `for...in` 来对这个 `names` 做循环。\n", | |||
"\n", | |||
"> 有些中文书籍把 *arbitrary arguments* 翻译成“可变参数”或者“任意参数”。事实上,在这样的地方,无论怎样的中文翻译都是很难准确表达原意的。这还算好的,甚至还见过翻译成“武断的参数”的——这样的翻译肯定会使读者产生说不明道不白的疑惑。\n", | |||
">\n", | |||
"> 所以,**入门之后就尽量只用英文**是个好策略。虽然刚开始有点吃力,但后面会很省心,很长寿——是呀,少浪费时间、少浪费生命,其实就相当于更长寿了呀!" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"在使用 *arbitrary argument* 的场合,有几点需要注意:\n", | |||
"* 参数变量名最好用复数单词,一看就知道是一组数据;这个变量在函数里通常都会被 `for...in` 循环处理,用复数名词在写类似 `for name in names` 的循环语句时会很舒服、很地道(*idiomatic*),是的,写程序和学外语一样,不写则已,写就要尽量写得“地道”;\n", | |||
"* 这种参数变量只能有一个,因为从它开始后面的输入值都会被当做它的一部分,多了就不知道怎么分了,显然,如果有这种参数,必须放在参数表的最后。\n", | |||
"\n", | |||
"上面的第二点,有一个不太常见的例外,那就是一个函数既有 *arbitrary arguments* 又有 *arguments with default values* 的情况,那么可以有两个 *arbitrary arguments*,其中第二个必须带缺省值,然后参数表排列成这样:\n", | |||
"\n", | |||
"`def monstrosity(*normal arguments*, *normal arbitrary argument*, *arguments with defaults*, *arbitrary argument with default*)`\n", | |||
"\n", | |||
"这样是完全符合语法要求的,调用时传入参数值还是按照前面讲的规则,先按照位置顺序匹配前两部分,多出来的都归 *normal arbitrary argument*;然后按照参数变量名指定对应值,没指定的都用缺省值。不过这实在是太麻烦了,不知道什么情况下才必须用这么可怕的函数,还是祈祷我们不会碰到这样的场景吧!\n", | |||
"\n", | |||
"当然,只有上面列出的前三个部分的情况还是有的,比如下面的例子:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 18, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def say_hi(*names, msg='Hi', punc='!'):\n", | |||
" for name in names:\n", | |||
" print(f'{msg}, {name}{punc}')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 19, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"name": "stdout", | |||
"output_type": "stream", | |||
"text": [ | |||
"Hi, Neo.\n", | |||
"Hi, Trinity.\n" | |||
] | |||
} | |||
], | |||
"source": [ | |||
"say_hi('Neo', 'Trinity', punc='.')" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"## 小试身手(1)\n", | |||
"阅读可变参数的vfunc函数,仿写milti函数实现返回所有参数的乘积。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 7, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def vfunc(*b):\n", | |||
" a=0\n", | |||
" for n in b:\n", | |||
" a+=n\n", | |||
" return a\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 8, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"15" | |||
] | |||
}, | |||
"execution_count": 8, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"vfunc(1,2,3,4,5)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"## 函数别名" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"在 Python 中,所有函数也是对象,证据就是它们都有对象 id。Python 会为创建的每一个对象(不管基本数据类型,还是某个 *class* 的实例)指定一个唯一的 id,可以用内置函数 `id()` 来查看,比如:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"n = 42\n", | |||
"id(n)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"函数也有这个 id,比如:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 3, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def _is_leap(year):\n", | |||
" return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 4, | |||
"metadata": {}, | |||
"outputs": [ | |||
{ | |||
"data": { | |||
"text/plain": [ | |||
"2989669354808" | |||
] | |||
}, | |||
"execution_count": 4, | |||
"metadata": {}, | |||
"output_type": "execute_result" | |||
} | |||
], | |||
"source": [ | |||
"id(_is_leap)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"既然函数有 id,是个对象,那是什么类型的对象呢?可以用内置函数 `type` 来看:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"type(_is_leap)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"所以函数是个 `function` 类型的对象。\n", | |||
"\n", | |||
"既然是个对象,我们就可以用赋值语句来创建函数的**别名**(*alias*):" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"is_leap = _is_leap" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"id(is_leap)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"可以看到,这两个函数的 id 完全一样,是同一个对象的两个名字而已。我们可以用这两个名字来调用这个函数,完全没区别:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"_is_leap(2018)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"is_leap(2018)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"那么,我们为什么需要给函数取别名呢?\n", | |||
"\n", | |||
"很多时候是为了提供更好的代码可读性,比如在特定上下文让某个函数的作用更显而易见,比如以前的例子里,我们曾经在 `Cat` 类里给父类 `Animal` 的 `voice()` 方法定义别名叫 `meow()`。\n", | |||
"\n", | |||
"还有一种情况是一个函数需要在运行时动态指向不同的实现版本。这里只简单描述一个典型场景:假定我们要渲染一段视频,如果系统里有兼容的显卡(GPU),就调用显卡来渲染,会更快更省电,如果没有则用 CPU 来渲染,会慢一点和更耗电一点,于是我们把用 GPU 渲染的算法写成函数 `_render_by_gpu()`,用 CPU 渲染的算法写成函数 `_render_by_cpu()`,而检测是否存在可用 GPU 的算法写成函数 `is_gpu_available()`,然后可以用下面的方法来定义一个函数 `render`:\n", | |||
"\n", | |||
"```python\n", | |||
"if is_gpu_available():\n", | |||
" render = _render_by_gpu\n", | |||
"else:\n", | |||
" render = _render_by_cpu\n", | |||
"```\n", | |||
"\n", | |||
"这样 `render()` 就成为一个当前系统中最优化的渲染函数,在程序的其他地方就不用管细节,直接用这个函数就好。这就是动态函数别名的价值。\n", | |||
"\n", | |||
"顺便说一句,在任何一个工程里,为函数或者变量取名都是**很简单却不容易**的事情——因为可能会重名(虽然已经尽量用变量的作用域隔离了),可能会因取名含混而令后来者费解,所以,仅仅为了少敲几下键盘而给一个函数取个更短的别名,实际上并不是好主意,更不是好习惯。尤其现在的编辑器都支持自动补全和多光标编辑的功能,变量名长点不是什么大问题。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"## 匿名函数" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"有的函数需要两个甚至更多名字,有的函数却一个也不要,人生就是这么丰富多彩啊!\n", | |||
"\n", | |||
"所谓匿名函数,就是有时候我们需要一个函数,但就在一个地方,用完就扔,再也不会用了,Python 对这种情况提供了一个方便的语法,不需要 `def` 那套严肃完整的语法,一行就可以写完一个函数,这个语法使用关键字 `lambda`。`lambda` 是希腊字母 `λ` 的英语音译,在计算机领域是个来头不小的词儿,代表了一系列高深的理论,[和阿伦佐·丘奇(Alonzo Church)的理论有关](https://en.wikipedia.org/wiki/Lambda_calculus),有兴趣的话可以自行研究。\n", | |||
"\n", | |||
"不过目前我们不需要管那么多,只要了解怎么快速创建“用过即扔”的小函数就好了。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"def add(x, y):\n", | |||
" return x + y\n", | |||
"add(3, 5)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"我们可以用 lambda 来改写:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"add = lambda x, y: x + y\n", | |||
"add(3, 5)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"甚至更简单一点,名字也不要了:" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"(lambda x, y: x + y)(3, 5)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"最后这种形式,就是典型的匿名函数了。简单地说,`lambda` 可以生成一个函数对象,出现在所有需要一个函数的地方,可以将其赋给一个变量(如上面的 `add`),这个变量就称为函数变量(别名),可以当函数用;也可以直接把 `lambda` 语句用括号括起来当一个函数用(上面后一种形式)。\n", | |||
"\n", | |||
"在 Python 官方文档中,`lambda` 语句的语法定义是这样的:\n", | |||
"\n", | |||
"`lambda_expr ::= \"lambda\" [parameter_list] \":\" expression`\n", | |||
"\n", | |||
"这个语法定义采用的是 [巴克斯范式(BNF)](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form)标注,现在不明白没关系(虽然对照上面的例子也能猜出个大概吧),以后我们会专门介绍。\n", | |||
"\n", | |||
"其实也很简单,就是这个样子:\n", | |||
"\n", | |||
"```python\n", | |||
"lambda x, y: x + y\n", | |||
"```\n", | |||
"\n", | |||
"先写上 `lambda` 关键字,其后分为两个部分,`:` 之前是参数表,之后是表达式,这个表达式的值,就是这个函数的返回值。**注意**:`lambda` 语句中,`:` 之后有且只能有一个表达式,所以它搞不出很复杂的函数,比较适合一句话的函数。\n", | |||
"\n", | |||
"而这个函数呢,没有名字,所以被称为 “匿名函数”。\n", | |||
"\n", | |||
"`add = lambda x, y: x + y`\n", | |||
"\n", | |||
"就相当于是给一个没有名字的函数取了个名字。" | |||
] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"## 小试身手(2)" | |||
] | |||
}, | |||
{ | |||
"cell_type": "raw", | |||
"metadata": {}, | |||
"source": [ | |||
"1.判断奇数 \n", | |||
"判断奇数的函数lambda函数的定义和使用 \n", | |||
"(1)使用匿名函数编写一个函数isOdd,能够判断一个整数是否是奇数,返回True或False。\n", | |||
"isodd = lambda x: x%2 == 1\n", | |||
"(2)请写出输入一个数x,调用isOdd函数判断x是否是奇数的程序段。 \n", | |||
"(3)请写出求50-100之间所有奇数的和的程序段,(使用isOdd实现奇数判断)。 \n", | |||
"(4)请写出求50-100之间所有偶数的和的程序段(使用isOdd实现偶数判断)。\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": 9, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"isodd = lambda x: x%2 == 1" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [] | |||
}, | |||
{ | |||
"cell_type": "markdown", | |||
"metadata": {}, | |||
"source": [ | |||
"2.构造整数\n", | |||
"问题描述\n", | |||
"求由任意个整数上的个位数构造的新整数并输出。\n", | |||
"具体要求\n", | |||
"(1)编写lambda函数getLastBit,该函数返回正整数number的个位数,例如正整数1234,则返回4。\n", | |||
"(2)编写可变参数的函数makeNumber,求任意个整数的个位数构成的一个新整数。\n", | |||
"(3)输出(45、81、673、938)4个数的个位数构成的新整数,得到的新的整数为:5138。请填空完整程序。\n", | |||
"\n", | |||
"新整数是5138" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [ | |||
"#定义lambda函数getLastBit返回一个数的个位数\n", | |||
"getLastBit= \n", | |||
"\n", | |||
"#定义一个可变参数的函数makeNumber,求任意个整数的个位数构成的一个新整数\n", | |||
"def makeNumber(a,*b):\n", | |||
" s=getLastBit(a)\n", | |||
" \n", | |||
"\n", | |||
"\n", | |||
" return s\n", | |||
"\n", | |||
"#输出(45、81、673、938)4个数的个位数构成的新整数。\n", | |||
"print(f\"新整数是{makeNumber(45,81,673,938)}\")\n", | |||
"\n" | |||
] | |||
}, | |||
{ | |||
"cell_type": "code", | |||
"execution_count": null, | |||
"metadata": {}, | |||
"outputs": [], | |||
"source": [] | |||
} | |||
], | |||
"metadata": { | |||
"kernelspec": { | |||
"display_name": "Python 3", | |||
"language": "python", | |||
"name": "python3" | |||
}, | |||
"language_info": { | |||
"codemirror_mode": { | |||
"name": "ipython", | |||
"version": 3 | |||
}, | |||
"file_extension": ".py", | |||
"mimetype": "text/x-python", | |||
"name": "python", | |||
"nbconvert_exporter": "python", | |||
"pygments_lexer": "ipython3", | |||
"version": "3.7.10" | |||
} | |||
}, | |||
"nbformat": 4, | |||
"nbformat_minor": 4 | |||
} |