Python入门之(5.1)函数

目录

函数

1.1函数的定义与使用

1.2函数的参数

1.3函数的返回值

1.4变量作用域

1.5匿名函数

1.6高阶函数

1.7函数的嵌套

1.8递归函数


函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。Python提供了许多内建函数,也可以创建自定义函数。

模块(module)是方法的集合,相当于内部函数的集合。Python提供了很多内置模块。模块在使用前需要用import语句导入,模块的文件类型是py。

包(package)是一个总目录,包目录下为首的一个文件便是_init_.py,用于定义初始状态。

函数

在编程中会发现某些代码需要重复编写,既降低开发效率,又不易维护。如果代码只写一次,而可以多次使用,可大大提高编程效率和代码的可靠性。

Python中的函数机制可以达到上述目的。函数是一个被指定名称的代码块,在任何地方要使用该代码块时,只要提供函数的名称即可,也称为函数调用。

1.1函数的定义与使用

1定义函数

定义函数使用关键字def,后接函数名和位于圆括号()中的可选参数列表。函数体位于冒号之后的数行,并且具有相同的缩进。

一般格式如下:

def 函数名(参数列表):
    ‘函数文档字符串’
    函数体
    return [表达式|值]

注意:

  1. 函数代码块以def关键词开头,后接函数标识符名称和圆括号(),不用指定返回值的类型;
  2. 任何传入的参数必须放在圆括号之内。圆括号之间的函数参数可以是0个、1个或者多个,且都不用指定数据类型;
  3. 函数的第一行语句可选,可以使用文档字符串来对函数进行说明;
  4. 函数体是功能实现代码,必须缩进;
  5. return语句是可选的,可以在函数体内的任何地方出现,表示函数调用执行到此结束。如果没有return语句,自动返回NONE;如果有return语句,但是return后没有表达式或值,也返回NONE;
  6. 函数名称必须唯一。如果以同样的名字再定义一个函数,将覆盖之前的函数定义,并且不会报错。

2使用函数

函数定义不会改变程序的执行流程。函数定义中的语句不是立即执行的,而是等到函数被程序调用时才依次执行。

在一个程序A中调用函数B时,会暂停当前程序A的运行,程序执行流程会跳到函数B中;执行完函数B中所有语句后,再跳转到程序A中的函数调用语句位置,继续执行程序A中的其余代码。

函数调用格式为:    函数名(参数列表)

注意:

  1. 函数名必须是已经定义好的函数;
  2. 参数列表中参数的个数和类型与函数定义中的参数一般要一一对应。另外,Python支持其他参数传递方式,详见下一节;
  3. 在函数定义之前调用函数会发生错误。

编写一个Python程序,输出心形图形。(定义一个输出心形图形的函数,再调用函数输出图形)

程序如下:

def love():
    '''
    利用join函数连接心形字符串,join之前的\n是要连接的字符串之间的分隔符,用于控制换行
    用x和y来控制行数和列数,用公式控制输出文字还是空格
    (x-y)%10控制从哪个位置的字符开始输出
    '''
    print('\n'.join([' '.join([('lovepython'[(x-y)%10]
    if ((x*0.05)**2+(y*0.1)**2-1)**3-(x*0.05)**2*(y*0.1)**3 <=0 else ' ')
    for x in range(-25,25)]) for y in range(25,-25,-1)]))
love()

运行结果如下:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
                                                               
                                                                                                   
                                                                                                   
                      p y t h o n l o v                       p y t h o n l o v                    
              o v e p y t h o n l o v e p y t h       o v e p y t h o n l o v e p y t h            
          l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l        
        l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v      
      l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p    
      o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y    
      v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t    
      e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h    
      p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o    
      y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n    
        h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n      
          n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n        
          l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n l        
              e p y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n            
                y t h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n              
                  h o n l o v e p y t h o n l o v e p y t h o n l o v e p y t h o n                
                      l o v e p y t h o n l o v e p y t h o n l o v e p y t h o                    
                          e p y t h o n l o v e p y t h o n l o v e p y t h                        
                              t h o n l o v e p y t h o n l o v e p y t                            
                                    l o v e p y t h o n l o v e p                                  
                                          p y t h o n l o v                                        
                                                o n l                                              
                                                  l                                                

Process finished with exit code 0

有点没看懂if语句的控制条件,大概是心形图形的函数表达式。。

1.2函数的参数

Python函数有两种类型的参数。一种是函数定义中出现的参数,称为形参;另一种是调用函数时传入的参数,称为实参。

Python中的任何东西都是对象,所以参数只支持引用传递的方式。通过名称绑定的机制,把实际参数的值和形式参数的名称绑定在一起,即形参和实参指向内存中同一个存储空间。

在Python函数中,实参向形参的传递方式有4种:按位置传递参数、按默认值传递参数、按关键字传递参数和可变参数传递。

1按位置传递参数

当实参按位置传递给形参时,函数调用语句中的实参和函数头中的形参按顺序一一对应,即第一个实参传递给第一个形参,第二个实参传递给第二个形参,以此类推。如果实参是一个表达式,则先计算表达式的值,再把计算后的结果传递给形参。

如果实参指向的对象是不可变的,如数值、字符串或元组对象等,即使在函数中改变了形参的值,实参指向的对象也不会发生任何改变。

下面的程序展示了即使形参num在函数定义中发生了变化,在调用函数的程序中实参也没有发生变化,如下所示:

def sample1(num):
    num=num+2
    return num
num=25
print(sample1(num))
print(num)

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
27
25

Process finished with exit code 0

下面的程序展示了可变列表对象作为形参,形参mylist在函数定义中发生了变化,且在函数调用之后,调用函数的程序中实参mylist也同样发生了变化,如下所示:

def sample2(mylist):
    mylist.append([1,2,3,4])
    print('函数内mylist:',mylist)
    return
mylist=['a','b','c']
sample2(mylist)
print('函数外mylist:',mylist)

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
函数内mylist: ['a', 'b', 'c', [1, 2, 3, 4]]
函数外mylist: ['a', 'b', 'c', [1, 2, 3, 4]]

Process finished with exit code 0

 

2按默认值传递参数

Python中的函数也可以给一个或多个参数(包括全部形参)指定默认值。如果在函数调用过程中省略了相应的实参,这些形参就是用默认值。这样,在调用时可以选择性地省略该参数。

定义默认值函数的格式如下:

def 函数名(形参1,形参2,形参3=值1,形参4=值2):
    函数体

注意:

  1. 函数调用时,除具有默认值的形参外,其他形参必须有对应的实参传递值;
  2. 函数定义时,没有默认值的参数必须放在有默认值的参数的前面,否则会报错。例如,下述函数头定义不合法:                                             def func(par1,par2=value1,par3):
  3. 当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面,变化小的参数就可以使用默认参数;
  4. 默认参数必须指向不变对象,否则再连续多次调用时,上次调用后计算的结果会保留,从而影响下次调用。

下面的程序给用户一次回答的机会;也可以不用修改函数,而是改变调用方式,给用户多次回答的机会:

def askandanswer(question,answer,tries=1):
    n=0
    while n<tries:
        n=n+1
        ans=input(question)
        if ans==answer:
            print('恭喜你,答对了!')
            break
    else:
        print('你的机会全部用完,回答错误!')
        print('正确答案是:',answer)
def main():
    question='python是什么类型的语言?'
    answer='面向对象'
    print('第一次函数调用结果:')
    askandanswer(question,answer)
    print('第二次函数调用结果:')
    askandanswer(question,answer,3)
main()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
第一次函数调用结果:
python是什么类型的语言?面向对象
恭喜你,答对了!
第二次函数调用结果:
python是什么类型的语言?过程
python是什么类型的语言?函数
python是什么类型的语言?强类型
你的机会全部用完,回答错误!
正确答案是: 面向对象

Process finished with exit code 0

 

3按关键字传递参数

函数也可以使用形参的名字,以“形参名=值”的形式来进行函数调用。这种形参和实参之间传值的方式称为关键字传值。由于在调用中通过形参名明确地指出了对应关系,所以不限制参数传递的顺序。

按照年复合利率计算储蓄账户余额,计算公式如下:账户余额=本金*(1+年利率)**到期年数,下面的程序展示了上述几种参数传递的用法:

def balance(capital,years,rate=.02):
    bal=capital*((1+rate)**years)
    return bal
def main():
    #按位置传递前两个实参,其中,最后一个参数使用默认值
    print('{0:,.4f}元'.format(balance(10000,3)))
    #按位置传递3个实参
    print('{0:,.4f}元'.format(balance(10000, 3,0.4)))
    #按关键字传递参数,可与按位置传递参数混合使用
    print('{0:,.4f}元'.format(balance(10000, rate=0.35,years=5)))
    print('{0:,.4f}元'.format(balance(years=4,capital=30000)))
    print('{0:,.4f}元'.format(balance(rate=0.47,capital=30000,years=3)))
main()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
10,612.0800元
27,440.0000元
44,840.3344元
32,472.9648元
95,295.6900元

Process finished with exit code 0

 

4可变参数传递

到目前为止,在定义一个函数的时候,必须预先确定该函数需要多少个参数(或者说可以接收多少个参数)。一般情况下都是可以确定的。但是也有在定义函数的时候,不能确定参数个数的情况。Python中带*的参数就是用来接收可变数量参数的。

在形参前加一个星号(*)或者两个星号(**)来指定函数可以接收任意数量的实参。

典型的定义可变参数的函数格式为: 

def 函数名(形参1,形参2,…,形参n,*tupleArg,**dictArg):
    函数体

注意:

  1. 不带*的参数是普通形参。调用时,实参可选择按位置传递、按默认值传递或按关键字传递的方式使用;
  2. 形参tupleArg前面的*表示这是一个元组参数,默认值为();
  3. 形参dictArg前面的**表示这是一个字典参数(键值对参数),默认值为{};
  4. 可以把tupleArg和dictArg看成两个默认参数;
  5. 对于多余的非关键字参数,函数调用时放在元组参数tupleArg中;
  6. 对于多余的关键字参数,函数调用时放在字典参数dictArg中;
  7. 在实参的参数中,如果使用*元组参数或者**字典参数,这两种参数应该放在参数列表的最后,并且*元组参数位于**字典参数之前;
  8. 实参中的元组对象前面如果不带*,或者字典对象前面如果不带**,则作为普通的对象传递参数。

上面第5和6条中,非关键字参数是指没使用关键字传值方式的参数;关键字参数是指使用关键字传值方式的参数

可变参数程序实例:

def func(para1,para2='love python',*tupleArg,**dictArg):
    #输出普通形参的值
    print('para1=',para1)
    print('para2=',para2)
    #输出元组的所有元素
    for i,elem in enumerate(tupleArg):
        print('tupleArg(元组对象) %d-->%s' % (i,str(elem)))
    #输出字典的所有元素
    for key in dictArg:
        print('dictArg(字典对象) %s-->%s' %(key,dictArg[key]))
#主函数进行不同参数组合的函数调用
def main():
    #定义一个列表
    mylist=['苹果','桔子','菠萝','哈密瓜']
    #定义一个字典
    mydict={'name':'Tom','age':22}
    #第一次函数调用,只传递一个实参,其他形参使用默认值
    func('第一个实参')
    #输出分割界限
    print('*'*40)
    #第二次函数调用,传递3个实参,其中a是多余的关键字参数,存放在字典中
    func('第一个实参',para2='第二个实参',a=1)
    print('*'*40)
    #第三次函数调用,字典实参前没有加*,当做普通参数使用
    #相当于多余的非关键字参数,存放在元组中
    func(345,mylist,mydict)
    print('*'*40)
    #第四次函数调用,rt是多余的关键字参数,存放在字典中
    #mylist变量之前加*,传递给元组形参变量;mydict前加**,传递给字典形参变量
    func(345,rt=123,*mylist,**mydict)
main()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
para1= 第一个实参
para2= love python
****************************************
para1= 第一个实参
para2= 第二个实参
dictArg(字典对象) a-->1
****************************************
para1= 345
para2= ['苹果', '桔子', '菠萝', '哈密瓜']
tupleArg(元组对象) 0-->{'name': 'Tom', 'age': 22}
****************************************
para1= 345
para2= 苹果
tupleArg(元组对象) 0-->桔子
tupleArg(元组对象) 1-->菠萝
tupleArg(元组对象) 2-->哈密瓜
dictArg(字典对象) rt-->123
dictArg(字典对象) name-->Tom
dictArg(字典对象) age-->22

Process finished with exit code 0

第4次调用时para2为什么是‘苹果’呢???

编写一个Python程序,输出指定范围内的所有素数。

程序如下:

#定义有一个参数的函数prime,用于判断形参是否是素数
def prime(num):
    for i in range(2,num):
        if num%i==0:
            return 0
    return 1
print('请指定一个正整数的数据范围[a,b]')
a=int(input('请输入范围的下限:'))
b=int(input('请输入范围的上限:'))
if a<=0 or b<=0:
    print('数据输入有误!')
    exit(0)
if a>b:
    t=a
    a=b
    b=t
print('{},{}范围内的素数如下:'.format(a,b))
i=0
for num in range(a,b+1):
    if prime(num)==1:
        print('{},'.format(num),end=' ')
        i=i+1
        if i%10==0 and i!=0:
            print()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
请指定一个正整数的数据范围[a,b]
请输入范围的下限:25
请输入范围的上限:85
25,85范围内的素数如下:
29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 
71, 73, 79, 83, 
Process finished with exit code 0

 

1.3函数的返回值

函数的返回值个数可以是0个、1个或多个,返回值可以是数值、字符串、布尔型、列表型、元组型等任何类型的对象。函数也可以没有返回值,即没有return语句。

Python支持一个函数返回多个结果。返回多个值的return语句格式如下:

return [表达式1|值1],[表达式2|值2],…,[表达式n|值n]

注意:

  1. 实际上返回的多个值是一个元组;
  2. 返回一个元组可以省略括号;
  3. 可以使用多个变量同时接收一个元组,按位置赋给对应的值。

计算球的表面积和体积。程序如下:

import math
#定义无参、无返回值函数main(),完成输入、计算函数调用、计算结果输出操作
def main():
    radius=float(input('请输入球的半径:'))
    #同时用两个变量接收compute()函数的两个返回结果
    area,vol=compute(radius)
    print('半径为{}的球的表面积为{:.3f},体积是{:.3f}'.format(radius,area,vol))
#定义compute()函数,具有1个形参,两个返回结果
def compute(radius):
    mj=4*math.pi*radius**2
    tj=4/3*math.pi*radius**3
    return mj,tj      #在return语句中用逗号分隔多个返回结果
main()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
请输入球的半径:3.5
半径为3.5的球的表面积为153.938,体积是179.594

Process finished with exit code 0

前面说在函数定义之前调用函数会发生错误。刚开始总感觉是在compute()函数定义之前调用了该函数,感觉好像是错的。又仔细想想好像不是。该程序是先定义了两个函数main()和compute(),然后调用main(),main()中又调用了compute()函数,所以两个函数都是先定义再调用的,没毛病。main()函数在前,compute()函数在后,并且main()中调用了compute()函数,但是函数定义中的语句不是立即执行的,而是等到程序被调用时才被执行

编写一个Python程序,用户输入一系列要检索的关键字和文本,定义函数完成关键字出现的次数及出现位置的统计。最后,调用函数并输出结果。

思路:

设计一个函数完成关键字的相关统计功能。该函数具有两个形参,分别用来接收一段文字和若干关键字。对每个关键字都遍历整个字符串,统计出现的次数和出现的位置,用列表来存放当前关键字出现的位置,然后把关键字、出现次数和所在位置组合成一个列表项并添加到结果列表中。最后,把结果列表对象作为返回值返回。

在主函数中,首先输入要检索的文本和关键字,并通过split()函数转换成关键字列表,然后调用上面定义的函数;最后输出函数返回值。

程序如下:

#定义主函数,主要完成输入、函数调用、输出任务
def main():
    print('关键字次数与位置统计')
    str=input('请输入一段文字:')
    keywords=input('请输入若干关键字,用分号分隔:')
    #应用字符串的split()函数,把用分号隔开的字符串转换为字符串列表
    keys=keywords.split(';')
    print(keys)
    #调用函数完成统计操作,实参是一个字符串对象和一个字符串列表
    result=keycount(str,keys)
    #输出结果,形式为[[关键字,出现次数,[位置1,位置2,…],…]
    print(result)
#定义关键字检索统计函数,有两个形参
def keycount(str,keys):
    result=[]
    for item in keys:
        tmpstr=str
        count=0
        location=tmpstr.find(item)
        loc=[]
        length=0
        while location!=-1:
            count+=1                      #累计找到的次数
            loc.append(location+length)   #添加到位置列表对象中
            length+=location+len(item)    #计算下一次检索的起始位置
            tmpstr=tmpstr[length:]        #减掉已检索字符串之后余下的字符串
            location=tmpstr.find(item)    #继续在子串中检索
        #当前关键字统计完毕,统计结果以列表形式添加到结果列表中
        result.append([item,count,loc])
    return result                        #返回结果对象列表
main()                                    #调用main()函数开始执行

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
关键字次数与位置统计
请输入一段文字:The idea grew out of one client's need to monitor the temperature of an office building's air conditioning during one long hot summer in Portland. The office property manager wanted to be alerted before the temperature in offices became unbearable and the tenants complained. The application is not limited to only detecting temperature changes, but has been used with a variety of input devices such as proximity switches and flood sensors.
请输入若干关键字,用分号分隔:idea;temperature;office;The
['idea', 'temperature', 'office', 'The']
[['idea', 1, [4]], ['temperature', 3, [54, 207, 260]], ['office', 2, [72, 151]], ['The', 3, [0, 147, 273]]]

Process finished with exit code 0

感觉Python对字符串的处理蛮方便的,不像C那么麻烦。。

1.4变量作用域

在Python程序中创建、改变或者查找变量名都是在命名空间中完成。作用域就是命名空间。

Python中的变量名在第一次赋值时已经创建,并且必须赋值后才能使用。因变量名不需要声明,Python将一个变量名被赋值的位置关联为一个特定的命名空间。代码中变量赋值的位置决定了该变量的命名空间,即该变量的可见范围。

在Python中,模块(module)、类(class)以及函数(def、lambda)会引入新的作用域,而其他代码块(如if、try、for等)不会引入新的作用域,即定义在这些代码块之内的变量还是可以被外部访问的。

所有的变量根据作用域可归纳为4种:①L:local,局部作用域,即函数中定义的变量;②E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;③G:global,全局变量,就是模块级别定义的变量;④B:build-in,系统固定模块里面的变量,比如int,bytearray等。

1局部变量

赋值的变量名除非声明为全局变量,否则均为本地变量。如果需要在函数内部对模块文件顶层的变量名赋值,需要在函数内部通过global语句声明该变量。

注意:

  1. 一个在def内定义的变量名能够被def内的代码使用。不能在函数的外部引用这样的变量名;
  2. def之中的变量名与def之外的变量名并不冲突。一个在def之外被赋值的变量X,与在这个def之中赋值的变量X是完全不同的。

局部变量应用示例:

area='面积'                            #全局变量area
cir='体积'                             #全局变量cir
def jisuan(length,wid):
    area=length*wid                     #局部变量area,只在函数体内有效
    cir=2*(length+wid)                  #局部变量cir,只在函数体内有效
    print('函数内的area=',area)
    print('函数内的cir=',cir)
    return area,cir
print('函数调用结果:',jisuan(15,6))
print('函数外的area=',area)            #此处使用的是全局变量area
print('函数外的cir=',cir)

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
函数内的area= 90
函数内的cir= 42
函数调用结果: (90, 42)
函数外的area= 面积
函数外的cir= 体积

Process finished with exit code 0

 

2全局变量

每个模块都是一个全局作用域。因此,创建于模块文件顶层的变量具有全局作用域。对于外部访问,就成了一个模块对象的属性。全局作用域的作用范围仅限于单个文件。‘全局’指的是在一个文件的顶层的变量名,对于这个文件而言是全局的。

注意:

  1. 全局变量是位于模块内部的顶层的变量名;
  2. 全局变量如果是在函数内部赋值,必须经过声明;
  3. 全局变量在函数内部不经过声明也可以使用。

全局变量应用示例:

fruit=['苹果','梨']                   #全局变量
fruit1=['香蕉','菠萝']                #全局变量
fruit2=['桔子','橙子']                #全局变量
def f1():
    fruit.append('葡萄')              #列表的append()方法可改变外部全局变量的值
    print('函数内fruit:%s'%fruit)
    fruit1='浙江省'                   #重新赋值不可改变外部全局变量的值
    print('函数内fruit1:%s'%fruit1)
    global fruit2                     #如果需要重新给列表赋值,需要使用global定义全局变量
    fruit2='浙江省'
    print('函数内fruit2:%s'%fruit2)
f1()
print('函数外fruit:%s'%fruit)
print('函数外fruit1:%s'%fruit1)
print('函数外fruit2:%s'%fruit2)

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
函数内fruit:['苹果', '梨', '葡萄']
函数内fruit1:浙江省
函数内fruit2:浙江省
函数外fruit:['苹果', '梨', '葡萄']
函数外fruit1:['香蕉', '菠萝']
函数外fruit2:浙江省

Process finished with exit code 0

 

3变量名查找规则

Python的变量名解析机制也称为LEGB法则,具体说明如下:当在函数中使用未确定的变量名时,Python搜索4个作用域:①本地作用域(local);②上一层嵌套结构中def或者lambda的本地作用域(enclosing);③全局作用域(global);④内置作用域(build-in)。按照上述查找原则,在第一处找到的地方停止。如果没有找到,Python报错。

变量作用域示例:

x=int(10)                   #Python内置作用域B
y=2                         #当前模块中的全局变量G
def outfunction():
    outfx=2                 #外层作用域E
    def infunction():
        infx=3              #局部作用域L

感觉‘内置作用域’有点不太懂。。

1.5匿名函数

在临时需要使用一个函数且功能非常简单的情况下,可以使用Python提供的匿名函数定义来完成。

Python允许快速定义单行的不需要函数名字的最小函数,称为lambda()函数。它是从Lisp语言借用来的,可以用在任何需要函数的地方。

使用lambda()函数的优势如下:

  1. 使用lambda()可以省去定义函数的过程,让代码更加简洁;
  2. 对于一些抽象的、不会被再次使用的函数,使用lambda不需要考虑命名的问题;
  3. 使用lambda,在某些时候让代码更容易理解。

lambda()函数定义格式为:   lambda 参数1,参数2,…:表达式

功能:lambda是一个表达式,而不是一个语句。作为一个表达式,lambda返回一个值,把结果赋值给一个变量名。

注意:

  1. 在参数列表周围没有括号,而且忽略了return关键字(隐含存在,因为整个函数只有一行)。可以没有参数,也可以有一个或多个参数;
  2. 使用lambda()函数时,可以不把表达式的结果赋值给一个变量;
  3. 可以把匿名函数赋值给一个变量,再利用该变量来调用函数;
  4. 可以把匿名函数作为函数的返回值返回给调用者;
  5. 在lambda()中仅能封装有限的业务逻辑。lambda()函数的目的是方便编写简单函数,def则专注于处理更大、更复杂的业务;
  6. 如果在程序中大量使用lambda表达式,会造成程序的结构混乱。另外,如果lambda表达式过于复杂,将降低程序的可读性。

匿名函数示例程序:

t=lambda:1                    #不带参数的匿名函数,并把函数赋值给一个变量
print('t:{}'.format(t))       #t是一个匿名函数的引用
print('t():{}'.format(t()))   #通过变量名()方式调用匿名函数,得到函数的计算结果
#函数的返回值是一个匿名函数
def compute(a,b,c,x):
    return lambda :a*x**2+b*x+c
#直接输出返回结果是匿名函数的引用
print('compute(2,3,4,1.5):{}'.format(compute(2,3,4,1.5)))
#如果函数的返回值是一个匿名函数,表达式末尾必须加()
print('compute(2,3,4,1.5)():{}'.format(compute(2,3,4,1.5)()))
#定义带有默认值的匿名函数
c=lambda x,y=2:x**y
print('c(2):{}'.format(c(2)))        #只传递一个参数,第二个使用默认值
print('c(2,5):{}'.format(c(2,5)))    #传递两个参数,不使用默认值
#判断字符串是否以某个字母开头
print((lambda x:x.startswith('K'))('Knowledge'))

运行结果如下:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
t:<function <lambda> at 0x0000000493617B70>
t():1
compute(2,3,4,1.5):<function compute.<locals>.<lambda> at 0x0000000493617BF8>
compute(2,3,4,1.5)():13.0
c(2):4
c(2,5):32
True

Process finished with exit code 0

编写一个Python程序,实现两个整数的如下位运算:左移运算(<<)、右移运算(>>)、按位与(&)、按位或(|)、按位异或(^)以及按位翻转(~)。

思路:定义一个函数main(),实现所要求的功能。首先设计菜单,让用户选择位运算符,并进行输入检查;然后,根据运算符操作数个数,要求用户输入一个或两个整数;接着,使用匿名函数实现多分支选择运算;最后,输出匿名函数运算结果。

程序如下:

#定义main()函数
def main():
    while True:
        print('请选择运算符:')
        print('1.左移运算(<<)',end=' ')
        print('2.右移运算(>>)',end=' ')
        print('3.按位与(&)',end=' ')
        print('4.按位或(|)')
        print('5.按位异或(^)',end=' ')
        print('6.按位翻转(~)',end=' ')
        print('0.退出')
        choice=int(input('请选择(0-6):'))
        if choice<0 or choice >6:                #对用户的选择进行有效性检查
            print('输入错误!')
            continue                            #退出当前循环,开始下一轮新的循环
        if choice==0:
            break                               #如果用户输入0,退出整个循环结构
        if choice==6:                           #按位取反是一元运算符,因此只输入一个操作数
            num1=int(input('请输入一个数:'))
            num2=0
        else:                                   #其他运算符是二元运算符,需要输入两个操作数
            print('请输入两个整数:')
            num1=int(input('请输入第一个数:'))
            num2=int(input('请输入第二个数:'))
            #通过匿名函数构造多分支选择结构
            result={
                1:lambda x,y:x<<y,
                2:lambda x,y:x>>y,
                3:lambda x,y:x&y,
                4:lambda x,y:x|y,
                5:lambda x,y:x^y,
                6:lambda x,y:~x,
            }[choice](num1,num2)
            print('result=',result)
main()

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
请选择运算符:
1.左移运算(<<) 2.右移运算(>>) 3.按位与(&) 4.按位或(|)
5.按位异或(^) 6.按位翻转(~) 0.退出
请选择(0-6):1
请输入两个整数:
请输入第一个数:5
请输入第二个数:2
result= 20
请选择运算符:
1.左移运算(<<) 2.右移运算(>>) 3.按位与(&) 4.按位或(|)
5.按位异或(^) 6.按位翻转(~) 0.退出
请选择(0-6):2
请输入两个整数:
请输入第一个数:64
请输入第二个数:2
result= 16
请选择运算符:
1.左移运算(<<) 2.右移运算(>>) 3.按位与(&) 4.按位或(|)
5.按位异或(^) 6.按位翻转(~) 0.退出
请选择(0-6):0

Process finished with exit code 0

 

1.6高阶函数

高阶函数是Python语言的一大特色。在Python中,函数可以赋值给一个变量名,并且可以通过这个变量名来调用函数。函数的参数可以是任何变量类型,因此这个变量也可以作为函数的参数。

如果一个函数可以接收另一个函数作为参数,或者把函数作为返回值返回,这种函数就称为高阶函数。

计算两个数的公因子的程序如下:

#定义一个匿名函数并赋值给变量f,用来计算数x的所有因子
f=lambda x:[i for i in range(1,x+1) if x%i==0]
#定义计算两个整数的公因子的函数,最后一个参数是一个函数
def commonFactor(n1,n2,f):
    return set(f(n1))&set(f(n2))
print(f(56))                     #输出某个整数的所有因子
print(f(64))
print(commonFactor(56,64,f))     #调用函数并输出两个数的公因子

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
[1, 2, 4, 7, 8, 14, 28, 56]
[1, 2, 4, 8, 16, 32, 64]
{8, 1, 2, 4}

Process finished with exit code 0

Python中还内置了一些高阶函数,如map(),filter(),reduce(),sort()函数等。这些函数的共同特点是至少需要2个参数。其中,第一个参数是一个函数变量f();第二个参数是一个序列对象,可以是列表对象、元组对象或字典对象。这4个函数的功能如下表所示:

Python内置高阶函数
函数格式功能说明
map(f,list)把函数f依次作用在list的每个元素上,得到一个新的list并返回
filter(f,list)对每个元素进行判断,返回True或False。filter()根据结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list
reduce(f,list)传入的函数f必须接收2个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果list
sorted(iterable,key=None,reverse=False)

key接收一个函数,该函数只接收一个元素,默认为None。reverse是一个布尔值,如果设置为True,列表元素将被倒序排列。默认值为False,正序排列。返回值为list中元素按f函数排列的list

内置高阶函数应用示例:

from functools import reduce
#利用map函数,把单词的第一个字母大写,其余字母小写
print('*******map()函数应用实例*******')
sname=['tom','mIKe','JHON','Aim']
f=lambda s:s[:1].upper()+s[1:].lower()  #字符串切片,分别执行大小写操作
print(list(map(f,sname)))
#利用filter()函数,统计50以内的所有素数
'''
计算素数的一个方法是埃氏筛法,简述如下:
(1)首先,列出从2开始的所有的自然数,构造一个序列:
2,3,4,5,6,7,8,9,10……
(2)取序列的第一个数2,它一定是素数。然后,用2把序列的2的倍数筛掉
3,5,7,9……
(3)取新序列的第一个数3,它一定是素数。然后,用3把序列的3的倍数筛掉
5,7……
(4)不断筛下去,得到所有的素数
算法代码如下:
'''
print('*******filter()函数应用实例*******')
nums=range(2,50)
for i in nums:
    f=lambda x:x==i or x%i
    nums=list(filter(f,nums))
print(list(nums))
print('*******reduce()函数应用实例*******')
print('字符串形式的浮点数转换为数值型的浮点数')
#f1把字母转换为数字
f1=lambda s:{'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}[s]
f2=lambda x,y:x*10+y
def strToFloat(s):
    s1,s2=s.split('.')
    return reduce(f2,map(f1,s1+s2))/10**len(s2) 
print('字符串转为浮点数(\'356.4896\')=',strToFloat('356.4896'))
print('*******sort()函数应用实例*******')
print('按英文字母排序,且不区分大小写')
def myCompare(str1):
    return str1.lower()
fruit=['Apple','orange','Banana','Pineapple','pear','Berry']
print('排序后的结果为:')
print(sorted(fruit,key=myCompare,reverse=True))

运行结果:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
*******map()函数应用实例*******
['Tom', 'Mike', 'Jhon', 'Aim']
*******filter()函数应用实例*******
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
*******reduce()函数应用实例*******
字符串形式的浮点数转换为数值型的浮点数
字符串转为浮点数('356.4896')= 356.4896
*******sort()函数应用实例*******
按英文字母排序,且不区分大小写
排序后的结果为:
['Pineapple', 'pear', 'orange', 'Berry', 'Banana', 'Apple']

Process finished with exit code 0

这也太巧妙了叭,,,

 

1.7函数的嵌套

由于函数是用def语句定义的,凡是其他语句可以出现的地方,def语句同样可以出现。因此,Python允许在定义函数的时候,其函数体内又包含另外一个函数的完整定义,称为函数的嵌套定义。

定义在其他函数内的函数叫作内部函数,内部函数所在的函数叫作外部函数。如果是多层嵌套,除了最外层和最内层的函数之外,其他函数既是外部函数,又是内部函数。

注意:

  1. 一个函数定义在另一个函数的里面,外部函数返回的是内部函数,即返回值是一个函数;
  2. 每次调用外部函数,内部函数都会被重新绑定;
  3. 内部函数定义的变量只在内部函数体内有效,包括其嵌套的内部函数,但是外部函数不能引用内部函数中定义的变量;
  4. 内部函数可以引用外部函数中定义的变量,但是不能对不可变的变量重新赋值;
  5. 执行具有多层函数定义的嵌套函数时,Python遇到该def语句并不会立即执行其语句体中的代码,即跳过内嵌的函数定义及函数体,只有遇到这些函数的调用时,对应def语句的函数体才会被执行;
  6. 使用嵌套函数的优势是可供其他以函数为参数的函数使用。

嵌套函数定义与使用示例:

def aFunc(a):         #嵌套函数定义,用来计算一个数的幂
    def bFunc(b):
        return a**b  #内层函数可以访问外层函数的变量a
    return bFunc
#调用嵌套函数的方式1
f1=aFunc(5)          #把返回的函数赋值给变量f1
#由于f1是指向函数的变量,因此要通过函数调用的方式来使用,并向内部函数传递参数
print('函数运行结果f1=',f1(4))
#调用嵌套函数的方式2,直接用一条语句得到调用结果,多个括号代表了嵌套的层次
f2=aFunc(5)(4)
print('函数运行结果f2=',f2)

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
函数运行结果f1= 625
函数运行结果f2= 625

Process finished with exit code 0

编写一个Python程序,用户输入两个矩阵,计算两个矩阵的乘积。假定输入的矩阵满足如下条件:第一个矩阵的行数等于第二个矩阵的列数。

设计思路:定义一个函数main(),实现矩阵输入、矩阵相乘函数调用并输出结果。在main()函数中接收从键盘输入的字符串形式的矩阵,然后转换为嵌套的列表来表示矩阵,定义嵌套函数实现矩阵的相乘。

程序如下:

#定义main()函数,实现输入、函数调用及输出操作
def main():
    print('矩阵的输入格式为行之间用分号分隔,列之间用逗号分隔\n3行3列矩阵输入格式为1,2,3;3,4,5;5,6,7')
    str1=input('请输入第一个矩阵:')
    str2=input('请输入第二个矩阵:')
    #调用字符串转列表函数,转换成数值型的嵌套列表来表示矩阵
    matrix1=strToMatrix(str1)
    matrix2=strToMatrix(str2)
    print(matrix1)
    print(matrix2)
    print('矩阵相乘后的运算结果为:')
    matrix3=list(matrixMultiply(matrix1)(matrix2))
    print(matrix3)
#字符串转矩阵函数
def strToMatrix(ju):
    m1=list(ju.split(';'))
    matrix=[]
    for item in m1:
        #对于列表m1的每个元素,用逗号作为分隔符,得到子列表,并转换为整数
        tmp=[int(x) for x in item.split(',')]
        matrix.append(tmp)
    return matrix
#定义嵌套函数,完成矩阵的相乘运算
def matrixMultiply(matrix1):
    #内部函数,用于实现两个列表对应位置的元素相乘后得到的列表再相加
    def compute(list1,list2):
        return sum(list(map(lambda x:x[0]*x[1],zip(list1,list2))))
    #内部函数,按矩阵运算规则实现矩阵相乘
    def multiply(matrix2):
        #第二个矩阵进行转置
        transMatrix=list(map(list,zip(*matrix2)))
        #存放运算结果的矩阵初始化为空列表
        result=[]
        for item1 in matrix1:
            #行元素结果列表初始化为空列表
            row=[]
            for item2 in transMatrix:
                #计算相应位置元素的结果,并添加到列表row中
                row.append(compute(item1,item2))
            #一行结果运算完毕,添加到列表result中
            result.append(row)
        #返回运算结果
        return result
    #返回嵌套函数
    return multiply
#调用main()函数,执行任务
main()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
矩阵的输入格式为行之间用分号分隔,列之间用逗号分隔
3行3列矩阵输入格式为1,2,3;3,4,5;5,6,7
请输入第一个矩阵:2,3,4;5,6,7;3,5,9
请输入第二个矩阵:1,2,5;3,6,3;4,5,2
[[2, 3, 4], [5, 6, 7], [3, 5, 9]]
[[1, 2, 5], [3, 6, 3], [4, 5, 2]]
矩阵相乘后的运算结果为:
[[27, 42, 27], [51, 81, 57], [54, 81, 48]]

Process finished with exit code 0

 

1.8递归函数

1.递归的概念

递归就是子程序(或函数)直接调用自己,或通过一系列调用语句间接调用自己。它是一种描述问题和解决问题的基本方法。递归通常用来解决结构相似的问题。结构相似是指构成原问题的子问题与原问题在结构上相似,可以用类似的方法解决。

构成递归的必备条件有:

  1. 子问题与原问题是同样的问题,但是更简单;
  2. 不能无限制地调用本身,必须有一个化为非递归处理的出口。

以下3种情况常用到递归方法:

  1. 定义本身是递归的,如阶乘的计算;
  2. 数据结构是递归的,如链表、树等;
  3. 问题的解法是递归的,如汉诺塔问题。

2.递归函数定义

典型的递归函数定义格式如下:

def 递归函数名(参数表):
    if 递归出口条件:
        return 返回值
    else:
        return 递归函数名(实参表)

注意:

  1. 递归函数的优点是定义简单,逻辑清晰;
  2. 递归函数必须有出口;
  3. 使用递归函数,需要注意防止栈溢出;
  4. 针对尾递归优化的语言,可以通过尾递归防止栈溢出,但是Python没有做尾递归;
  5. Python3默认递归的深度不能超过100层。

编写一个Python程序,在一个有序列表中用二分法查找某个元素。

设计思路:定义一个递归函数binarySearch(),找到中间位置的关键字进行比较。若等于,则找到;若小于中间位置的关键字,用列表的左半边进行递归查找,否则用列表的右半边进行递归查找;若起始位置大于终止位置,查找失败。

程序如下:

#定义main()函数,实现输入、函数调用、输出操作
def main():
    list1=[2,12,16,18,20,38,40,55,59,60,70,72,75,80,83,88,90,92,99]
    print('二分查找算法测试:')
    #要查找的关键字
    key1=55
    key2=84
    #调用二分查找函数进行查找,起始位置为0,终止位置是最后一个
    result1=binarySearch(list1,key1,0,len(list1))
    result2=binarySearch(list1,key2,0,len(list1))
    #输出查找结果
    if result1!=-1:
        print('{}成功找到,位置为{}'.format(key1,result1))
    else:
        print('{}未找到'.format(key1))
    if result2!=-1:
        print('{}成功找到,位置为{}'.format(key2,result2))
    else:
        print('{}未找到'.format(key2))
#定义二分查找递归函数,4个参数分别表示列表对象、查找关键字、起始位置、终止位置
def binarySearch(list1,key,start,end):
    if list1==[] or end==-1:        #数据有效性检查
        return -1
    if start>end:                   #关键字不存在情况下的递归出口
        return -1
    mid=start+int((end-start)/2)    #计算列表的中间位置
    #如果中间位置的元素与查找关键字相同,则找到,退出递归
    if key==list1[mid]:
        return mid
    #若关键字小于中间位置的关键字,用列表的左半边进行递归查找
    elif key<list1[mid]:
        return binarySearch(list1,key,start,mid-1)
    #否则用列表的右半边进行递归查找
    else:
        return binarySearch(list1,key,mid+1,end)
main()

运行结果为:

E:\PycharmProjects\PythonTest\venv\Scripts\python.exe E:/PycharmProjects/PythonTest/ssss.py
二分查找算法测试:
55成功找到,位置为7
84未找到

Process finished with exit code 0

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值