第五章:函数的设计和使用
5.1函数定义
将可能需要反复执行的代码封装为函数,并在需要该功能的地方进行调用,不仅可以实现代码复用,更重要的是可以保证代码的一致性,只需要修改该函数代码则所有调用均受影响。
设计函数时,应注意提高模块的内内聚习性,同时降低模块之间的隐式耦合。
在实际项目开发中,往往会把一些通用的函数封装到一个模块中,并把这个通用模块文件放到顶层文件夹中,这样更方便管理。
在编写函数时,应该尽量减少副作用,尽量不要修改参数本身,不要修改除返回值以外的其他内容。
应充分利用python函数式编程的特点,让自己定义的函数尽量符合纯函数式编程的要求,例如保证线程安全、可以并行运行(可移植性)等等。
函数定义语法:
def 函数名([参数列表]): #(多个参数逗号隔开)
'''注释'''
函数体
注意事项
- 函数形参不需要声明其类型,也不需要指定函数返回值类型
- 即使该函数不需要接收任何参数,也必须保留一对空的圆括号
- 括号后面的冒号不可少
- 函数体相对于def关键字必须保持一致的空格缩进
- python允许嵌套定义函数(允许,但是尽量避免)
生成斐波那契数列的函数定义和调用
#生成斐波那契数列的函数定义和调用
#1 1 2 3 5 8... 后面的数是前面两个数字的和
def fib(n):
a,b=0,1
while a<n:
print(a,end=' ')
a,b=b,a+b
print()
fib(1000)
在定义函数时,开头部分的注释并不是必需的,但是如果为函数的定义加上这段注释的话,可以为用户提供友好的提示和使用帮助。
Python是一种高级动态编程语言,变量类型是随时可以改变的。Python中的函数和自定义对象的成员也是可以随时发生改变的,可以为函数和自定义对象动态增加新成员。
>def func():
print(func.x) #查看函数func的成员x
>func() #查看函数func还没有成员x,出错
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
func()
File "<pyshell#14>", line 2, in func
print(func.x)
AttributeError: 'function' object has no attribute 'x'
>func.x=3 #动态为函数增加新成员
>func()
3
>func.x #在外部也可以直接访问函数的成员
3
>del func.x #动态删除函数成员
>func() #删除之后不可访问
Traceback (most recent call last):
File "<pyshell#20>", line 1, in <module>
func()
File "<pyshell#14>", line 2, in func
print(func.x)
AttributeError: 'function' object has no attribute 'x'
函数递归
函数的递归调用是函数调用的一种特殊情况,函数调用自己,自己再调用自己,自己再调用自己,…,当某个条件得到满足的时候就不再调用了,然后再一层一层地返回直到该函数第一次调用的位置。
栈是用来记住递归调用到的位置
5.2形参与实参
函数定义时括弧内为形参,一个函数可以没有形参,但是括弧必须要有,表示该函数不接受参数。函数调用时向其传递实参,将实参引用传递给形参。在定义函数时,对参数个数并没有限制,如果有多个形参,需要使用逗号进行分割。
编写函数,接受两个整数,并输出其中最大数。
#编写函数,接受两个整数,并输出其中最大数。
def printMax(a,b): #a和b是形参,形参是可以作为局部变量使用的
if a>b:
print(a,'is the max')
else:
print(b,'is the max')
对于绝大多数情况下,在函数内部直接修改形参的值不会影响实参,而是创建一个新变量。例如:
>def addOne(a): #接收一个参数a
print(id(a),':',a) #获取内存地址和a
a+=1 #a=a+1;直接修改形参的值
print(id(a),':',a)
>v=3
>id(v)
1694294999344
>addOne(v)
1694294999344 : 3
1694294999376 : 4
>v #函数执行后,不会影响实参的值
3
>id(v)
1694294999344
在有些情况下,可以通过特殊的方式在函数内部修改实参的值,例如下面的代码。
>def modify(v): #传递的参数是列表
v[0]=v[0]+1 #i下标加一,会对实参造成影响
>a=[2]
>modify(a)
>a
[3]>def modify(v,item): #在函数内部调用列表的方法
v.append(item) #对实参造成影响
>a=[2]
>modify(a,3)
>a
[2, 3]
也就是说,如果传递给函数的是可变序列,并且在函数内部使用下标或可变序列自身的方法增加、删除元素或修改元素时,实参也得到相应的修改。
>def modify(d): #修改字典元素值或为字典增加元素
d['age']=38 #字典有,修改;字典没,增加一个新元素
>a={'name':'Dong','age':37,'sex':'Male'}
>a
{'name': 'Dong', 'age': 37, 'sex': 'Male'}
>modify(a)
>a
{'name': 'Dong', 'age': 38, 'sex': 'Male'}
5.3参数类型
在Python中,函数参数有很多种:可以为普通参数、默认值参数、关键参数、可变长度参数等等。
Python在定义函数时不需要指定形参的类型,完全由调用者传递的实参类型以及Python解释器的理解和推断来决定,类似于重载和泛型。
Python函数定义时也不需要指定函数的类型,这将由函数中的return语句来决定,如果没有return语句或者return没有得到执行,则认为返回空值None。
Python支持对函数参数和返回值类型的标注,但实际上并不起任何作用,只是看起来方便。
>def test(x:int,y:int)->int: #->int:函数的返回值也是整型(这些也不起作用,传递字符串也是可以的,只是形式上比较好看)
'''x and y must be integers,return an integer x+y'''# assert:断言,x必须是整型的;否则抛出异常:x must be integer
assert isinstance(x,int),'x must be integer'
assert isinstance(y,int),'y must be integer'
z=x+y
assert isinstance(z,int),'must return an integer' #必须保证z是整型的
return z>test(1,2)
3
>test(2,3.0) #必须要求整型
Traceback (most recent call last):
File "<pyshell#60>", line 1, in <module>
test(2,3.0)
File "<pyshell#58>", line 4, in test
assert isinstance(y,int),'y must be integer'
AssertionError: y must be integer
位置参数
位置参数(positional arguments)是比较常用的形式,调用函数时实参和形参的顺序必须严格一致,并且实参和形参的数量必须相同。
>def demo(a,b,c):
print(a,b,c)
>demo(3,4,5) #按位置传递参收
3 4 5
>demo(3,5,4)
3 5 4
>demo(1,2,3,4) #实参与形参数量必须相同
Traceback (most recent call last):
File "<pyshell#69>", line 1, in <module>
demo(1,2,3,4)
TypeError: demo() takes 3 positional arguments but 4 were given
5.3.1默认值参数
默认值参数必须出现在函数参数列表的最右端,任何一个默认值参数右边不能有非默认值参数。
>def f(a=3,b,c=5):
SyntaxError: non-default argument follows default argument>def f(a=3,b):
SyntaxError: non-default argument follows default argument
>def f(a,b,c=5): #默认值参数必须在最右端;可以传值,也可以不传
print (a,b,c)
调用带有默认值参数的函数时,可以不对默认值参数进行赋值,也可以为其赋值,具有很大的灵活性。
>def say(message,times=1): #不指定次数,默认一次
print(message * times)
>say('hello')
hello
>say('hello',3)
hellohellohello
>say('hi',7)
hihihihihihihi
下面的函数使用指定分隔符将列表中所有字符串元素连接成一个字符串。
>def Join(List,sep=None):
return (sep or ' ').join(List) #没有指定分隔符,默认值是空格;空格是True>aList=['a','b','c']
>Join(aList)
'a b c'
>Join(aList,',')
'a,b,c'
默认值参数如果使用不当,会导致很难发现的逻辑错误,
例如:
def demo(newitem,old_list=[]):
old_list.append(newitem)
return old_list
print(demo('5',[1,2,3,4]))
print(demo('aaa',['a','b']))
print(demo('a'))
print(demo('b'))
原因在于默认值参数的赋值只会在函数定义时被解释一次。当使用可变序列作为参数默认值时,一定要谨慎操作。
终极解决方案:改写成下面的样子就不会有问题了
#解决方案
def demo(newitem,old_list=None): #默认值是空值
if old_list is None:
old_list=[] #创建空列表
#切片:主要的作用是不管是否是空列表,都返回一个新列表
new_list=old_list[:]
new_list.append(newitem)
return new_list
print(demo('5',[1,2,3,4]))
print(demo('aaa',['a','b']))
print(demo('a'))
print(demo('b'))
注意:
默认值参数只在函数定义时被解释一次
可以使用“函数名.__defaults__”(是一个元组)查看所有默认参数的当前值
>i=3
>def f(n=i): #参数n的值取决于i的当前值
print(n)
>f()
3
>i=5 #函数定义后修改i 的值不会影响参数n的默认值
>f()
3
>f.__defaults__ #查看函数默认值参数的当前值
(3,)
5.3.2关键参数
关键参数主要指实参,即调用函数时的参数传递方式。
通过关键参数,实参顺序可以和形参顺序不一致,但不影响传递结果,避免了用户需要牢记位置参数顺序的麻烦。
>def demo(a,b,c=5):
print(a,b,c)
>demo(3,7)
3 7 5
>demo(a=7,b=3,c=6) #关键参数,全部指定,保证顺序的正确
7 3 6
>demo(c=8,a=9,b=0)
9 0 8
5.3.3可变度参数
*parameter用来接受多个实参并将其放在一个元组中
** parameter接受(多个)关键参数并存放到字典中
*parameter的用法
>def demo(*p): #*(一个星号)可以接收多个参数,数量可变
print(p)
>demo(1,2,3)
(1, 2, 3)
>demo(1,2)
(1, 2)
>demo(1,2,3,4,5,6,7)
(1, 2, 3, 4, 5, 6, 7)
**parameter的用法
>def demo(**p): #**(两个星号)接收关键参数,放字典中
for item in p.items(): #.items()查看字典中的全部元素keys、values
print(item)
>demo(x=1,y=2,z=3)
('x', 1)
('y', 2)
('z', 3)
几种不同类型的参数可以混合使用,但不建议这样做
>def func_4(a,b,c=4,*aa,**bb):
print(a,b,c) #abc
print(aa) #除了关键参数的
print(bb)
>func_4(1,2,3,4,5,6,7,8,9,xx='1',yy='2',zz='3')
1 2 3
(4, 5, 6, 7, 8, 9)
{'xx': '1', 'yy': '2', 'zz': '3'}>func_4(1,2,3,4,5,6,7,xx='1',yy='2',zz=3)
1 2 3
(4, 5, 6, 7)
{'xx': '1', 'yy': '2', 'zz': 3}
5.3.4参数传递的序列解包
传递参数时,可以通过在实参序列前加一个星号将其解包,然后传递给多个单变量形参。
>def demo(a,b,c):
print(a+b+c)
>seq=[1,2,3]
>demo(*seq)
6
>tup=(1,2,3)
>demo(*tup)
6
>dic={1:'a',2:'b',3:'c'}
>demo(*dic) #*dic使用的是字典的键
6
>Set={1,2,3}
>demo(*Set)
6
>demo(*dic.values()) *dic.values()使用的是字典的值
abc
注意:调用函数时如果对实参使用一个星号*进行序列解包,这么这些解包后的实参将会被当做普通位置参数对待,并且会在关键参数和使用两个星号 ** 进行序列解包的参数之前进行处理。
>def demo(a,b,c): #定义函数
print(a,b,c)
>demo(*(1,2,3)) #调用,序列解包
1 2 3
>demo(1,*(2,3)) #位置参数和序解包同时使用
1 2 3
>demo(1,*(2,),3)
1 2 3
>demo(a=1,*(2,3)) #序列解包相当于位置参数,优先处理
Traceback (most recent call last):
File "<pyshell#143>", line 1, in <module>
demo(a=1,*(2,3))
TypeError: demo() got multiple values for argument 'a'
>demo(b=1,*(2,3))
Traceback (most recent call last):
File "<pyshell#144>", line 1, in <module>
demo(b=1,*(2,3))
TypeError: demo() got multiple values for argument 'b'
>demo(c=1,*(2,3))
2 3 1
#序列解包不能在关键参数解包之前
>demo(**{'a':1,'b':2},*(3,)) #一个星号的序列解包先执行
SyntaxError: iterable argument unpacking follows keyword argument unpacking>demo(*(3,),**{'a':1,'b':2})
Traceback (most recent call last):
File "<pyshell#147>", line 1, in <module>
demo(*(3,),**{'a':1,'b':2})
TypeError: demo() got multiple values for argument 'a'>demo(*(3,),**{'c':1,'b':2})
3 2 1
5.4return
return语句用来从一个函数中返回一个值,同时结束函数。
如果函数没有return语句,或者有return语句但是没有执行倒,或者只有return而没有返回值,Python将认为该函数以return None结束。
def maximum(x,y):
if x>y:return x
else:
return y
在调用函数或对象方法时,一定要注意有没有返回值,这决定了该函数或方法的用法。
>a_list=[1,2,3,4,9,5,7]
>print(sorted(a_list)) #sorted内置函数有返回值
[1, 2, 3, 4, 5, 7, 9]
>print(a_list)
[1, 2, 3, 4, 9, 5, 7]
>print(a_list.sort()) #a_list.sort():列表的方法没有返回值,原地排序
None
>print(a_list)
[1, 2, 3, 4, 5, 7, 9]
5.5变量作用域
变量起作用的代码范围称为变量的作用域,不同作用域内变量名可以相同,互不影响。
一个变量在函数外部定义和在函数内部定义,其作用域是不同的。
在函数内部定义的普通变量只在函数内部起作用,称为局部变量。当函数执行结束后,局部变量自动删除,不再可以使用。
局部变量的引用比全局变量速度快,应优先考虑使用。
如果想要在函数内部给一个定义在函数外的变量赋值,那么这个变量就不能是局部的,其作用域必须为全局的,能够同时作用于函数内外,称为全局变量,可以通过global来定义。这分为两种情况:
一个变量已在函数外定义,如果在函数内需要为这个变量赋值,并要将这个赋值结果反映到函数外,可以在函数内用global声明这个变量,将其声明为全局变量。
在函数内部直接将一个变量声明为全局变量,在函数外没有声明,该函数执行后,将增加为新的全局变量。
也可以这么理解:
在函数内如果只引用某个变量的值而没有为其赋新值,该变量为(隐式的)全局变量;
如果在函数内任意位置有为变量赋新值的操作,该变量即被认为是(隐式的)局部变量,除非在函数内显式地用关键字global进行声明。
>def demo():
global x #声明全局变量x;创建全局变量x
x=3
y=4
print(x,y)
>x=5 #创建一个全局变量x=5
>demo() #执行这个函数demo()
3 4
>x
3
>y #函数内部的局部变量,不调用没法访问
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
y
NameError: name 'y' is not defined
>del x #删除全局变量x
>x
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
x
NameError: name 'x' is not defined
>demo() #执行这个函数;调用这个函数的时候并不存在x,是重新创建的全局变量x
3 4 #这个函数还可以执行
>x
3
>y
Traceback (most recent call last):
File "<pyshell#15>", line 1, in <module>
y
NameError: name 'y' is not defined
注意:在某个作用域内只要有为变量赋值的操作,该变量在这个作用域内就是局部变量,除非使用global进行了声明。
>x=3 #全局变量x=3
>def f():
print(x) #本意是先输出全局变量x的值,但是不允许这样做
x=5 #有赋值操作,因此在整个作用域内x都是局部变量;局部变量在定义之前是没法访问的
print(x)
>f()
Traceback (most recent call last):
File "<pyshell#22>", line 1, in <module>
f()
File "<pyshell#21>", line 2, in f
print(x)
UnboundLocalError: local variable 'x' referenced before assignment
如果局部变量与全局变量具有相同的名字,那么该局部变量会在自己的作用域内隐藏同名的全局变量。
>def demo():
x=3 #没有global,表示声明的是局部变量
>x=5 #全局变量
>x
5
>demo()
>x #函数执行,不影响外面全局变量的值
5
如果需要在同一个程序的不同模块之间共享全局变量的话,可以编写一个专门的模块来实现这一目的。例如,假设在模块A.py中有如下变量定义:
gloval_variable=0
而在模块B.py中包含以下用来设置全局变量的语句:
import A
A.global_variable=1
在模块C.py中有以下语句来访问全局变量的值:
import A
print(A.global_variable)
除了局部变量和全局变量,Python还支持使用nonlocal(非局部)关键字定义一种介于二者(global和local)之间的变量。关键字nonlocal声明的变量会引用距离最近的非全局作用域的变量,要求声明的变量已经存在,关键字nonlocal不会创建新变量。
def scope_test(): #定义一个函数;函数里面又定义了几个函数
def do_local():
spam="我是局部变量" #没有global声明,是一个局部变量
def do_nonlocal():
#nonlocal使用距离最近的局部变量的值
nonlocal spam #这时要求spam必须是已存在的变量
spam="我不是局部变量,也不是全局变量"
def do_global():
global spam #如果全局作用域内没有spam,就自动新建一个
spam="我是全局变量"
spam="原来的值" #局部变量
do_local()
print("局部变量赋值后:",spam)
do_nonlocal()
print("nonlocal变量赋值后:",spam)
do_global#是在外层函数创建了全局变量,而非内层
print("全局变量赋值后:",spam)
scope_test()
print("全局变量:",spam)
5.6lambda表达式
lambda表达式可以用来声明匿名函数,也就是没有函数名字的临时使用的小函数,尤其适合需要一个函数作为另一个函数参数的场合。
lambda表达式只可以包含一个表达式,该表达式的计算结果可以看作是函数的返回值,不允许包含其他复杂的语句(选择、循环...),但在表达式中可以调用其他函数。
>f=lambda x,y,z:x+y+z #可以给lambda表达式起名字f;接收三个参数xyz;函数的返回值x+y+z
>f(1,2,3) #像函数一样调用
6
>g=lambda x,y=2,z=3:x+y+z #参数默认值
>g(1)
6
>g(2,z=4,y=5) #关键参数传值
11
#列表中有三个lambda表达式
>L=[(lambda x:x**2),(lambda x:x**3),(lambda x:x**4)]
#第一个:访问列表中下标为0位置是一个lambda表达式,传递实参为2
>print(L[0](2),L[1](2),L[2](2))
4 8 16
>D={'f1':(lambda:2+3),'f2':(lambda:2*3),'f3':(lambda:2**3)} #没有参数的函数
>print(D['f1'](),D['f2'](),D['f3']()) #D['f1']:f1这个键对应的lambda表达式
5 6 8
>L=[1,2,3,4,5]#map:映射到列表中的每个元素上
>print(list(map(lambda x:x+10,L))) #模拟向量运算
[11, 12, 13, 14, 15]>L #map 的操作不影响原来的列表
[1, 2, 3, 4, 5]
>def demo(n):
return n*n>demo(5)
25>a_list=[1,2,3,4,5]
>list(map(lambda x:demo(x),a_list)) #在lambda表达式中可以调用函数
[1, 4, 9, 16, 25]#不影响原来的列表,map是创建一个新对象
>data=list(range(20)) #创建列表0-19
>data
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>import random
>random.shuffle(data) #对列表随机打乱顺序
>data
[9, 12, 18, 17, 8, 4, 14, 0, 19, 15, 13, 16, 5, 7, 1, 3, 6, 11, 2, 10]#key:指定排序规则,排序规则是一个函数,给x,返回x
>data.sort(key=lambda x:x) #和不指定规则效果一样,默认从小到大
>data
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
#把原来的字符变成字符串,然后算长度,以长度进行排序
>data.sort(key=lambda x:len(str(x))) #默认升序
>data
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]#
>data.sort(key=lambda x:len(str(x)),reverse=True) #降序
>data #排序之前谁在前边,排序之后谁还在前边;是以他们的长度进行排序
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>import random
#使用列表推导式创建列表for i in range(5):列表推导式里面有5个子列表
#random.randint(1,10) for j in range(5):又有5个1-10之间的随机数
>x=[[random.randint(1,10) for j in range(5)] for i in range(5)]
>for item in x:
print(item)
[7, 6, 5, 7, 8]
[3, 2, 2, 6, 3]
[6, 6, 2, 10, 9]
[10, 8, 9, 9, 3]
[6, 4, 3, 10, 3]
#使用内置函数sorted
#指定的排序规则key:首先按照下标为1的排序,排不出来,按照下标为4的进行排序
>y=sorted(x,key=lambda item:(item[1],item[4]))
>for item in y:
print(item)
[3, 2, 2, 6, 3]
[6, 4, 3, 10, 3]
[7, 6, 5, 7, 8]
[6, 6, 2, 10, 9]
[10, 8, 9, 9, 3]
5.7案例精选
例5-1
:编写函数计算圆的面积。
#编写函数计算圆的面积。
from math import pi as PI
def CircleArea(r):#r:圆的半径
if isinstance(r,(int,float)):#isinstance:确保接收的参数为数值
return PI*r*r
else:
print('You must give me an integer or float as radius(半径).')
print(CircleArea(3))
例5-2
:编写函数,接收任意多个实数(可变长度的参数),返回一个元组,其中第一个元素为所有参数的平均值,其他元素为所有参数中大于平均值的实数。
#平均值,大于平均值
def demo(*para):#前面加一个*,可变长度参数
avg=sum(para)/len(para) #计算平均值
g=[i for i in para if i>avg] #得到的是列表
return (avg,)+tuple(g)#数字和元组转化为列表
print(demo(1,2,3,4))
例5-3
:编写函数,接收字符串参数,返回一个元组,其中第一个元素为大写字母个数,第二个元素为小写字母个数。
#计算大写字母个数和小写字母个数
def demo(s):
result=[0,0] #第一个0:大写字母个数;第二个0:小写字母个数
for ch in s: #循环遍历字符串中的每个字符
if 'a'<=ch<='z': #如果是小写字母
result[1]+=1 #下标为1的加一
#使用的是elif,因为不只是大小写字母,还有可能是别的,else不行哦
elif 'A'<=ch<='Z': #如果是大写字母
result[0]+=1 #下标为0的加一
return tuple(result)
print(demo('aaaabbbbC'))
例5-4
:编写函数,接收包含20个整数的列表Ist和一个整数k作为参数,返回新列表。处理规则为:将列表Ist中下标k之前的元素逆序,下标k之后的元素逆序,然后将整个列表Ist中的所有元素再逆序。
#列表下标k前后逆序,之后整个逆序
def demo(lst,k):
x=lst[:k]#切片获取,不包含k
x.reverse() #逆序
y=lst[k:]
y.reverse()
r=x+y
r.reverse()
return r
lst=list(range(1,21))
print(demo(lst,5))
本例的执行结果实际上是把列表中所有元素循环左移k位。在collections标准库的deque对象已经实现了该功能,直接调用即可。
>import collections
>x=list(range(20))
>x=collections.deque(x)
>x
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])>x.rotate(-3)
>x
deque([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2])>x=list(x)
>x
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2]
要是把代码写成这样,会不会眼前一亮呢?
>def shift(lst,k):
return lst[k:]+lst[:k]>x=list(range(20))
>shift(x,3)
[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2]
>shift(x,-3)
[17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
例5-5
:编写函数,接收整数参数t,返回斐波那契数列中大于t 的第一个数。
#编写函数,接收整数参数t,返回斐波那契数列中大于t 的第一个数。
#1 1 2 3 5 8 13
def demo(t):
a,b=1,1#斐波那契数列前两项
while b<t:
a,b=b,a+b
else:
return b
print(demo(50))
例5-6
:编写函数,接收一个包含若干整数的列表参数lst,返回一个元组,其中第一个元素为列表Ist中的最小值,其余元素为最小值在列表lst中的下标。
#lst中最小值,最小值在lst中的下标
import random
def demo(lst):
m=min(lst)#min获取最小值
result=(m,)#创建写最小值的元组
#enumerate:枚举列表,包含下标和值
for index,value in enumerate(lst):
if value==m: #某一个位置上的值和最小值是一样的
result=result+(index,)#记下位置,接到result后
return result
#在整数1-20之间找50次
x=[random.randint(1,20) for i in range(50)]
print(x)
print(demo(x))
例5-7
:编写函数,接收一个整数t为参数,打印杨辉三角前t行。
#t为参数,打印杨辉三角前t行
#杨辉三角:左边和斜边是1,中间的数字:上边数字+上边数前一个数
def demo(t):
print([1])#第0行
print([1,1])#第1行
line=[1,1]#设一个临时的变量
for i in range(2,t):#从第2行开始
r=[]#一个空列表
#循环遍历第1行,除了最后一个数,也就是1
for j in range(0,len(line)-1):
#向空列表追加上一行说有相邻数字相加
r.append(line[j]+line[j+1])
line=[1]+r+[1]#每行左右都是1,再加上中间生成的数字
print(line)#把每行循环都打印出来
demo(10)
例5-8
:编写函数,接收一个正偶数为参数,输出两个素数,并且这两个素数之和等于原来的正偶数。如果存在多组符合条件的素数,则全部输出。
#输出两个素数,并且这两个素数之和等于原来的正偶数
import math
#编一个函数,判断是否为素数
def IsPrime(n):
m=int(math.sqrt(n))+1
for i in range(2,m):
if n%i==0:
return False
return True
def demo(n):#n为正偶数
#是不是整数, 是不是正数,是不是偶数
if isinstance(n,int) and n>0 and n%2==0:
for i in range(3,n//2+1):
#判断i和n-i是不是素数;i+n-i=n
if IsPrime(i) and IsPrime(n-i):
print(i,'+',n-i,'=',n)
demo(60)
例5-9
:编写函数,接收两个正整数作为参数,返回一个元组,其中第一个元素为最大公约数,第二个元素为最小公倍数(两数乘积,再除以他们的最大公约数)。
#最大公约数和最小公倍数
def demo(m,n):
if m>n:
m,n=n,m#如果m>n,交换两个数
p=m*n
while m!=0:
r=n%m
n=m
m=r
return(n,p//n)#n是最大公约数,p//n:最小公倍数
print(demo(12,30))
python标准库已经提供了计算最大公约数的方法
>import fractions
>fractions.gcd(36,39) #最大公约数
3
>fractions.gcd(30,20)
10
>30*20/fractions.gcd(30,20)
60.0
>import math
>math.gcd(36,39)
3
例5-10
例5-10:编写函数,接收一个所有元素值都不相等的整数列表x和一个整数n,要求将值为n的元素作为支点,将列表中所有值小于n的元素全部放到n的前面,所有值大于n的元素放到n的后面。
#以n作为支点,n前的数字小于n,n后的数字大于n
import random
def demo(x,n):#x为列表,n为整数
if n not in x:#列表中是否有这个整数
print(n,'is not an element of',x)
return
i=x.index(n)#有这个整数,获取这个n的下标
x[0],x[i]=x[i],x[0]#n这个数字和第一个数字交换
key=x[0] #支点元素值
i=0 #i指向列表的第一个元素
j=len(x)-1 #j指向列表的最后一个元素
while i<j:
while i<j and x[j]>=key:
j-=1
x[i]=x[j]
while i<j and x[i]<=key:
i+=1
x[j]=x[i]
x[i]=key
x=list(range(1,10))#创建一个列表
random.shuffle(x)#把这个列表打乱顺序
print(x)#输出打乱顺序的列表
demo(x,4)#以4作为支点,将列表分成两部分
print(x)
上面的代码演示的是快速排序算法中非常重要的一个步骤,当然也可以使用下面更加简介的代码来实现。
>import random
>def demo(x,n):
t1=[i for i in x if i<n] #遍历列表找出比n小的数字
t2=[i for i in x if i>n] #遍历列表找出比n大的数字
return t1+[n]+t2>x=list(range(1,10))
>random.shuffle(x)
>x
[7, 3, 9, 2, 4, 8, 1, 6, 5]
>demo(x,4)
[3, 2, 1, 4, 7, 9, 8, 6, 5]
例5-11
编写函数,计算字符串匹配的准确率。以打字练习程序为例,假设origin为原始内容,userlnput为用户输入的内容,下面的代码用来测试用户输入的准确率。
#计算字符串匹配的准确率
#origin:原始内容 userlnput:用户输入的内容
def Rate(origin,userInput):
#保证原始的内容是字符串,用户输入的内容也是字符串
if not (isinstance(origin,str) and isinstance(userInput,str)):
print('The two parameters must be strings.')#如果不是,给出错误的提示
return
if len(origin)<len(userInput): #原始内容比用户输入的内容短
print('Sorry.I suppose the second parameter string is shorter.')
return
right=0 #假设零个字符是正确的
#zip把origin,userInput压缩到一起并用origin_char,user_char遍历
for origin_char,user_char in zip(origin,userInput):
#如果两个是相等的
if origin_char==user_char:
right+=1 #打对字符的数量
return right/len(origin) #准确率=打对字符的数量/原始字符的长度
origin='Shandong Institute of Business and Technology'
userInput='ShanDong institute of business and technology'
print(Rate(origin,userInput))
参考:
Python函数设计与使用1:函数定义与调用_哔哩哔哩_bilibili
Python函数设计与使用2:默认值参数_哔哩哔哩_bilibili
Python函数设计与使用3:关键参数、可变长度参数、参数解包_哔哩哔哩_bilibili
Python函数设计与使用4:详解变量作用域_哔哩哔哩_bilibili
Python函数设计与使用5:lambda表达式语法与应用_哔哩哔哩_bilibili
Python函数设计与应用6:例题讲解1_哔哩哔哩_bilibili