11.函数
接下来开始学习函数,函数这个知识点很重要,需要认真学习
先来了解下什么是函数?
函数是组织好的,可重复使用的,用来实现单一或相关功能的代码块。
如何使用函数
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率,Python提供了许多内建函数,比如print()。
但也可以自己创建函数,这被叫做用户自定义函数。
定义函数
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()
def关键词必须后跟函数名称和带括号的形式参数列表
构成函数体的语句从下一行开始,并且必须缩进
函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明
return语句返回一个函数的值。 return没有表达式参数返回None。从函数的末尾掉落也会返回None
现在以Fibonacci数作为例子:
def fib(n): # 定义一个函数名称,用来计算fibonacci数
a,b = 1,1 # 赋值
for i in range(n):
print(a,end=' ')
a,b = b,a+b
print()调用函数
已经知道了如何定义函数,那么该如何调用这个函数,接下来继续以上面的那个例子作为示例,进行讲解
看,这样就调用了,是不是很简单
运行结果:
函数引用
运行结果:
函数的参数
现在已经都知道了如何定义函数和调用函数了,接下来,讲解一下函数里面的参数,这一点很重要,要认真学喔
形参和实参
参数从调用的角度来说,分为形式参数和实际参数。
形参指的是函数创建和定义过程中小括号里的参数,而实参则指的是函数在被调用的过程中传递进来的参数。
下面举个小例子
在person(name)中name是形参,因为这只是代表一个位置,一个变量名而已;而person("angle")中传递的字符串"angle"是实参,因为这是一个具体的内容,是赋值到变量中的值
关键字参数
由于在调用函数的时候,有时候可能会很粗心不小心把参数位置的顺序给弄错了,从而导致函数无法按照预期实现要求。
因此关键字参数是为了解决这个问题而出现的,有了这个就可以很简单的解决这个潜在的问题了。
举个例子
运行结果:
从结果可以看到关键字参数是很有用的,因为参数的顺序一不小心弄错,会导致程序无法完成预期的结果的
默认参数
默认参数可以在参数定义的过程中,为形参进行赋值,当函数调用的时候,不传递实参,则默认使用形参的初始值代替。
举个小栗子
是不是很简单,那么继续往下学习
收集参数
什么是收集参数?
在定义函数的时候,不知道这个函数有多少个参数,因为在工作开发的时候,需要根据实际情况添加和删除参数的,这就造成了参数的不确定,由于添加和删除参数工作有点繁琐,所以这时才有了收集参数,收集参数是为了解决这个问题而出现的,收集参数两种,一种是将参数打包元组,一种是以字典形式打包
args
先来讲下第一种,只需要在参数前面加上一个星号(*)就可以了
运行结果
参数在传过去的时候,会进行打包成元组传递过去
如果想要单独额外指定参数,可以使用关键字参数
运行结果
星号其实既可以打包又可以解包。什么是解包?
举个小栗子
将列表a传入test参数的收集参数*args中
运行结果
那么调用test(a)时便会出错,此时需要在a前面加上个星号(*)表示实参需要解包后才能使用
运行结果
kwargs
另一种收集方式,用两个星号(**)表示
运行结果
慢慢来解析一下是什么意思?
函数文档
在程序的时候,有时候为了使代码可读性更高,有时候会在程序旁边进行注释,可是这样影响python代码的美观,这时就可以使用函数文档。
举个例子
运行结果
这样做还有个好处,这样可以不用打开源码慢慢去找文档注释,这时可以通过特殊属性
运行结果
在不确定函数的用法的时候,可以使用help()函数来查看函数的文档
运行结果
返回函数
python函数的返回值,可以返回一个值,也能够返回多个值
单个值
运行结果
多个值
运行结果
再返回多个值得时候,会打包成元组返回
递归
递归就是不停的调用自己,也就是不停重复做某一个事情
举个小栗子
怎么来理解这个函数了
这里假设输入3

运行结果
匿名函数lambda
Lambda表达式(有时称为lambda表单)用于创建匿名函数
如何使用
先来看看语法格式,然后看下例子
语法
实例
等价于
这样感觉是不是很繁琐,还是感觉lambda好用吧
在举一下其他的例子
比如前面讲过的列表排序
列表排序
内嵌函数和闭包
讲解了什么是函数,如何使用,现在来学习下什么是内嵌函数和闭包
内嵌函数
内嵌函数其实就是一个函数在其内部在套一个函数,和嵌套循环一样,又可以叫做内部函数
示例:
运行结果:
是不是很简单,但是这个只能调用outFunc()函数,而inFunc()函数只能在outFunc()函数中调用,而在outFunc()函数外调用,会进行报错,因为inFunc()函数的作用域只在outFunc()函数内才生效
运行结果:
闭包
python中的闭包从表现形式上定义为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包。
举个小栗子
运行结果:
可以将
缩写为
这两者之间是等价的
运行结果
装饰器
什么是装饰器?
装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。
装饰器有什么用?
抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能,也称之为扩展功能。
先看一个简单的例子:
现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码
可是如果函数 bar()、bar2() 也有类似的需求,怎么做?再写一个 logging 在 bar 函数里?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个新的函数:专门处理日志 ,日志处理完之后再执行真正的业务代码
这样做逻辑上是没问题的,功能是实现了,但是调用的时候不再是调用真正的业务逻辑 foo 函数,而是换成了 use_logging 函数,这就破坏了原有的代码结构, 现在不得不每次都要把原来的那个 foo 函数作为参数传递给 use_logging 函数,那么有没有更好的方式的呢?当然有,答案就是装饰器。
简单装饰器
use_logging 就是一个装饰器,它一个普通的函数,它把执行真正业务逻辑的函数 func 包裹在其中,看起来像 foo 被 use_logging 装饰了一样,use_logging 返回的也是一个函数,这个函数的名字叫 wrapper。在这个例子中,函数进入和退出时 ,被称为一个横切面,这种编程方式被称为面向切面的编程。
@语法糖
@符号是装饰器的语法糖
借鉴维基百科的看下什么是语法糖?
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·蘭丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。 语法糖让程序更加简洁,有更高的可读性
有了@,就可以省去foo = use_logging(foo)这一句了,能够直接调用foo()得到想要的答案,并且提高了程序的可重复利用性,并增加了程序的可读性
如何传参
传入一个参数
传入多个参数
带参数的装饰器
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。
上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。
等价于如下
类装饰器
没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
functools.wraps
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、name、参数列表,先看例子:
不难发现,函数 f 被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。
装饰器顺序
假如一个函数同时有多个装饰器如下
运行结果
执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器
高级函数
接下来讲下高级函数,这些都是python的内置函数
filter
简述
filter()函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器,产生可迭代对象,如果转换为列表,需要使用list()。
该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。是真的。 如果function为None,则返回true的项。
语法
参数
function:函数
iterable:可迭代的对象
返回值
返回一个可迭代对象
实例
sorted
简述
sorted()函数从iterable中的项返回一个新的排序列表
语法
参数
iterable -- 可迭代对象。
key指定一个参数的函数,用于从每个列表元素中提取比较键:key=str.lower。默认值为None (直接比较元素)。
reverse是一个布尔值。如果设置为True,则对列表元素进行排序,并且反转
返回值
一个参数返回对象类型, 三个参数,返回新的类型对象
实例
温馨提示
sort 与 sorted 区别:
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
map
简述
map() 会根据提供的函数对指定序列做映射。
第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
语法
参数
function:函数
iterable:一个或多个序列
返回值
返回迭代器
实例
最后更新于
这有帮助吗?
