Python【第四篇】函数、内置函数、递归、装饰器、生成器和迭代器
一、函数
函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性:
- 减少重复代码
- 使程序变的可扩展
- 使程序变得易维护
1.定义
def 函数名(参数): ... 函数体 ... 返回值
函数的定义主要有如下要点:
- def:表示函数的关键字
- 函数名:函数的名称,可根据函数名调用函数
- 函数体:函数中进行一系列的逻辑计算
- 参数:为函数体提供数据
- 返回值:当函数执行完毕后,可以给调用者返回数据。
2.参数
函数的有三种不同的参数:
- 普通参数(也称为位置参数)
- 默认参数
- 动态参数
普通参数
#定义函数 #n是函数name的形式参数,简称:形参 def name(n): print(n) # jack #执行函数 #'jack'是函数name的实际参数,简称:实参 name('jack')
默认参数
def func(name, age = 18): print("%s:%s"%(name,age)) # 指定参数 func('jack', 19) # 上面输出jack:19 # 使用默认参数 func('jack') # 上面输出jack:18 注:默认参数需要放在参数列表最后
动态参数(*args)
def func(*args): print(args) # 执行方式一 func(11,22,33,55,66) # 上面输出(11, 22, 33, 55, 66) # 执行方式二 li = [11,22,33,55,66] # 上面输出(11, 22, 33, 55, 66) func(*li)
动态参数(**kwargs)
def func(**kwargs): print(kwargs) # 执行方式一 func(name='jack',age=18) # 上面输出{'name': 'jack', 'age': 18} # 执行方式二 li = {'name':'jack', 'age':18, 'job':'pythoner'} func(**li) # 上面输出{'name': 'jack', 'age': 18, 'job': 'pythoner'}
参数顺序:位置参数、默认参数(即关键字参数,形参中如果默认参数后面有可变位置参数,实参中,这个默认参数不能写成关键字参数样式,只能写一个值,即位置参数的样子)、可变位置参数、可变关键字参数。
def hi(a,*args,**kwargs): print(a,type(a)) # 11 <class 'int'> print(args,type(args)) # (22, 33) <class 'tuple'> print(kwargs,type(kwargs)) # {'k1': 'jack', 'k2': 'tom'} <class 'dict'> hi(11,22,33,k1='jack',k2='tom')
3.返回值
函数外部的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回。
def stu_register(name, age, course='python' ,country='CN'): print("----注册学生信息------") print("姓名:", name) print("age:", age) print("国籍:", country) print("课程:", course) if age > 22: return False else: return True registriation_status = stu_register("老王",22,course="PY全栈开发",country='JP') if registriation_status: print("注册成功") else: print("too old to be a student.")
注意:
- 函数在执行过程中只要遇到return语句,就会停止执行并返回结果,所以也可以理解为 return 语句代表着函数的结束
- 如果未在函数中指定return,那这个函数的返回值为None
4.全局、局部变量
在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
全局变量作用域是整个程序,局部变量作用域是定义该变量的函数。
当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
全局变量在函数里可以随便调用,但要修改就必须用 global 声明
# 全局变量 P = 'jack' def name(): global P # 声明修改全局变量 P = 'jenny' # 局部变量 print(P) # jenny def name2(): print(P) # jenny name() name2() # jenny
二、内置函数
Python的内置函数有许多,如下图:
# 匿名函数,冒号前面是形参,冒号后面是函数体,并将结果return到函数调用处 f = lambda a, b: a + b print(f(2, 3)) # 5 # abs() 取绝对值 print(abs(-111)) # 111 # all() 循环可迭代对象的每个元素,都为真则返回True,否则返回假 # 0,None ,"",[],(),{} 是假的 print(all([11, 22])) # True # any 有一个为真,全部都为真 print(any([0, 0, None])) # False # bin 将十进制转换成2进制 # oct() hex() print(bin(11)) # 0b1011 # chr() 找到数字对应的ascii码 # ord() ascii码对应的数字 # chr ord 只适用于ascii码 print(chr(65)) # A print(ord('A')) # 65 # divmod 返回除法的(值,余数) print(divmod(10, 3)) # (3,1) # eval 计算器的功能 返回结果 print(eval('a+60', {'a': 90})) # 150 # exec,执行python代码,没有返回值 exec("for i in range(5):print(i)") # 直接循环输出0,1,2,3,4 # filter(函数,可迭代的对象) # 循环可以迭代的对象,传入函数中执行,如果不符合就过滤 def fun(s): # 定义判断一个数是否是偶数的函数 if s % 2 == 0: return True else: return False ret = filter(fun, [1, 2, 3, 4, 5, 6, 7, 8]) for i in ret: print(i) # 打印出2,4,6,8 # 用匿名函数改写一下 ret1 = filter(lambda x: x % 2 == 0, [1, 2, 3, 4, 5, 6, 7, 8]) for i in ret1: print(i) # 2,4,6,8 # map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回 ret = map(lambda x: x + 100, [1, 2, 3]) for i in ret: print(i) # 101,102,103 # globals() 获取当前文件的所有全局变量 # locals() 获取当前文件的所有局部变量 # hash() 获取哈希值 # isinstance 看某个对象是不是某个类创建的 # iter() 创建一个可以被迭代的对象 next()取下一个值 k = iter([1, 2, 3, 4]) print(next(k)) # 1 # pow() 求指数 print(pow(2, 10)) # 1024 # round() 四舍五入 # zip l1 = [1, 2, 3, 4] l2 = ['a', 'b', 'c', 'd'] k = zip(l1, l2) for i in k: print(i) # 打印出(1,a),(2,b).... a = [1, 2, 3, 4, 5] b = ['aaa', 'bbb', 'ccc', 'ddd'] c = [111, 222, 333, 444] for i in zip(a, b, c): print(i) # (1, 'aaa', 111) # (2, 'bbb', 222) # (3, 'ccc', 333) # (4, 'ddd', 444) for i, j, k in zip(a, b, c): print(i, j, k) # 1 aaa 111 # 2 bbb 222 # 3 ccc 333 # 4 ddd 444 # zip应用 for m, n in zip(title_list, content_list): # 把标题和图片对个对应 print('正在下载>>>>>:' + m, n)
三、递归
- 递归就是在过程或函数里调用自身。
- 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
- 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
- 在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序。
用递归写一个阶乘函数 f(n)算出n的阶乘
def f(n): if n==0: # n=0的话直接返回空,对用户输入的零进行判断 return None elif 1==n: # n=1的话就不再递归 return n else: return n*f(n-1) # 递归在执行f(n-1) 直到f(1) print(f(5)) # 120 ''' f(5)的执行过程如下 ===> f(5) ===> 5 * f(4) ===> 5 * (4 * f(3)) ===> 5 * (4 * (3 * f(2))) ===> 5 * (4 * (3 * (2 * f(1)))) ===> 5 * (4 * (3 * (2 * 1))) ===> 5 * (4 * (3 * 2)) ===> 5 * (4 * 6) ===> 5 * 24 ===> 120 '''
利用函数编写如下数列:
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368...
用递归获取斐波那契数列中的第10个数
def fun(n): # fun(10)即可计算第十个斐波拉数 if 1==n : # 直接定义前面两个数为 0 ,1,如果输入的n为1,2的话直接就返回了 return 0 elif 2==n: return 1 else: return fun(n-1)+fun(n-2) #如果输入的不是1,2则进行递归出来计算他前面两个数的和 ''' fun(5)的执行过程如下(fun(10)的结果为34) ===> fun(5) ===> fun(4)+fun(3) ===> fun(3)+fun(2) + fun(2)+fun(1) ===> fun(2)+fun(1)+fun(2)+fun(2)+fun(1) ===> 1+0+1+1+1+0 ===> 3 '''
四、装饰器
本质是函数,装饰其它函数,为其它函数添加附加功能,原则:
- 不能修改被装饰函数的源代码
- 不能修改被装饰的函数的调用方式
装饰器对被装饰的函数是透明的,即:被装饰的函数感知不到装饰器的存在,因为没改函数的代码,运行方式也没变
装饰器执行顺序
def login(func): # 1,3 def inner(arg): # 4,7 print('yanzheng') # 8 func(arg) # 9 return inner # 5 @login # 2,10 def tv(name): print('welcom %s' %name) # 11 tv('wgy') # 6
统计函数执行时间
import time def timmer(func): def warpper(*args, **kwargs): start_time = time.time() func() stop_time = time.time() print('the func run time is %s' %(stop_time-start_time)) return warpper @timmer def test1(): time.sleep(1) print('in the test1') test1()
五、生成器和迭代器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。要创建一个generator,有很多种方法。
第一种方法很简单,只要把一个列表生成式的[]
改成(),
就创建了一个generator:
b = (i*2 for i in range(3)) # 列表生成式的[]改为(),就变成了生成器 print(b) for i in b: print(i)
''' <generator object <genexpr> at 0x00000000021DE8E0> 0 2 4 '''
通过函数写生成器
函数中加yield(暂停),表示可以将函数变成生成器,调用函数时得到一个生成器,yield之后的代码不执行,yield就返回生成器地址,next调用生成器,可以像return一样,返回值,但是不会像return返回一次函数就终止了,而且是在执行过程中,可以多次将数据或者状态返回到next调用处(可以返回函数循环体中每次产生的值),如,读取一个大文件,一边读一边返回。此时脚本中的return信息只在抛出异常的时候打印。
def fib(max): n, a, b = 0, 0, 1 while n < max: print('before yield') # print(b) yield b # 把函数的执行过程冻结在这一步,并且把b的值返回给外面的next() a, b = b, a + b n = n + 1 return 'done' f = fib(3) # 调用函数,将函数变成一个生成器,将地址返回给f print(f) print(f.__next__()) print(next(f)) print(f.__next__()) print(next(f))
''' <generator object fib at 0x0000000001E7E8E0> before yield 1 before yield 1 before yield 2 Traceback (most recent call last): File "F:/test.py", line 15, in <module> print(next(f)) StopIteration: done '''
迭代器
可以直接作用于for循环的数据类型有以下几种:
- 一类是集合数据类型,如list、tuple、dict、set、str等;
- 一类是generator,包括生成器和带yield的generator function。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
注意区别:
可迭代对象:Iterable(可以直接作用于for循环)
迭代器:Iterator(可以被next()函数调用并不断返回下一个值)
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。把list、dict、str等Iterable变成Iterator可以使用iter()函数
from collections import Iterator a = [1,2,3] b = iter(a) print(isinstance(b,Iterator)) print(b.__next__()) print(b.__next__()) print(b.__next__()) print(b.__next__()) ''' True 1 2 3 Traceback (most recent call last): File "F:/test.py", line 8, in <module> print(b.__next__()) StopIteration '''
六、练习题
笔试题汇总(linux、shell、mysql、java、python、性能、自动化、docker、k8s等):
https://www.cnblogs.com/uncleyong/p/11119489.html
__EOF__
关于博主:擅长性能、全链路、自动化、企业级自动化持续集成(DevTestOps)、测开等
面试必备:项目实战(性能、自动化)、简历笔试,https://www.cnblogs.com/uncleyong/p/15777706.html
测试提升:从测试小白到高级测试修炼之路,https://www.cnblogs.com/uncleyong/p/10530261.html
欢迎分享:如果您觉得文章对您有帮助,欢迎转载、分享,也可以点击文章右下角【推荐】一下!