飞道的博客

【python与数据分析】CH5 函数

399人阅读  评论(0)

目录

前言

一、函数定义与调用基本语法

1.函数定义语法

2.【例5.1.1】编写生成(不大于n)斐波那契数列的函数并调用

3.函数递归调用

(1)介绍

 (2)【例5-2】使用递归法对整数进行因数分解

二、函数参数

1.位置参数

2.默认值参数

 3.关键参数

4.可变长度参数

5.传递参数时的序列解包

三、变量作用域

四、lambda表达式

五、生成器函数设计要点

1.介绍

2.【例5-3】编写并使用能够生成斐波那契数列的生成器函数


前言

  • 掌握函数定义和调用的用法
  • 理解递归函数的执行过程
  • 掌握位置参数、关键参数、默认参数和长度可变参数的用法
  • 理解函数调用时参数传递的序列解包用法
  • 理解变量作用域
  • 掌握lamda表达式的定义与用法
  • 理解生成器函数工作原理

一、函数定义与调用基本语法

1.函数定义语法

def 函数名([参数列表]):

        '''注释'''

        函数体

【注】

  • 为何要定义函数:实现代码的复用、保证代码的一致性、复杂问题简化(任务分拆)等
  •  函数形参不需要声明类型,也不需要指定函数返回值类型
  • 即使该函数不需要接受任何参数,也必须保留一对空的圆括号
  • 括号后面的冒号必不可少
  • 函数体相对于def关键字必须保持一定的空格缩进
  • python允许嵌套定义函数

2.【例5.1.1】编写生成(不大于n)斐波那契数列的函数并调用

  • 斐波那契数列:0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181……
  • 这个数列第0项是0,第1项是1,从第三项开始,每一项都等于前两项之和
  • f(n)=f(n-1)+f(n-2),n=2,3……

  
  1. >>> import turtle
  2. >>> a,b= 0, 1
  3. >>> while a< 1000:
  4. ... turtle.circle(a, 90)
  5. ... a,b=b,a+b
  6. ...
  7. ...


  
  1. >>> def fib( n):
  2. ... a,b= 1, 1
  3. ... while a<n:
  4. ... print(a,end= ' ')
  5. ... a,b=b,a+b
  6. ... print()
  7. ...
  8. ...
  9. >>> fib( 100)
  10. 1 1 2 3 5 8 13 21 34 55 89

3.函数递归调用

(1)介绍

        函数的递归调用是函数调用的一种特殊情况,函数调用自己,自己在调用自己,自己再调用自己……当某个条件得到满足的时候就不再调用了,然后再一层一层地返回直到该函数第一次调用的位置。


  
  1. #计算第n个Fibonacci数
  2. >>> def fib( n):
  3. ... a,b= 0, 1
  4. ... count= 1
  5. ... while count<n:
  6. ... a,b=b,a+b
  7. ... count=count+ 1
  8. ... print(a)
  9. ...
  10. ...
  11. >>> fib( 10)
  12. 34
  13. >>> def fib( n):
  14. ... if n== 0 or n== 1:
  15. ... return n
  16. ... else:
  17. ... return fib(n- 1)+fib(n- 2)
  18. ...
  19. ...
  20. >>> fib( 10)
  21. 55

 (2)【例5-2】使用递归法对整数进行因数分解


  
  1. >>> from random import randint
  2. >>> def factors( num,fac=[]):
  3. ... #每次都从2开始查找因数
  4. ... for i in range( 2, int(num** 0.5)+ 1):
  5. ... #找到一个因数
  6. ... if num%i== 0:
  7. ... fac.append(i)
  8. ... #对商继续分解,重复这个过程
  9. ... factors(num/i,fac)
  10. ... #注意,这个break非常重要
  11. ... break
  12. ... else:
  13. ... #不可分解了,自身也是个因数
  14. ... fac.append(num)
  15. ...
  16. ...
  17. >>> fac=[]
  18. >>> n=randint( 2, 10** 8)
  19. >>> factors(n,fac)
  20. >>> result= '*'.join( map( str,fac))
  21. >>> if n== eval(result):
  22. ... print(n, '= '+result)
  23. ...
  24. ...
  25. 10666235 = 5* 941* 2267.0

二、函数参数

        函数定义时圆括弧内是使用括号分隔开的形参列表(parameters),函数可以有多个参数,也可以没有参数,但定义和调用时一对圆括弧必须要有,表示这是一个函数并且不接受参数。

        调用函数时向其传递实参(arguments),根据不同的参数类型,将实参的引用传递给形参。

        定义函数时不需要声明参数类型,解释器会根据实参的类型自动推断形参的类型。

1.位置参数

        位置参数(positional arguments)是比较常用的形式,调用函数时实参和形参的顺序必须严格一致,并且实参和形参的数量必须相同


  
  1. >>> def demo( a,b,c):
  2. ... print(a,b,c)
  3. ...
  4. ...
  5. >>> demo( 3, 4, 5) #按位置传递参数
  6. 3 4 5
  7. >>> demo( 3, 5, 4)
  8. 3 5 4
  9. >>> demo( 1, 2, 3, 4) #实参与形参数量必须相同
  10. Traceback (most recent call last):
  11. File "<pyshell#57>", line 1, in <module>
  12. demo( 1, 2, 3, 4)
  13. TypeError: demo() takes 3 positional arguments but 4 were given

2.默认值参数

        在调用带有默认值参数的函数时,可以不用为设置了默认值的形参进行传值,此时函数将会直接使用函数定义时设置的默认值,当然也可以通过显式赋值来替换其默认值。在调用函数时是否为默认值参数传递实参是可选的。

        需要注意的是,在定义带有默认值参数的函数时,任何一个默认值参数右边不能再出现没有默认值的普通位置参数,否则会提示语法错误。

        带有默认值(事先给形参赋的值)参数的函数定义语法如下:

def 函数名(……,形参名=默认值):

        函数体


  
  1. >>> def say( message,times=1):
  2. ... print((message+ ' ')*times)
  3. ...
  4. ...
  5. >>> say( 'hello')
  6. hello
  7. >>> say( 'hello', 3)
  8. hello hello hello

 3.关键参数

        关键参数主要指调用函数时的参数传递方式(调用函数时给出参数名(关键参数),传递实参就与顺序无关了!)与函数定义无关。通过关键参数可以按参数名字传递值,明确指定哪个值传递给哪个参数,实参顺序可以和形参顺序不一致,但不影响参数值的传递结果,避免了用户需要牢记参数位置和顺序的麻烦,使用函数的调用和参数传递更加灵活方便。


  
  1. >>> def demo( a,b,c=5):
  2. ... print(a,b,c)
  3. ...
  4. ...
  5. >>> demo( 3, 7)
  6. 3 7 5
  7. >>> demo(c= 8,a= 0,b= 9)
  8. 0 9 8

4.可变长度参数

        可变长度参数主要有两种形式:在参数名前加1个*或2个**

  • *parametre用来接收多个位置参数并将其放在一个元组

  
  1. >>> def demo( *p):
  2. ... print(p)
  3. ...
  4. ...
  5. >>> demo( 1, 2, 3)
  6. ( 1, 2, 3)
  7. >>> demo( 1, 2)
  8. ( 1, 2)
  9. >>> demo( 1, 2, 3, 4, 5, 6, 7, 8)
  10. ( 1, 2, 3, 4, 5, 6, 7, 8)
  • **parametre接收多个关键参数并存放到字典

  
  1. >>> def demo( **p):
  2. ... for item in p.items():
  3. ... print(item)
  4. ...
  5. ...
  6. >>> demo(x= 12,y= 11,z= 10)
  7. ( 'x', 12)
  8. ( 'y', 11)
  9. ( 'z', 10)

5.传递参数时的序列解包

        传递参数时,可以通过在实参序列前加一个星号将其解包,然后传递给多个单变量形参


  
  1. >>> def demo( a,b,c):
  2. ... print(a+b+c)
  3. ...
  4. ...
  5. >>> seq=[ 1, 2, 3]
  6. >>> demo(*seq)
  7. 6
  8. >>> tup=( 1, 2, 3)
  9. >>> demo(*tup)
  10. 6
  11. >>> dic={ 1: 'a', 2: 'b', 3: 'c'}
  12. >>> demo(*dic)
  13. 6
  14. >>> set1={ 1, 2, 3}
  15. >>> demo(*set1)
  16. 6
  17. >>> demo(*dic.values())
  18. abc

        如果函数实参是字典,可以在前面加两个星号进行解包,等价于关键参数。


  
  1. >>> def demo( a,b,c):
  2. ... print(a+b+c)
  3. ...
  4. ...
  5. >>> dic={ 'a': 1, 'b': 2, 'c': 3}
  6. >>> demo(**dic)
  7. 6
  8. >>> demo(a= 1,b= 2,c= 3)
  9. 6
  10. >>> demo(*dic.values())
  11. 6

三、变量作用域

        变量起作用的代码范围称为变量的作用域,不同作用域内变量名可以相同,互不影响

        在函数内部定义的普通变量只在函数内部起作用,称为局部变量。当函数执行结束后,局部变量自动删除,不再可以使用。

        局部变量的引用比全局变量速度快,应优先考虑使用。

        全局变量可以通过关键字global来定义。这分为两种情况:

  • 一个变量已在函数外定义,如果在函数内需要为这个变量赋值,并要将这个赋值结果反映到函数外,可以在函数内使用global将其声明为全局变量
  • 如果一个变量在函数外没有定义,在函数内部也可以直接将一个变量定义为全局变量,该函数执行后,将增加一个新的全局变量

        也可以这么理解:

  • 在函数内只引用某个变量的值而没有为其赋新值,如果这样的操作可以执行,那么该变量为(隐式的)全局变量
  • 如果在函数内任意位置有为变量赋新值的操作,该变量即被认为是(隐式的)局部变量,除非在函数内显式地用关键字global进行声明

  
  1. >>> def demo():
  2. ... global x #函数内定义x为全局变量
  3. ... x= 3
  4. ... y= 4 #y为函数体内的变量(局部变量)
  5. ... print(x,y)
  6. ...
  7. ...
  8. >>> x= 5
  9. >>> demo() #调用函数即启动了全局变量x
  10. 3 4
  11. >>> x
  12. 3
  13. >>> y
  14. Traceback (most recent call last):
  15. File "<pyshell#114>", line 1, in <module>
  16. y
  17. NameError: name 'y' is not defined
  18. >>> del x
  19. >>> x #全局变量删除后就失效了
  20. Traceback (most recent call last):
  21. File "<pyshell#116>", line 1, in <module>
  22. x
  23. NameError: name 'x' is not defined
  24. >>> demo() #再次定义了全局变量
  25. 3 4
  26. >>> x
  27. 3
  28. >>> y
  29. Traceback (most recent call last):
  30. File "<pyshell#119>", line 1, in <module>
  31. y
  32. NameError: name 'y' is not defined

        如果局部变量与全局变量具有相同的名字,那么该局部变量会在自己的作用域内隐藏同名的全局变量


  
  1. >>> def demo():
  2. ... x= 3 #创建了全局变量,并自动隐藏了同名的全局变量
  3. ...
  4. ...
  5. >>> x= 5
  6. >>> x
  7. 5
  8. >>> demo()
  9. >>> x #函数执行不影响外面全局变量的值
  10. 5

四、lambda表达式

        lambda表达式可以用来声明匿名函数,也就是没有函数名字的临时使用的小函数,尤其适合需要一个函数作为另一个函数参数的场合,也可以定义具名函数

        lambda表达式只可以包含一个表达式,该表达式的计算结果可以看作是函数的返回值,不允许包含复合语句,但在表达式中可以调用其他函数。


  
  1. >>> f= lambda x,y,z:x+y+z #可以给lambda表达式起名字
  2. >>> f( 1, 2, 3) #像函数一样调用
  3. 6
  4. >>> g= lambda x,y= 2,z= 3:x+y+z #参数默认值
  5. >>> g( 1)
  6. 6
  7. >>> g( 2,z= 4,y= 5) #关键参数
  8. 11
  9. >>> L=[ 1, 2, 3, 4, 5]
  10. >>> print( list( map( lambda x:x+ 10,L))) #模拟向量运算
  11. [ 11, 12, 13, 14, 15]
  12. >>> L
  13. [ 1, 2, 3, 4, 5]
  14. >>> data= list( range( 20)) #创建列表
  15. >>> data
  16. [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
  17. >>> import random
  18. >>> random.shuffle(data) #打乱顺序
  19. >>> data
  20. [ 16, 2, 15, 0, 7, 17, 12, 14, 9, 11, 4, 18, 3, 8, 1, 10, 19, 13, 6, 5]
  21. >>> data.sort(key= lambda x: len( str(x))) #按转换成字符串以后的长度排序
  22. >>> data
  23. [ 2, 0, 7, 9, 4, 3, 8, 1, 6, 5, 16, 15, 17, 12, 14, 11, 18, 10, 19, 13]
  24. >>> data.sort(key= lambda x: len( str(x)),reverse= True) #降序排序,字符长度一样保持原位置
  25. >>> data
  26. [ 16, 15, 17, 12, 14, 11, 18, 10, 19, 13, 2, 0, 7, 9, 4, 3, 8, 1, 6, 5]

五、生成器函数设计要点

1.介绍

        包含yield语句的函数可以用来创建生成器对象,这样的函数也成生成器函数。

        yield语句与return语句的作用类似,都是用来从函数中返回值。与return语句不同的是,return语句一旦执行会立刻结束函数的运行,而每次执行到yield语句并返回一个值后会暂停或挂起后面代码的执行,下次通过生成器对象的__next__()方法、内置函数next()、for循环遍历生成器对象元素或其他方式显式“索要”数据时恢复执行。

        生成器具有惰性求值的特点,适合大数据处理。

2.【例5-3】编写并使用能够生成斐波那契数列的生成器函数


  
  1. >>> def f():
  2. ... a,b= 1, 1 #序列解包,同时为多个元素赋值
  3. ... while True:
  4. ... yield a #暂停执行,需要时再生产一个新元素
  5. ... a,b=b,a+b #序列解包,继续生成新元素
  6. ...
  7. ...
  8. >>> a=f() #创建生成器对象
  9. >>> for i in range( 10): #斐波那契数列前10个元素
  10. ... print(a.__next__(),end= ' ')
  11. ...
  12. ...
  13. 1 1 2 3 5 8 13 21 34 55
  14. >>> for i in f(): #斐波那契数列中第一个大于100的元素
  15. ... if i> 100:
  16. ... print(i,end= ' ')
  17. ... break
  18. ...
  19. ...
  20. 144
  21. >>> a=f() #创建生成器对象
  22. >>> next(a) #使用内置函数next()获取生成器对象中的元素
  23. 1
  24. >>> next(a) #每次索取新元素时,由yield语句生成
  25. 1
  26. >>> a.__next__() #也可以调用生成器对象的__net__()方法
  27. 2
  28. >>> a.__next__()
  29. 3
  30. >>> next(a)
  31. 5


转载:https://blog.csdn.net/m0_72318954/article/details/127471639
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场