欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python实训(2)--函数与模块化编程

程序员文章站 2022-04-19 19:37:37
...

讲python中的函数——与C/C++不同点会在这里着重讲解一下,共通点则简介。

定义一个python函数:

def myFunc(a,b,c) :

函数无需定义返回值类型,参数也无需定义数据类型,因为解释型语言可以在运行的时候动态决定其数据类型。
所以如果要确保函数只对某些数据类型的变量进行处理,则要在函数一开始检查是否是需要的那些数据类型:

def my_abs(x):
    if not isinstance(x, (int, float)):
        raise TypeError('bad operand type') #这两句话就是检查
    if x >= 0:
        return x
    else:
        return -x

所谓返回多个值只是返回多个值组成的tuple,这很好理解。

默认参数:

默认参数的定义使用基本与C/C++一致,只不过可以在调用函数时,通过默认参数的名称可以指定某个默认参数的值,而不需要按照顺序指定默认参数的值。

注意点:

如果某个参数使用了默认值,而在函数中对这个参数进行了改变,那么在下一次调用此函数时会使用被改变了的值作为默认值。
要解决这个问题很简单,可以学C/C++的内部原理,在函数一开始给默认参数赋值:

def add_end(L=None):
    if L is None: #这样在调用函数不指定默认参数L时,先赋值为[]
        L = []
    L.append('END')
    return L

可变参数:

在python中想要传入数量不定的若干参数,可以定义可变参数,只要在定义函数时参数名前加*号

def calc(*numbers):
  	  sum = 0
  	  for n in numbers:
  	      sum = sum + n * n
  	  return sum

这样会自动把传进来的若干参数组装成名为number的一个tuple
同样,调用时手动输入若干参数也很累人,可以对某个tuple或list的参数前加*号,表示把tuple或list里面所有值输入函数。

注意点:

在和普通的必填参数同用时,先填写必填参数,并指定所有的默认参数,这里给出一个可能因此犯错的案例,分析其运行原理:
首先在定义时不会有任何问题,即使是三种参数都用上

def sum(a,b=100,*c):
    ans=a+b
    for i in c:
        ans+=i
    return ans

但是当调用时,我想让b保持默认值:

c=[1,1,1]
print(sum(1,*c))

运行如下:
Python实训(2)--函数与模块化编程
可见,答案不是我们想的104 而是4。其实原因在于
调用函数时使用的*c,本质上是把c这个列表中所有的参数打散,一个一个写入函数的参数列表
本例中,相当于调用了:

print(sum(1,1,1,1))

显而易见会覆盖默认参数,甚至如果不指定a的值,可以想象答案是3

关键字参数:

定义时名字前加两个*号,其本质是构造一个字典(可变参数是tuple):

def findScore(name,subjectName,**dic):
    print(name,subjectName,dic[subjectName])

调用函数时带入的参数当为键值对,或者类似地把另一个字典打散输入

Mydic={'语文':59,'数学':100}
findScore('CakeCN','语文',**Mydic)

Python实训(2)--函数与模块化编程

命名关键字:

关键字参数,对于传入的参数名无法限制。如果想对参数名有限制,就用到了命名关键字参数

但是我个人对命名关键字参数不够上心,让函数定义和调用都变得麻烦了。
然而有意见归有意见,用还是要会用的:

def findScore(name,subjectName,*,Chinese,Math):
    if subjectName=='Chinese':
        print(name,subjectName,Chinese)
    else:
        print(name,subjectName,Math)	

*号后面就指定哪些参数名是需要指定的,调用时这样写:

findScore('CakeCN','Chinese',Chinese=59,Math=60)

确实麻烦但也好在能用字典打散赋值

Mydic={'Chinese':59,'Math':100}
findScore('CakeCN','Chinese',**Mydic)	

Python实训(2)--函数与模块化编程

递归:

做好边界条件限制,然后自己调用自己,与C/C++一致,不多说。

匿名函数:

也很好理解,就是把定义一个函数的一堆话总结成一句话:
Python实训(2)--函数与模块化编程
这里f(x)和下面的匿名函数等价

高阶函数:

本质是自己给定一个函数,一系列参数,让系统去调用若干次我给定的函数,其中用到我这一系列参数。
先看Python内置的map/reduce两个高阶函数。

map():

用法是指定一个函数、指定一个list,会帮你将这个函数作用到列表每一个list的元素上并形成一个新list:
Python实训(2)--函数与模块化编程
这里,如果想然让这个f为多参数函数,需要自行利用一些第三方包

reduce():

用法是指定一个函数、一个list,此函数必须是2个参数:
一句话理解:

reduce(f, [x1, x2, x3, x4]) == f(f(f(x1, x2), x3), x4)

Python实训(2)--函数与模块化编程

fitler():

另一个内置的高阶函数,类似与map()的用法,指定一个函数和一个list。只不过是更具指定的函数返回true还是false决定保留还是丢弃该元素
一个例子理解:

Python实训(2)--函数与模块化编程

sorted():

要求指定一个list,再更具后面的一些默认参数的指定状态来讲list排序:
Python实训(2)--函数与模块化编程
多关键字排序,或想对元素为list等的列表排序,则需要自己定义key的写法,当返回值为元组时表示按元组顺序从前到后优先级递减。
一个很好的例子把我上面两个情况都用上了:

lis = ['a1b', 'a2a', 'a3b', 'b5d', 'a4f', 'c3f', 'b3f', 'a6f', 'c1f', 'a1a'] 
print(sorted(lis, key=lambda x: (x[2], x[1], x[0])))
结果为:['a1a', 'a2a', 'a1b', 'a3b', 'b5d', 'c1f', 'b3f', 'c3f', 'a4f', 'a6f']

函数是一个实例对象:

可以将函数作为返回值,再在之后决定要不要调用。(联想回调函数,但不一样)

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

比如这个求和函数,我们用F1,F2记录不同的求和状态:

F1=lazy_sum(1,2,3)
F2=lazy_sum(4,5,6)  #确定了求和的参数,但是没有调用sum()
print(F1(),F2())		#调用相应的sum()

Python实训(2)--函数与模块化编程
(所以要多此一举的话直接套娃:print(lazy_sum(1,2,3)()) #不许不许套娃!)
现在应该能理解什么叫“函数也是一个对象”

闭包:

我觉得我的水平很低不足以讲清闭包这个概念,在这里挪用一下*,我念了一遍感觉还能意会。
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了*变量的函数。这个被引用的*变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

装饰器:

不修改函数本身又能动态添加一些处理,比如输入日志,可以用装饰器:
本质上,是一个返回函数的高阶函数,这个高阶函数要求 给定返回函数的参数调用
看一下使用就懂了:

def log(func):	#当调用func这个函数时输出日志
    def wrapper(*args, **kw):
        print('call %s():' % func.__name__)
        return func(*args, **kw)	//组装func() 用来调用
    return wrapper

Python实训(2)--函数与模块化编程
可见实际上就是通过log调用一些其他代码,然后重新“组装”一次func()

最后一点:多多利用第三方模块

Import xxx

Python的精华 好好学好好用!