Python 入门之函数结构 python中函数的概念

您所在的位置:网站首页 printf函数功能及参数作用 Python 入门之函数结构 python中函数的概念

Python 入门之函数结构 python中函数的概念

#Python 入门之函数结构 python中函数的概念| 来源: 网络整理| 查看: 265

第一部分:函数,参数及参数解构一.什么是函数1.函数(1)数学定义:y=f(x),y是x的函数,x是自变量(2)python函数:由若干语句组成的语句块,函数名称,参数列表构成,它是组织代码的最小单元,完成一定的功能。可以通过函数名在程序的不同地方多次执行(这通常叫做函数调用),却不需要在所有地方都重复编写这些语句。2.函数的作用(1)结构化编程对代码的最基本的封装,一般按照功能组织一段代码(2)疯转的目的为了复用,减少冗余代码(3)代码更加简洁美观,可读易懂(4)可扩展性3.函数的分类(1)内建函数,如max(),reversed()等(2)库函数,如math.ceil()等二.函数定义和调用1.函数定义方法:(1)语法:def语句定义函数

def 函数名(参数列表) 函数体(代码块) [return返回值]

(1)函数名就是标识符,命名要求一样(2)语句块必须缩进,约定4个空格(3)python的函数没有return语句,隐式会返回一个None值(4)定义中的参数列表成为一个形式参数,只是一种符号表达,简称形参(5)代码:

def test(x): #def:定义函数的关键字,test是函数名,(x)内可定义形参 "函数注释" #"文档描述":文档描述(非必要,但是强烈建议为你的函数添加描述信息) x+=1 #x+=1:泛指代码块或程序处理逻辑 return x #return:定义返回值,函数碰到return整体结束掉 a=test(x) #变量a=函数名():调用运行(可以带参数也可以不带) print(a) #输出a

2.调用(1)函数定义,只是声明了一个函数,它不会被执行,需要调用(2)调用的方式,就是函数名加上小括号,括号内写上参数(3)调用时写的参数是实际参数,是实实在在传入的值,简称实参3.函数定义调用举例

def test(x): ''' :功能:y=2*x+1 :param x:整型数字 :return y:返回计算结果 ''' y=2*x+1 return y a=test(5) print(a) #运行返回:11

总结:(1)上面只是一个函数的定义,有一个函数叫test,接收1个参数(2)计算的结果,通过返回值返回(3)调用通过函数名test加1个参数,返回值可使用变量接收(4)定义需要在调用前,也就是说调用时,已经被定义过了,否则抛NameError异常(5)函数是可以调用的对象,callable()4.为什么使用函数(1)降低编程的难度(2)代码重用(3)可扩展性三.函数和过程1.过程:过程就是简单特殊没有返回值的函数2.当一个函数/过程没有使用return显示的定义返回值时,python解释器会隐式的返回None,在python中即便是过程也可以算作函数。举例1:

def test1(): #定一个过程没有return msg='hello xi' print msg def test2(): #定一个函数有return msg='hello xixi' print msg return msg t1=test1() t2=test2() print 'from test1 return is [%s]' %t1 print 'from test2 return is [%s]' %t2 输出结果: hello xi hello xixi from test1 return is [None] from test2 return is [hello xixi]

举例2:

def test1(): msg = 'test1' print(msg) def test2(): msg = 'test2' print(msg) return msg def test3(): msg = 'test3' print(msg) return 0,10,'hello',['xixi','xixi'],{'wang':'xixi'} t1=test1() #首先运行test1() 把结果赋值给t1 t2=test2() #test2() 把结果赋值给t2 t3=test3() #test3() 把结果赋值给t3 print (t1) #取t1执行test1的执行结果 print (t2) #取t2执行test2的执行结果 print (t3) #取t2执行test3的执行结果 输出结果: test1 test2 test3 None test2 (0, 10, 'hello', ['xixi', 'xixi'], {'wang': 'xixi'})

总结:返回值数=0:返回None返回值数=1:返回object返回值数>1:返回tuple四.函数参数1.分位置参数和关键字参数(1)位置参数:1)方式:def f(x,y,z)调用使用f(1,3,5)2)按照参数定义顺序传入实参:实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值(2)关键字参数1)方式:def f(x,y,z)调用使用f(x=1,y=3,z=5)2)使用形参的名字来出入实参的方式,如果使用了形参名字,那么传参顺序就可和定义顺序不同形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量(3)传参关键字命名参数传参:f(z=None,y=10,x=[1])位置参数+关键字命名参数传参:f((1,),z=6,y=4.1)f(y=5,z=6,2):传参失败:要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的注意:动态行语言,传实参不强求传的参数是什么类型2.函数参数默认值(简化工作)(1)参数默认值:定义时,在形参后跟上一个值(2)举例x,y不传参数,用默认值

def add(x=4,y=5): return x+y xx = add() print(xx) 返回: 9

x位置传参数6,y不传参数用默认的

def add(x=4,y=5): return x+y xx = add(6) print(xx) 返回: 11

x,y关键字命名参数给默认值参数传参

def add(x=4,y=5): return x+y xx = add(y=6,x=7) print(xx) 返回: 13

传的位置参数不允许在关键字参数后面(报错)

def add(x=4,y=5): return x+y xx = add(y=6,1) print(xx)

缺省值参数(位置参数)不允许在关键字参数后面(报错)

def add(x=4,y): return x+y

(3)参数的默认值作用:参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值的默认值参数非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用3.可变参数(1)位置参数的可变参数举例:有多个数,需要累加求和

def add(*nums): #在形参nums前使用*表示该形参是可变参数 xx = 0 print(type(nums)) #收集多个或0个实参为一个tuple(元祖) for x in nums: xx += x print(xx) #调用add函数传3,6,9 add(3,6,9) 返回: 18

(2)关键字参数的可变参数

def showconfig(**kwargs): #形参前使用**符号,表示可以接收多个关键字参数 print(type(kwargs)) #收集的实参名称和值组成一个字典 for k,v in kwargs.items(): print('{} = {}'.format(k,v)) ##调用showconfig函数传host='127.0.0.1',port='8080',username='xixi',password='123456' showconfig(host='127.0.0.1',port='8080',username='xixi',password='123456') 返回: username = xixi password = 123456 port = 8080 host = 127.0.0.1

(3)可变参数混合使用举例:位置参数xixi,位置参数的可变参数123,关键字参数的可变参数x=1,y=2混合使用

def showconfig(username,*args,**kwargs): print(username) print(args) print(kwargs) showconfig('xixi',123,x=1,y=2) 返回: xixi (123,) {'x': 1, 'y': 2}

(4)可变参数总结:有位置可变参数和关键字可变参数位置可变参数在形参前使用一个星号*关键字可变参数在形参前使用两个星号**位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict混合使用参数的时候,可变参数要放到参数列表的最后,普通参数需要放到参数列表前面,位置可变参数需要在关键字可变参数之前4.keyword-only参数(1)keyword-only参数(python3加入)如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数了,而是keyword-only参数

def fn(*args,x): #args截获了所有的位置参数,x不使用关键字参数就不可能拿到实参,x是keyword-only参数 print(x) print(args) #调用fn函数传3,5,7,x=1 fn(3,5,7,x=1) 返回: 1 (3, 5, 7)

(2)keyword-only参数另一种形式

def fn(*,x,y): #*号之后,普通形参都变成了必须给出的keyword-noly参数 print(x,y) fn(x=5,y=6) 返回: 5 6

5.可变参数和默认值参数结合使用举例1:可变参数不传值,默认参数不传值

def fn(*args,x=5): print(args) print(x) fn() #等价于fn(x=5) 返回: () 5

举例2:可变参数传值,默认参数不传值

def fn(*args,x=5): print(args) print(x) fn(6) 返回: (6,) 5

举例3:可变参数不传值,默认参数传值

def fn(*args,x=5): print(args) print(x) fn(x=6) 返回: () 6

举例4可变参数传值,默认参数传值

def fn(*args,x=5): print(args) print(x) fn(1,2,3,x=6) 返回: (1, 2, 3) 6

6.函数参数规则参数列表参数一般顺序是,普通参数,缺省参数,可变位置参数,keyword-only参数(可带缺省值),可变关键字参数7.参数解构(1)参数解构:给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的解构解开,提取出所有元素作为函数的实参非字典类型使用*解构成关键参数字典类型使用**解构成关键字参数提取出来的元素数目要喝参数的要求匹配,也要和参数的类型匹配(2)法函数举例:

def add(x,y): return x + y #解构(2,3)[0]元祖传第一个元素2,[3][0]列表传第一个元素3 xx = add((2,3)[0],[3][0]) print(xx) 返回: 5

举例:解构元祖

def add(x,y): return x + y #把多个元素的集合(4,5)结构后传到add函数里 xx = add(*(4,5)) print(xx) 返回: 9

举例:解构列表

def add(x,y): return x + y #把多个元素的列表[4,5]结构后传到add函数里 xx = add(*[4,5]) print(xx) 返回: 9

举例:解构可迭代对象

def add(x,y): return x + y #把range(1,3)可迭代对象结构后传到add函数里 xx = add(*range(1,3)) #个数要跟形参一一匹配 print(xx)

举例:解构字典

def add(x,y): return x + y d = {'a':5,'b':6} #把字典{'a':5,'b':6}结构后传到add函数里 xx = add(*d.values()) print(xx) 返回: 11

8.参数解构和可变参数给函数提供实参的时候,可以在集合类型前使用*或者**,把集合类型的结构解开,提取出所有元素作为函数的实参举例:解构列表+可变参数

def add(*iterble): result = 0 for x in iterble: result += x return result #解构[1,2,3]传到函数add的可变参数 xx = add(*[1,2,3]) print(xx) 返回: 6

举例:解构可迭代对象+可变参数

def add(*iterble): result = 0 for x in iterble: result += x return result #range(5)可迭代对象传到函数add的可变参数 xx = add(*range(5)) print(xx) 返回: 10

9.练习:编写一个函数,能够随机接收随机十个数,返回最小值和最大值

import random def double_values(*nums): print(nums) return max(nums),min(nums) #列表解析随机生成10个10到20的数字的列表:([random.randint(10,20) for _ in range(10)])再通过*解构得到10到20个数字传的实参 #把return max(nums),min(nums)返回的元祖解构通过*解构成两个数字 print(*double_values(*[random.randint(10,20) for _ in range(10)])) #把实参传到可变参数里得到的元祖解构成两个数字 返回: (15, 11, 17, 16, 10, 13, 18, 12, 14, 14) 18 10

编写一个函数,接收有一个参数n,n为正整数,左右两种打印方式。要求数字必须对齐右对齐方式:

def show(n): tail = " ".join([str(i) for i in range(n,0,-1)]) #计算出最后一行字符串12 11 10 9 8 7 6 5 4 3 2 1 width = len(tail) #算出最后一行字符串的宽度 for i in range(1,n): #(" ".join([str(j) for j in range(i,0,-1)]))每循环一遍减去一个数字 #width最后一行字符串的宽度26,也就是每行的总宽度 #("{:>{}}".format(每次循环减去一个数字)),把每次循环减一个数字传到{}按右对齐 print("{:>{}}".format(" ".join([str(j) for j in range(i,0,-1)]),width)) print(tail) show(12) 输出: 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1 6 5 4 3 2 1 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 11 10 9 8 7 6 5 4 3 2 1 12 11 10 9 8 7 6 5 4 3 2 1

左对齐方式:

def showtail(n): tail = " ".join([str(i) for i in range(n,0,-1)]) #计算出最后一行字符串12 11 10 9 8 7 6 5 4 3 2 1 print(tail) #打印首行:12 11 10 9 8 7 6 5 4 3 2 1 for i in range(len(tail)): #把符串从头到尾迭代一遍 if tail[i] == ' ': #12 11 10 9 8 7 6 5 4 3 2 1从做到右如果发现一个空格 print(' '*i,tail[i+1:]) #把发现空格前面的数字替换成一个空格 showtail(12) 输出: 12 11 10 9 8 7 6 5 4 3 2 1 11 10 9 8 7 6 5 4 3 2 1 10 9 8 7 6 5 4 3 2 1 9 8 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 7 6 5 4 3 2 1 6 5 4 3 2 1 5 4 3 2 1 4 3 2 1 3 2 1 2 1 1

第二部分:函数的返回值一.函数返回值1.特点(1)python函数使用return语句返回“返回值”(2)所有函数都有返回值,如果没有return语句,隐式调用return None(3)return语句并不一定是函数的语句块的最后一条语句(4)一个函数可以存在多个return语句,但是只有一条可以被执行。如果没有一条return语句被执行到,隐式调用return None(5)如果有必要可以显示调用return None,可以简写为return(6)如果函数执行了return语句,函数就会返回,当前被执行的return语句之后的其它语句就不会被执行了2.作用:结束函数调用,返回值举例:

def fn(x): for i in range(x): if i > 3: return i #当4大于3的时候返回4 else: print("{}不大于3".format(i)) print(fn(5)) 返回: 0不大于3 1不大于3 2不大于3 3不大于3 4

举例:

def fn(x): for i in range(x): if i > 3: #没有执行到return语句返回None return i else: print("{}不大于3".format(i)) print(fn(3)) 返回: 0不大于3 1不大于3 2不大于3 None

3.返回多个值(1)函数不能同时返回多个值(2)return[1,3,5]是指明返回一个列表,是一个列表对象(3)recent1,3,5看似返回多个值,隐式的被python封装成一个元祖

def showlist(): return 1,3,5 x,y,z = showlist() #使用解构提取更为方便 print(x,y,z) 返回: 1 3 5

第三部分:嵌套函数1.函数嵌套:在一个函数中定义了另外一个函数(1)函数有可见范围,这就是作用域的概念(2)内部函数不能被外部直接使用,会抛NameError异常2.函数嵌套举例(1)举例1

def outer(): def inner(): print("outer()调用内部") print("inner()调用外部") inner() #内部调用 outer() #外部调用 #inner() #外部不可以直接调用内部函数报错NameError 打印返回: inner()调用外部 outer()调用内部

(2)举例2

def one(name): #第一层函数one print('第一层函数%s' %name) #执行当前层的局部变量print('第一层函数%s' %name)和def two(): def two(): #第二层函数two name='shishi' print('第二层函数%s' %name) #打印当前层的局部变量 def three(): #第三层函数three name='yaoyao' print('第三层函数%s' %name) #打印当前层的局部变量 three() #内部调用 two() #内部调用 one('xixi') 打印结果: 第一层函数xixi 第二层函数shishi 第三层函数yaoyao

 

第四部分:函数柯里化Currying1.柯里化指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有的第二个参数为参数的函数2.z=f(x,y)转换成z=f(x)(y)的形式3.将加法函数柯里化函数加法函数:

 

def add(x,y): return x + y xixi = add(5,6) print(xixi) #返回:11

 

转换柯里化:

 

def add(x): #x调add(5)的时候把5传进去 def _add(y): #第二个参数y对应传的是6 return x + y #这里可以调用到x,x是外层的变量,里面的函数相当于闭包 return _add #新的函数_add返回一个以原有第二个参数为参数的函数 xixi = add(5)(6) #add(5)返回值相当于函数对象,在传入一个6 print(xixi) #返回:11

 

总结:通过嵌套函数可以把函数转换成柯里化函数第五部分:函数作用域一.函数作用域1.作用域:一个标识符的可见范围,这就是标识符的作用域,一般常说的是变量的作用域2.局部作用域:(1)在当前函数,类等内部可见(2)局部变量使用范围不能超过其所在的局部作用域

def fn1(): #函数fn1() x =1 #x=1局部作用域,在fn1内 def fn2(): #函数fn2() print(x) #不可见x print(x) #不可见x

3.全局作用域:在整个程序运行环境中都可见

name='全局作用域' def change_name(): print('change_name',name) #函数内全局作用域 change_name() print(name) #函数外调用全局作用域 打印结果: change_name 全局作用域 全局作用域

4.作用域嵌套结构举例1:foo函数内嵌了一个bar函数

name = '全局作用域' def foo(): name='局部作用域' def bar(): print(name) return bar a=foo() print(a) a() #返回结果: 局部作用域

执行详解1:a=foo()和print(a)运行过程详解:执行foo()首先运行:①name='局部变量'②加载了个函数def bar():到内存当中 ③return bar了一个结果,赋值给a,print(a)打印获得bar的内存地址执行详解2:a()运行过程详解:有了bar的内存地址就可以运行bar()这个函数了,这个函数有个print(name),执行a()相当于执行bar()这个函数,不管a()在那个位置调用bar()这个函数,函数在运行当中的作用域都跟定义的时候有关举例2:外层变量作用域在内层作用域可见

def outer1(): o = 65 def inner(): print("inner{}".format(o)) #内层作用域可用外层变量 print(chr(o)) print("outer{}".format(o)) #打印调用外层的变量 inner() outer1() #返回结果: outer65 inner65 A

举例3:内层作用域inner中,如果定义了o=97,相当于当前作用域中重新定义了一个新的变量o,但是这个o并没有覆盖外层作用域outer中的o

def outer1(): o = 65 def inner(): o = 97 print("inner{}".format(o)) #内层作用域调用内层从新定义的变量97 print(chr(o)) print("outer{}".format(o)) #o并没有覆盖外层作用域outer中的 inner() outer1() #返回结果: outer65 inner97 a

5.全局变量global:把局部变量强制成全局变量

x = 5 def foo(): global x x +=1

(1)使用global关键字变量,将foo内的x声明为使用外部的全局作用域中定义的x(2)全局作用域中必须有x的定义(3)如果函数没有被调用的话 global是没有用的举例:

NAME = "xixi" def s1(): global NAME #已经声明,NAME就是全局的那个变量xixi NAME = "yueyue" #修改全局的变量为yueyue print('打印出', NAME) def s2(): print('打印出', NAME) s1() s2() #返回结果: 打印出 yueyue 打印出 yueyue

(4)global使用原则1)外部作用域变量会内部作用域肯建,但也不要在这个内部的作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离2)如果函数需要使用外部全局变量,清使用函数的形参传参解决二.闭包1.自由变量:未在本地作用域中定义的变量。例如定义在内存函数外的外层函数的作用域中的变量2.闭包:就是一个概念,出现在嵌套函数中,指的是内层函数引用到了外层函数的自由变量,就形成了闭包。很多语言都有这个概念,最熟悉的就是JavaScript举例1:

def counter(): c = [1] #c=[1]是内层inc()函数的自由变量 def inc(): print('当前自由变量',c[0]) c[0] += 1 #引用的是外层counter()函数的变量c= [1] return c[0] return inc foo = counter() print(foo(),foo()) c = 100 print(foo()) #返回结果: 当前自由变量 1 当前自由变量 2 2 3 当前自由变量 3 4

(4)举例2

def one(name): #one(name)最外层接收xixi print('最外层%s' %name) def two(): #two()中间层:中间层two print('中间层%s' %name) def three(): #three()最里层:闭包three print('最里层%s' %name) three() #执行three()取值在自己的three()最里层闭包里取值 two() #执行two()取值在自己的two()中间层闭包里取值 one('xixi') #最外层传入xixi #输出结果:最外层中间层最里层都可以接收到xixi因为他们是层级的关系 最外层xixi 中间层xixi 最里层xixi

三.nonlocal关键字(python3后引用):指定上一级变量:如果没有就继续往上直到找到为止(1)使用了nonlocal关键字,将变量标记为在上级的局部作用域中定义,但不能是全局作用域定义(2)举例:

name = "全局变量" def s1(): name = "局部变量" #外层局部变量被内层name="西西"修改为西西 def ss1(): nonlocal name #把上一级变量name = "局部变量"改为name=西西 name = "西西" ss1() print('2',name) print('1',name) s1() print('3',name) #打印结果: 1 全局变量 2 西西 3 全局变量

四.默认值的作用域(1)默认值作用域举例1:外部不可以调用内部默认值

def foo(xyz=1): print(xyz) foo() #print(xyz) #报错,当前作用域没有xyz变量,外部不可以调用内部默认值 #返回结果: 1

(2)默认值作用域举例2:python把函数默认值放在了属性中

def foo(xyz=[]): #第一次调用foo(),把对象默认值放在特殊的属性上 #第二次调用foo() xyz.append(1) #给xyz添加1,xyz=[1] #再给xyz添加1,xyz=[1,1] print(xyz) foo() foo() #返回结果: [1] [1, 1]

总结:1)为什么第二次调用foo函数打印的是[1,1]2)因为函数也是对象,python把函数的默认值放在了属性中,这个属性就伴随这这个函数对象的整个生命周期(3)默认值作用域举例3:查看foo.__defaults__函数对象的属性

def foo(xyz=[],u='abc',z=123): #作用域在函数内部,形参就是函数的本地变量,没给值从函数对象的defaults属性去把缺省值拿回来 xyz.append(100) return xyz print(1,foo.__defaults__) #1 ([], 'abc', 123) print(foo(),id(foo)) #foo()返回:[100] id(foo)返回:1867560 print(2,foo.__defaults__) #2 ([100], 'abc', 123) print(foo(),id(foo)) #foo()返回:[100, 100] id(foo)返回:1867560 print(3,foo.__defaults__) #3 ([100, 100], 'abc', 123) #返回结果: 1 ([], 'abc', 123) [100] 3833640 2 ([100], 'abc', 123) [100, 100] 3833640 3 ([100, 100], 'abc', 123)

总结:1)函数地址没有变,就是说函数整个对象的没有变,调用它,它的属性__defaults__中使用元祖保存所有默认值2)xyz默认值是引用类型,引用类型的元素变动,并不是元祖的变化(4)默认值作用域的两种方法方法一:使用影子拷贝创建一个新的对象,永远不能改变传入的参数

def foo(xyz=[],u='abc',z=123): #第一次调用从缺省值的属性里拿到空列表 xyz = xyz[:] #影子拷贝:空列表拷贝出来一份,返回一个新的列表,变成局部变量 xyz.append(1) #新列表追加1 print(xyz) #打印新列表 foo() print(1,foo.__defaults__) #1 ([], 'abc', 123) foo() print(2,foo.__defaults__) #2 ([], 'abc', 123) foo([10]) print(3,foo.__defaults__) #3 ([], 'abc', 123) foo([10,5]) print(4,foo.__defaults__) #4 ([], 'abc', 123) #返回结果: [1] 1 ([], 'abc', 123) [1] 2 ([], 'abc', 123) [10, 1] 3 ([], 'abc', 123) [10, 5, 1] 4 ([], 'abc', 123)

总结:xyz都是传入参数或者默认参数的副本,如果就想修改参数,无能为力方法二:使用不可变类型默认值(常用)1)如果使用缺省值None就创建一个列表2)如果传入一个列表,就修改这个列表3)通过值的判断就可以灵活的选择创建或者修改传入对象4)这种方式灵活,应用广泛5)很多函数的定义,都可以看到使用None这个不可变的值作为默认参数,可以说这是一种惯用法举例1

def foo(xyz=None,u='abc',z=123): #第一次调用没有传值,调用缺省值,当前缺省值是None if xyz is None: #如果是None xyz = [] #给xyz从新赋值 xyz.append(1) print(xyz) #打印列表里的内容 foo() print(1,foo.__defaults__) #1 (None, 'abc', 123) foo() print(2,foo.__defaults__) #2 (None, 'abc', 123) foo([10]) print(3,foo.__defaults__) #3 (None, 'abc', 123) foo([10,5]) print(4,foo.__defaults__) #4 (None, 'abc', 123) #返回结果: [1] 1 (None, 'abc', 123) [1] 2 (None, 'abc', 123) [10, 1] 3 (None, 'abc', 123) [10, 5, 1] 4 (None, 'abc', 123)

总结:如果使用缺省值None就出安静一个列表如果传入一个列表,就修改这个列表举例2:

def foo(xyz=None,u='abc',z=123): #2.第一次调用没有传值,调用缺省值,当前缺省值是None #9.第二次传有值不用缺省值,xyz不是None if xyz is None: #3.如果是None xyz = [] #4.给xyz从新赋值产生空列表 xyz.append(1) #5.给空列表加一个元素[1] #10.给列表[1]加一个元素变为[1,1] return xyz #6.把[1]返回 #11.把[1,1]返回 lst = foo() #1.调用foo函数 #7.lst接收[1] a = foo(lst) #8.调用foo函数把[1]传进去 print(a) #12打印[1,1] #返回结果: [1, 1]

五.函数即变量

def test1(): #定义了个变量test1() print 'in the test1' test2() #test1()这个变量定义了一个不存在的变量test2()(内存不存在) def test2(): #把test2()变量又定义了出来(内存里就存在了) print 'in the test2' test1() #返回结果: in the test1 in the test2name = '全局变量' #第1步执行 def s1(): #第2步执行 name = "嵌套1" #第4.1步执行 print(name) #第4.2步执行(输出第二行) def s2(): #第4.3步执行 name = "嵌套2" #第4.4.1步执行 print(name) #第4.4.2步执行(输出第三行) def s3(): #第4.4.3步执行 name = '嵌套3' #第4.4.5.1步执行执行 print(name) #第4.4.5.2步执行(输出第五行) print(name) #第4.4.4步执行(输出第四行) s3() #第第4.4.5步执行步执行 s2() #第4.4步执行 print(name) #第4.5步执行(输出第六行) print(name) #第3步执行(输出第一行) s1() #第4步执行 print(name) #第5步执行(输出第七行) #返回结果: 全局变量 嵌套1 嵌套2 嵌套2 嵌套3 嵌套1 全局变量

六.函数的销毁1.全局函数销毁(1)重新定义同名函数(2)del语句删除函数对象(3)程序结束时2.局部函数(1)重新在上级作用域定义同名函数(2)del语句删除函数对象(3)上级作用域销毁时七.变量名解析原则LEGB1.Local,本地作用域,局部作用域的local命名空间,函数调用时创建,调用结束消亡2.Enclosing,python2.2时引入了嵌套函数,实现了闭包,这个就是嵌套函数的外部函数的命名空间3.Global,全局作用域,即一个模块的命名空间,模块被import时创建,解释器退出时消亡。4.Build-in,内置模块的命名空间,生命周期从python解释器启动时创建到解释器退出时消亡。例如print(open),print和open都是内置的变量5.所以一个名次的查找顺序就是LEGB第五部分:python匿名函数lambda1.匿名函数:是一种快速定义单行的最小函数方法,通常是用来跟其它函数一块联合使用,他不是单独存在,可以用在任何需要函数的地方1.即没有名字的函数(2)python借助Lambda表达式构建匿名函数2.语法结构:(1)lambda x:x+1  (lambda的关键字加上匿名函数的形参x:x+1)(2)lambda语句中,冒号前是匿名函数的形参,可以有多个,用逗号隔开,冒号后就是这个函数的返回值(相当于函数直接给你return了个值)(3)lambda语句构建的其实是一个函数对象3.特点(1)使用lambda关键字来定义匿名函数(2)参数列表不需要小括号(3)冒号是用来分割参数列表和表达式(4)不需要使用return,表达式的值,就是匿名函数返回值(5)lambda表达式(匿名函数)只能写在一行上,被称为单行函数4.用途在高阶函数传参时,使用lambda表达式,往往能简化代码(1)无参函数标准函数写法:

def fn(): return 0 print(fn()) #返回:0

匿名函数写法:

>>> print((lambda:0)()) 0

(2)传1个形参数函数标准函数写法:

def fn(x,y=3): return x+y print(fn(5)) #返回:8

匿名函数写法:

>>> print((lambda x,y=3:x+y)(5)) #8

(3)传1个形参1个实参数函数标准函数写法:

def fn(x,y=3): return x+y print(fn(5,6)) #返回:11

匿名函数写法:

>>> print((lambda x,y=3:x+y)(5,6)) 11

(4)传1个形参加默认值参数标准函数写法:

def fn(x,*,y=30): return x+y print(fn(5)) #返回:35

匿名函数写法:

>>> print((lambda x,*,y=30:x+y)(5)) 35

(5)传1个形参加1个实参替换默认值参数标准函数写法:

def fn(x,*,y=30): return x+y print(fn(5,y=10)) #返回:15

匿名函数写法:

>>> print((lambda x,*,y=30:x+y)(5,y=10)) 15

(6)可变参数标准函数写法:

def fn(*args): return [x for x in args] print(fn(*range(5))) #返回:[0, 1, 2, 3, 4]

匿名函数写法:

>>> print((lambda *args:[x for x in args])(*range(5))) [0, 1, 2, 3, 4]

(7)可变参数加运算标准函数写法:

def fn(*args): return [x + 2 for x in args] print(fn(*range(5))) #返回:[2, 3, 4, 5, 6]

匿名函数写法:

>>> print((lambda *args:[x+2 for x in args])(*range(5))) [2, 3, 4, 5, 6]

第六部分:递归函数一.递归函数在函数内部,可以调用其他函数。如果在调用一个函数的过程中直接或间接调用自身本身(1)递归:递归就是程序调用自身不断深入嵌套,直到满足条件退出的一种算法递归特性:(1)必须有一个明确的结束条件(2)每次进入更深一层递归时,问题规模相比上次递归都应有所减少(3)递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)1.函数执行流程

#全局帧中生成foo1,foo2,foo3,main函数对象 def foo1(b,b1=3): print("foo1 called",b,b1) #第四步:print函数压栈,字符串和变量b,b1压栈,调用函数,弹出栈顶,返回值 def foo2(c): foo3(c) #第六步:foo3函数压栈,变量c引用压栈,调用foo3,创建栈帧 print("foo2 called",c) #第八步:foo2恢复调用,执行print后,返回值,min中foo2调动结束弹出栈顶 def foo3(d): print("foo3 called",d) #第七步:foo3完成portin函数调用后返回。 def main(): print("main called") #第二步:main中查找内建函数print压栈,将常量字符串压栈,调用函数,弹出栈顶 foo1(100,101) #第三步:main中全局查找函数foo1压栈,将常量100,101压栈,调用函数foo1,创建栈帧 foo2(200) #第五步:main中全局查找foo2函数压栈,将常量200压栈,调用foo2,创建栈帧。 print("main ending") #第六步:main继续执行print函数调用,弹出栈顶,main函数返回 main() #第一步:main函数调用main()函数 #返回结果: main called foo1 called 100 101 foo3 called 200 foo2 called 200 main ending

2.函数的递归(Recursion)(1)函数直接或者间接调用自身就是递归(2)递归需要有边界条件,递归前进段,递归返回段(3)递归一定要有边界条件(4)当边界条件不满足的时候,递归前进(5)当边界条件满足的时候,递归返回(6)举例:

def fib(n): return 1 if n < 2 else fib(n-1) + fib(n-2) #第二步:判断4不小于2走else得到fib(3)+fib(2),当前要返回的值是2 #第三步:fib(3)调用函数把3传进去,判断3不小于2走else得到fib(2)+fib(1),当前要返回的值是3 #第四步:fib(2)调用函数把2传进去,判断2不小于2走else得到fib(1),当前要返回的值是4 #第五步:fib(1)调用函数把1传进去,判断1小于2是边界return返回,当前要返回的值是5 print(fib(4)) #第一步:调用fib函数把4传进去 #返回结果 5

3.递归要求(1)递归一定要有退出条件,递归调用一定要执行到这个退出条件。没有退出条件的递归调用,就是无限调用。(2)递归调用的深度不宜过深:python对递归调用的深度做了限制,以保护解释器,超过递归深度限制,抛出RecursionError:maxinum recursion depth exceeded超出最大深度sys.getrecursionlimit()4.递归的性能(1)未优化递归实现斐波那契数列(循环写法)

import datetime n = 35 start = datetime.datetime.now() def fib(n): return 1 if n < 2 else fib(n - 1) + fib(n - 2) for i in range(n): print(fib(i),end=' ') delta = (datetime.datetime.now() - start).total_seconds() print() print('执行时间:',delta) #返回结果: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 10946 17711 28657 46368 75025 121393 196418 317811 514229 832040 1346269 2178309 3524578 5702887 9227465 执行时间: 8.91451

总结:1)循环稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果2)fib函数代码极简易懂,但是只能获取到最外层的函数调用,内部递归结果都是中间结果,而且给定一个n都要进行2n次递归,深度越深,效率越低,为了获取斐波那契数列需要外面在套一个n次的循环,效率就更低了3)递归还有深度限制,如果递归复杂,函数返回压栈,栈内存内裤就溢出了(2)优化递归实现斐波那契数列(上一次的计算结果直接作为函数的实参)

pre = 0 cur = 1 #print(pre,cur) def fib(n,pre=0,cur=1): pre,cur = cur,pre + cur print(cur,end=' ') if n == 2: return #return none fib(n-1,pre,cur) #第二步:函数接收参数fib(n=5,pre=0,cur=1),pre(1),cur(1) = cur(1)给pre,(pre + cur)(1)给cur,当前cur是1打印1,if判断5不等于2,当前fib(5-1,pre(1),cur(1))继续递归 #第三步:函数接收参数fib(n=4,pre=1,cur=1),pre(1),cur(2) = cur(1)给pre,(pre + cur)(2)给cur,当前cur是2打印2,if判断4不等于2,当前fib(4-1,pre(1),cur(2))继续递归 #第四步:函数接收参数fib(n=3,pre=1,cur=2),pre(2),cur(3) = cur(2)给pre,(pre + cur)(3)给cur,当前cur是3打印3,if判断3不等于2,当前fib(3-1,pre(2),cur(3))继续递归 #第五步:函数接收参数fib(n=2,pre=2,cur=3),pre(3),cur(5) = cur(3)给pre,(pre + cur)(5)给cur,当前cur是1打印1,if判断2等于2执行return none跳出递归 fib(5) #第一步:调用函数把5传进去 #返回结果: 1 2 3 5

总结:1)fib函数和循环的思想类似2)参数n是边界条件,用n来计数3)上一次的计算结果直接作为函数的实参4)效率很高5)和循环比较,性能相近。所以并不是说递归一定效率低下。但是递归有深度限制5.间接递归间接递归,是通过别的函数调用了函数自身,但是,如果构成了函数递归调用是非常危险的,但是往往这种清空在代码复杂的情况下,还是可以发生这种调用。要用代码的规范来避免这种递归调用的发生

def foo1(): foo2() def foo2(): foo1() foo1()

6.递归总结(1)递归是一种很自然的表达,符合逻辑思维(2)递归相对运行效率低,每一次调用函数都要开辟栈帧(3)递归有深度限制,如果递归层次太深,函数反复压栈,栈内存很快就溢出了(4)如果是有限次数的递归,可以使用递归调用,或者使用循环代替,循环代替稍微复杂一些,但是只要不是死循环,可以多次迭代直至算出结果(5)绝大多数递归,都可以使用循环实现(6)即使递归代码很简洁,但是能不用则不用递归7.递归应用

#!/usr/bin/python # -*- coding: utf-8 -* import time person_list=['wang','shi','yao'] #定义人名列表['wang','shi','yao'] def ask_way(person_list): #定义函数把人这个列表传进去 print('-'*30) if len(person_list) == 0: #如果person_lis不在['wang','shi','yao']这些里代表没人知道 return '没人知道' person=person_list.pop(0) #把每次列表的名字一次弹出一个赋值给person if person == 'yao': #如果人名等于yao执行return 返回"我知道,路就在这里" return '%s说:我知道,路就在这里' %person print('xix问:[%s],路在那里?' %person) print('%s回答道:我不知道,你等着,我帮你问问%s...' %(person,person_list)) time.sleep(2) res=ask_way(person_list) #递归着问就要在函数里面调用自己(把问的结果交给res) return res res=ask_way(person_list) print(res) #输出结果: ------------------------------ xix问:[wang],路在那里? wang回答道:我不知道,你等着,我帮你问问['shi', 'yao']... ------------------------------ xix问:[shi],路在那里? shi回答道:我不知道,你等着,我帮你问问['yao']... ------------------------------ yao说:我知道,路就在这里

第七部分:内建函数1.标识id:返回对象的唯一标识,CPython返回内存地址2.哈希hash():可hash的数据类型即不可变数据类型,不可hash的数据类型即可变数据类型(1)特性:不管传入参数有多长最终结果长度固定不能根据最终hash的最终结果反推出来只要变量不变,得到的hash值结果都一样

hash('xixi') #字符串 #返回:485502670088932892 hash(str(sorted({'1':1}))) #字典 #返回:7666464346782421378

3.类型type():返回对象的数据类型

msg='123' if type(msg) is str: #判断msg是否是整型 msg=int(msg) #不是转换成int res=msg+1 print(res) #返回:124

4.类型转换:float() int() bin() hex() oct() bool() list() tuple() dict() set() complex() bytes() bytearray()5.输入input([prompt]):接收用户输入,返回一个字符串6.打印print(*objects,sep='',end='\n',file=sys.stdout,flush=False):打印输出,默认使用空格分割,换行结尾,输出到控制台7.对象长度len(s):返回一个集合类型的元素个数8.isinstance(obj,class_or_tuple):判断对象obj是否属于某种类型或者元祖中列出某个类型

a = 2 isinstance (a,int) #判断a是否是int类型 #返回:True

9.issubclass(cls,class_or_tuple):判断类型cls是否是某种类型的子类型或元祖中列出的某个类型的子类10.绝对值abs(x):x为数值

print (abs(-45)) #返回:45

11.最大值max(),最小值()功能1:简单列表比较

l=[1,2,3,-5,100] print(max(l)) print(min(l)) #返回: 100 -5

功能2:比较复杂列表

l=[(5,'e'),(1,'c'),(2,'b'),(3,'d'),] #默认从第一个值开始比较5最大 print(list(max(l))) #返回:[5, 'e']

功能3:比较字典

age_dic={'age1':19,'age4':20,'age3':100,'age2':30} print(max(age_dic)) #默认比较的是字典的key,根据ASCII码逐步去比较大小 print(max(age_dic.values())) #比较的是字典的value print((max(zip(age_dic.values(),age_dic.keys()))) ) #(zip(age_dic.values(),age_dic.keys())相当于[(19,'age1'),(20,'age4'),(100,'age3') ] #返回: age4 100 [100, 'age3']

功能4:比较复杂的字典

people=[{'name':'xixi','age':1000},{'name':'wang','age':1111},{'name':'shi','age':111},{'name':'yao','age':11},] print(max(people,key=lambda dic:dic['age'])) #for循环people把取出来的值都给lambda表达式,值都是小字典 #返回:{'age': 1111, 'name': 'wang'}

注意:(1)max和min函数处理的是可迭代对象,相当于for循环取出每个元素进行比较,不通类型直接不能进行比较。(2)每个元素间进行比较,是从每个元素的第一个位置以此比较,如果第一位置分出大小,后面的都不需要比较,直接得出这俩元素的大小12.round(x)四舍六入五取偶,round(-0.5)

print(round(3.5)) #返回:4

13.pow(x,y)等价于x**y

print(pow(3,3)) #返回:9

14.range(stop)从0开始到stop-1的可迭代对象;range(start,stop[,step])从start开始到stop-1结束步长为step的可迭代对象15.divmod(x,y)除数和余数运算结果结合起来(做分页功能),等价于tuple(x//y,x%y)

divmod(10, 3) #返回:(3, 1)---3总共分了3页余1页

16.sum(iterable[,start])对可迭代对象的所有数值元素求和

l=[1,2,3,4] print(sum(l)) #返回:10

17.chr(i)给一个一定范围的整数返回对应的字符1)十六进制

print chr(0x30), chr(0x31), chr(0x61) #十六进制 #返回:0 1 a

2)十进制

print chr(48), chr(49), chr(97) #十进制 #返回:0 1 a

18.ord()返回对应的ASCII数值

print(ord('a')) #返回:97

19.str()将对象转化为字符串

print(str({'a':1})) #返回:"{'a':1}"

20.repr()函数将对象转化为供解释器读取的形式21.sorted(iterable[,key][,reverse])排序,返回一个新的列表,默认升序,revers是反转举例:默认升序

>>> sorted([3,1,5]) [1, 3, 5]

举例:加参数

>>> sorted([1,3,5],reverse=True) [5, 3, 1]

22.翻转reversed(seq):返回一个翻转元素的迭代器举例:(reversed("13579")生成可迭代对象元素,list生成新列表

>>> list(reversed("13579")) ['9', '7', '5', '3', '1']

23.枚举enumerate(seq,start=0)迭代一个序列,返回索引数字和元素构成的二元组,start表示索引开始的数字,默认是0(1)举例:

for i in enumerate(range(5)): print(i) #返回:二元组 (0, 0) (1, 1) (2, 2) (3, 3) (4, 4)

(2)举例:带参数索引偏移

for i in enumerate(range(5),start=2): print(i) #返回: (2, 0) (3, 1) (4, 2) (5, 3) (6, 4)

(3)举例:每次循环k,v解构拿到两个值

for k,v in enumerate(range(5)): print(k,v,end="\t") #返回: 0 0 1 1 2 2 3 3 4 4

24.迭代器和取元素iter(iterable),next(iterator)iter讲一个可迭代对象封装成一个迭代器next对一个迭代器去下一个元素,如果全部元素都取过了,再次next会抛出StopIteration异常举例:next按顺序每次拿一个

>>> it=iter(range(3)) >>> next(it) 0 >>> next(it) 1 >>> next(it) 2 >>> next(it) #报错 Traceback (most recent call last): File "", line 1, in StopIteration

举例:reversed按顺序隔一个取

>>> it = reversed([1,3,5]) >>> next(it) 5 >>> next(it) 3 >>> next(it) 1 >>> next(it) Traceback (most recent call last): File "", line 1, in StopIteration

25.拉链函数zip(*iterables)像拉链一样,把多个可迭代对象合并在一起,返回一个迭代器,将每次从不同对象中取到的元素举例:从第一个迭代对象拿一个0,从第二个迭代对象里拿一个0凑成一对,以此类推生成元祖

>>> list(zip(range(10),range(10))) [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)]

举例:从第一个迭代对象拿一个0,从第二个迭代对象里拿一个0凑成一对,以此类推生成元祖,以最短的配

>>> list(zip(range(10),range(10),range(5))) [(0, 0, 0), (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4)]

举例:从第一个迭代对象拿一个0,从第二个迭代对象里拿一个0凑成一对,以此类推生成元祖,解构成字典

>>> {str(x):y for x,y in zip(range(10),range(10))} {'6': 6, '9': 9, '5': 5, '4': 4, '0': 0, '3': 3, '7': 7, '2': 2, '1': 1, '8': 8}

26.all():把序列所有元素做布尔运算如果都是返回true,否则返回false(元素除了是 0、空、FALSE外都算TRUE)举例1:

all(['a', 'b', 'c', 'd']) #列表list,元素都不为空或0 #返回:True

举例2:

all(['a', 'b', '', 'd']) #列表list,存在一个为空的元素 #返回:False

27.any():把序列所有元素做布尔运算如果都是返回true,如果有一个为True,则返回True,如全是0、空、FALSE则返回FALSE举例1:

any(['a', 'b', 'c', 'd']) #列表list,元素都不为空或0 #返回:True

举例2:

any(['a', 'b', '', 'd']) #列表list,存在一个为空的元素 #返回:True

举例3:

any([0, '', False]) #列表list,元素全为0,'',false #返回:False

28.bytes():把一个字符串转换成字节的形式(编码转换二进制)

name="你好" print(bytes(name,encoding='utf-8')) #返回: b'\xe4\xbd\xa0\xe5\xa5\xbd'

decode方式解码:

print(bytes(name,encoding='utf-8').decode('utf-8')) #返回:你好

29.chr():用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。30.dir():打印某一个对象下面的各种方法名字31.eval():功能1:把字符串里的数据结构提取出来

dic_str1="{'name':'xixi'}" eval(dic_str1) #返回:{'name': 'xixi'}

功能2:把字符串里的数学运算做一遍

x=5 eval( '3 * x' ) #返回:15

32.help():打印方法的相关解释33.bin():把10进制转换成2进制,以字符串形式表示

bin(10) #返回:'0b1010'

34.hex()函数:将10进制整数转换成16进制,以字符串形式表示

hex(255) #返回:'0xff'

35.oct()函数:将一个整数转换成8进制字符串

oct(10) #返回:'012'

36.globals()函数:会以字典类型返回当前位置的全部全局变量

a='xixi' print(globals()) #globals函数返回一个全局变量的字典,包括所有导入的变量。 #返回:{'__builtins__': , '__name__': '__main__', '__doc__': None, 'a': 'xixi', '__package__': None}

37.locals()函数:会以字典类型返回当前位置的全部局部变量

def runoob(arg): #第一个局部变量argz z = 1 #第二个局部变量z print (locals()) runoob(2) #返回:{'z': 1, 'arg': 2} #返回一个名字/值对的字典

38.set()函数:创建一个无序不重复元素集

print(set('hello')) #返回:{'l','o','h','e'}

39.slice()函数:实现切片对象,主要用在切片操作函数里的参数传递

l='hello' s1=slice(3.5) s2=slice(1,4,2) #取步长 print(l[s1]) print(l[s2]) 返回: lo ell='hello' s1=slice(3.5) s2=slice(1,4,2) #取步长 print(l[s1]) print(l[s2]) #返回: lo el

40.str()函数:将对象转化为字符串41.vars()函数:返回对象object的属性和属性值的字典对象

def test(): msg='西西西西西' print(vars()) test() #返回:{'msg': '西西西西西'}

42.__import__()函数:用于动态加载类和函数43.reduce归纳函数:处理一个序列,然后把序列进行合并操作举例1:数字列表[1,2,3,100]把所有的值加起来再加上初始值100(1)匿名函数lambda方式写法:

num_l=[1,2,3,100] def reduce_test(func,array,init=None): if init is None: res=array.pop(0) else: res=init for num in array: res=func(res,num) return res print(reduce_test(lambda x,y:x+y,num_l,100)) #详解:res=array.pop(0)先拿到列表的第一个值,初始值设定默认init=None,判断当init=None没有初始值,否则:res=init #输出: 206

(2)reduce函数写法:

from functools import reduce #导入模块 num_l=[1,2,3,100] print(reduce(lambda x,y:x+y,num_l,1)) #指定初始值1 print(reduce(lambda x,y:x+y,num_l)) #不指定初始值 详解:第一个参数函数lambda x,y:x+y,第二个是一个序列num_l,第三个是初始值1 #输出: 107 106

举例:计算1到100的和

from functools import reduce print(reduce(lambda x,y:x+y,range(100),100)) #range(100)相当于得到list列表,然后x,y一次赋俩个值做x+y print(reduce(lambda x,y:x+y,range(1,101))) #输出: 5050 5050

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3