一 函数类型
类型 | 高阶函数 | 闭包函数 | 递归函数 | 内置函数 |
定义 | 一个函数作为参数传递到另一个函数中进行调用的一种特殊嵌套函数 | 引用了外函数的局部自由变量的特殊嵌套函数,属于高阶函数的一种用法。 | 是解决编程问题的一种编程思维或解决思路,是程序不断重复调用自身的一种代码写法,是一种不断自己调用自己的函数。递归函数也属于高阶函数的一种用法。 | python解释器提供了一些常用的内置函数给开发者在python任意一个位置使用 |
声明 |
# 普通函数 # 高阶函数 ret = calc(10,20,add) |
def foo(): func = foo() 闭包函数声明和执行过程中,外函数作用域下的局部变量不会因为闭包函数执行结束而被内存回收删除,而是与内部函数进行引用绑定,导致该变量会继续保留在内存中,并且闭包函数还可以在其定义环境外被执行,并保持与该变量的关系。 |
# 方式1: # 方式2,也叫尾递归: 尾递归的运行速度和消耗性能比普通递归要好,但是CPython不支持尾递归 |
包含基础函数和高阶函数 |
属性 |
# __closure__ 获取闭包函数使用过的外函数作用域下的局部变量,如果值是None则表示当前函数不是闭包 func = foo() |
|||
特点/应用 |
# 应用:计算某个函数运行耗时 def fn1(): print("fn1功能") time.sleep(2) # 让程序执行到这里时,停顿(睡眠、阻塞)2秒 def fn2(): print("fn2功能") time.sleep(3) def timer(fn): """ 检测指定函数的运行耗时 :param fn: :return: """ start_time = time.time() fn() end_time = time.time() return end_time - start_time print(timer(fn1)) 一个高阶函数应该具备下面至少一个特点:
|
# # 应用:计算一个函数被使用了次数 # def outer(): # x = 0 # def inner(a,b): # nonlocal x # x += 1 # print(f"计算了{x}次数据") # return a+b # return inner # # # 调用闭包,不仅可以解决问题,还可以跟踪/记录问题处理的细节 # fn = outer() # ret = fn(10,20) # print(ret) # # 计算了1次数据 # # 30 # ret = fn(50,20) # print(ret) # # 计算了2次数据 # #特点: # 1. 必须是一个嵌套函数 # 2. 必须返回嵌套函数 # 3. 嵌套函数必须引用嵌套作用域自由变量的局部自由变量 |
||
意义 |
1. 闭包可以优先使用外函数中的变量,并对闭包中的值起到了封装保护的作用,外部无法访问。 |
附:1 内置函数的基础函数
函数 | 描述 | 实例 |
all(iterable) | 判断可迭代参数 iterable 中的所有元素是否都为 True,如果是返回 True,否则返回 False。除了是 0、空、None、False 外都算 True。 |
# data = [1, 2, 3, 4] # print(all(data)) # True |
bytes([source[, encoding[, errors]]]) | 相当于str(source).encode(),将参数source转换成bytes字节流,是一个不可变序列。 |
# b0 = b'abc' # print(b0, type(b0) is bytes) # b'abc' True # # bytes字节流也是序列类型,也可以被for循环遍历 # for i in b0: # print(i, chr(i)) # chr是根据整数转换成ascii码 # """ # 97 a # 98 b # 99 c # """ |
bytearray([source[, encoding[, errors]]]) | 将参数source转换成bytes字节流列表,是一个可变序列。 |
# ba0 = bytearray(b'abc') # for i in ba0: # print(i, chr(i)) # # """ # 97 a # 98 b # 99 c # """ |
chr(i) | 返回当前整数对应的 ASCII 字符。 |
# print( chr(97)) # a # print( chr(65)) # A |
ord(c) | 返回当前ASCII字符对应的整数。 |
# print( ord("a") ) # 97 # print( ord("A") ) # 65 |
exec(object[, globals[, locals]]) | python提供的一个内置代码解释器,可以执行储存在字符串或文件中的多行Python语句,有一定的安全性问题。 |
str2 = """ data = [1,2,34] for i in range(1,5): print(i) """ # # exec是没有返回结果的,但是exec执行的字符串代码产生的变量会被保存当前作用域下 exec(str2) print(data)#会有pycharm无法理解的误报,实际上,在exec执行完成以后,是存在在当前作用域的一个变量。 # [1, 2, 34] |
compile(source, filename, mode[, flags[, dont_inherit]]) | 将一个字符串编译为字节代码。一般都是compile配合exec使用。 |
exec 执行的字符串代码大多数来自文件、网络、第三方软件(数据库) 为了提升代码的执行速度,一般使用compile结合exec来执行 compile 翻译: 编译。主要是帮我们把字符串代码编译成字节码 # str1 = """ # for i in range(0,10): # print(i) # """ # content = compile(str1, '', 'exec') # 中间函数表示给当前字符串代码起一个别名或者文件名,留空即可。 # exec(content) # 扩展:传入数据到exec解释器 # data = {'name': 'moluo', 'data': [18, 73, 84]} # code = """ # def fn1(): # return f"name: {name} age:{data[0]}" # """ # func = compile(code, "", "exec") # # 这里代码的意思是:把data字典作为一个作用域信息导入到func中,并且func函数也会被作为一个变量,被记录到data里面 # exec(func, data) # print(data["fn1"]()) #name: moluo age:18 |
dir([object]) | 收集当前作用域范围内的信息(变量,函数等) |
# dir 列表一个对象的成员信息 # 对象(object),是属于面向对象的范畴。 # 我们现在可以对对象先有个基本的了解。 # 所谓的对象,实际上可以理解为类似容器一样的数据类型,主要保存数据的. # 对象有成员,成员分属性(property, 变量,就是对象里面的数据)和方法(method, 函数,这些函数主要用于操作对象或对象的数据) # python是一面面向对象的语言,所以在python中,一切皆为对象,我们之前所使用的各种数据类型,各种的代码结构,本质上都是对象。 # python中的对象非常多。所以如果要了解一个对象给我们提供了什么操作(有什么函数?) # 可以使用dir来列出 # data = {"name": "xiaoming"} # print(dir(data)) # str1 = "hello" # str1.title() # 这里其实就是使用了对象内部提供的函数 |
enumerate | 将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,常用于 for 循环中。 |
# set_data = {"A", "C", "D"} # print(enumerate(set_data)) # <enumerate object at 0x00000260C0F01040> # for k, i in enumerate(set_data): # print(k, i) |
reversed | 反转序列,可以把字符串、列表、元组等序列进行反转排列顺序。 |
区别于reverse方法 # data = [1, 5, 2, 4] # data.reverse() # print(data) # [4, 2, 5, 1] # 上面列表提供的反转,只能给列表使用,无法给字符串,元组等使用。而且,操作过程中,是针对于列表本身操作,会改变原来的数据内容。 # 如果需要反转排列其他的数据类型,或者在反转过程中,需要产生一个新的可遍历对象(可迭代对象),不想改动原有数据, # 则可以使用revered函数 # data = [1, 5, 2, 4] # ret = reversed(data) # print(ret) # <list_reverseiterator object at 0x000002A7D37E7B20>, 注意关键字:iterator,表示可迭代对象 # print(list(ret)) # 针对reversed这种返回一个可迭代的无法直接打印的信息,可以使用list进行强制转换 # print(data) |
zip | 将可迭代的对象作为参数,将对象中对应的元素压缩成一个个元组,然后返回由这些元组组成的列表。加*号则表示解压。 |
""" zip 压缩数据和解压数据,一般用于实现矩阵效果,下面是一个3x3的矩阵 [ [1,2,3], [4,5,6], [7,8,9], ] """ # data1 = [1,3,5] # data2 = [2,4,6] # zip可以实现把多个列表的数据,按列进行压缩,返回值是一个可遍历对象(可迭代对象) # ret = zip(data1, data2) # print(ret) # <zip object at 0x000002C3222B48C0> # print(list(ret)) # [(1, 2), (3, 4), (5, 6)] 注:按列压缩时,如果超出部分,会忽略的 # 解压还原 # data11, data21 = zip(*zip(data1, data2)) # print(data11) # (1, 3, 5) # print(data21) # (2, 4, 6) |
locals | 获取当前作用域的所有标记符(变量,函数,类等...) |
"""应用1:把函数内部所有变量全部导出外界""" def fn(): title = "小标题" user = "小明" age = 15 return locals() ret = fn() print(ret) # {'title': '小标题', 'user': '小明', 'age': 15} |
globals | 获取全局作用域的所有标记符(变量,函数,类等...) |
"""应用2:批量声明变量""" # 动态创建5个全局变量p1~p5 def fn(): for i in range(1,6): data = globals() data[f"p{i}"] = i fn() print(p1) print(p2) print(p3) print(p4) print(p5) |
eval(expression[, globals[, locals]]) | python提供的一个内置代码解释器,可以执行储存在字符串中的Python表达式字符串,功能没有exec强大,也有一定的安全性问题 |
# content = input("请输入你要执行的表达式代码:") # print(content, type(content)) # ret = eval(content) # print(f"程序执行结果:{ret}") 请输入你要执行的表达式代码:1+2 |
complex([real[, imag]]) | 转化一个字符串或数为复数。复数是python中数值类型之一 |
# 复数的运算:实部和实部计算,虚部和虚部计算。 # num1 = complex(3,-2) # print(num1) # (3-2j) |
frozenset | 返回一个冻结的集合,冻结后集合不能再添加或删除任何元素 |
# print(set_dat)={'D', 'B', 'A', 'C'} # # 转成冰冻集合 # fro_set = frozenset(set_dat) # print(fro_set) # frozenset({'C', 'B', 'A', 'D'}) # #不能新增删除成员,否则会报错。 |
hash | 获取取一个对象(字符串或者数值等)的哈希值。 |
# 在python,数据类型分可变和不可变类型 # 不可变类型:数值类型(整型、浮点型、布尔型、复数类型)、字符串、元组 # 可变类型:列表、集合、字典 # 可变类型是无法hash的,只有不可变类型才能hash # 所以有时候,可不可以hash也可以作为判断一个数据类型是否是可变类型还是不可变类型的依据 # 后续使用hashlib。工作中不使用hash,改用hashlib原因,因为它无法区分数值相等,不同类型的信息。 # data1 = 1 # data2 = 1.0 # data3 = True # print(hash(data1)) # 1 # print(hash(data2)) # 1 # print(hash(data3)) # 1 |
注:
1 bytes字节流与bytearray字节数组,属于二进制的字节数据。string字符串与bytes字节流的区别: 1. 字符串是字符组成的有序的不可变序列 2. bytes是字节组成的有序的不可变序列 3. bytearray是有序的字节组成的可变序列 # bytes字节流属于不可变类型,与字符串的操作一样,只是一个bytes字节流,一个是字符串而已。 # bytearray字节数组属于可变类型,与列表的操作类似,字节数组不仅支持append, insert等序列操作,还支持字符串操作,可以理解为列表和字符串的组合 # s1 = "abc" # ret = s1.replace("b", "w") # print(ret) # awc # print(s1) # abc 替换的不是原来的字符串,而是基于替换操作产生一个新的字符串序列 #b5 = bytearray(b'bcd') #print(ord("e")) # 101 #b5.append(ord("e")) #print(b5) # bytearray(b'bcde')
2 滑动序列 """ zip在面试中,有时候会被问到一个滑动序列的操作 所谓的滑动序列,指可以根据指定一个任意长度n,对列表进行划分成n个子序列的序列容器。 生活中,开发中需要滑动序列的场景: 1. 班上所有同学组成一个名单,按指定数量n分多个兴趣小组。 2. 有一批学生要坐车到不同的公司企业实习,每一辆的座位有限,可以使用滑动序列可以快速划分每辆车的学生名单 3... """ data = [1, 2, 3, 4, 5, 6, 7, 8, 9] # n = 2 # data = [(1, 2), (3, 4), (5, 6), (7, 8)] #目标 # 根据zip可以实现矩阵效果,列出目标列表数据 # d1 = [1,3,5,7] # d2 = [2,4,6,8] # print( list(zip(d1,d2)) ) # n = 2 # for i in range(n): # print( data[i::n] ) # [1, 3, 5, 7, 9] # [2, 4, 6, 8] # n = 3 # data = [(1, 2, 3), (4, 5, 6), (7, 8, 9)] # d1 = [1,4,7] # d2 = [2,5,8] # d3 = [3,6,9] # print( list( zip(d1,d2,d3) ) ) # 运用 data = [1, 2, 3, 4, 5, 6, 7, 8, 9] def ss(data, n): """滑动序列""" list_data = [] for i in range(n): list_data.append(data[i::n]) return list( zip(*list_data) ) ret = ss(data,2) print(ret) # [(1, 2), (3, 4), (5, 6), (7, 8)] ret = ss(data,3) print(ret) # [(1, 2, 3), (4, 5, 6), (7, 8, 9)] """ list_data = [data1, data2] zip(*list_data) <==等同==> zip(data1, data2) """ 3 输出n位验证码 # # 输出一串大小写字母、数字组成的列表 def content(): data = [] number = list(range(48,48+10)) #1-9 lowercase = list(range(97,97+26)) #a-z uppercase = list(range(65,65+26)) #A-Z for i in number+lowercase+uppercase: data.append(chr(i)) return data import random def random_string(length=6): """ 生成指定长度的随机字符串 :param length: :return: """ ls = content() random.shuffle(ls) return "".join(ls[:length]) code = random_string() print(code)
2 内置函数的高阶函数
函数 | 描述 | 实例 |
filter(function, iterable) | 过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。 |
# data = [1, 2, 3, 4] # def fn1(item): # """保留偶数""" # return not item % 2 # # # ret = filter(fn1, data) # # print(list(ret)) # [2, 4] # # # 采用匿名函数更加优雅,简洁 # ret = filter(lambda item: not item % 2, data) # print(list(ret)) # [2, 4] |
map(function, iterable, ...) | 根据提供的函数对指定序列做映射处理。 |
# data = [99.333, 55, 60, 30.560] # def fn1(item): # return f"{item/100:.2%}" # ret = map(fn1, data) # print(list(ret)) # ['99.33%', '55.00%', '60.00%', '30.56%'] |
sorted(iterable, cmp=None, key=None, reverse=False) | 对所有可迭代的对象进行排序操作。 |
""" sorted 高阶排序 sort是 list的操作方法,修改的是原列表数据,其他数据类型没有这个sort函数 而sorted可以对所有可迭代的对象进行排序操作,返回修改后的新数据。 """ # 原来的列表排序,针对源数据进行操作,会破坏原来的数据内容 # data = [5, 2, 6, 3, 4] # data.sort() # 正序 # print(data) # [2, 3, 4, 5, 6] # 使用sorted不会破坏原来的数据内容,会产生一个新的可迭代对象 # data = [5, 2, 6, 3, 4] # ret = sorted(data) # 正序 # print(data) # [5, 2, 6, 3, 4] # print(ret) # [2, 3, 4, 5, 6] # # 让字典/列表(二级等长容器)里面的键按字母排列顺序来对成员进行排序 # data = { # "age": 4, # "mobile": 6, # "name": 5, # "sex": 3, # } # # def func(item): # return item[0] # # ret = sorted(data.items(), key=func) # ret = sorted(data.items(), key=lambda item: item[0]) # print(ret) # [('age', 4), ('mobile', 6), ('name', 5), ('sex', 3)] # # sorted还可以针对复杂的数据结构进行排序 # data = [ # {"name": "小名", "chinese": 88, "math": 70}, # {"name": "小白", "chinese": 85, "math": 71}, # {"name": "小黑", "chinese": 94, "math": 69}, # ] # # 按总成绩进行排序[从大到小] # def func(item): # print(f"item={item}") # return item["chinese"] + item["math"] # ret = sorted(data, key=func, reverse=True) # print(ret) # # # lambda 编写简单,代码简洁,易上脑,但是不好调试,不好拍错,复杂lambda表达式不好理解 # ret = sorted(data, key=lambda item: item["chinese"]+item["math"], reverse=True) # print(ret) |
functools.reduce(function, iterable[, initializer]) 注意:在python2中,reduce属于内置函数,直接可以使用,在python3中被官方移至functools模块中了,必须导入functools才能使用。 |
对参数序列中元素进行叠加/累积操作,返回一个最终结果 | """
reduce的使用类似map,但是与map不一样, map是针对列表中每一个成员进行加工映射操作,就结束了。 reduce是针对列表中的每一个成员进行加工映射,除此之外,还进行累积操作,一般用于对列表中的成员进行累加/累乘等相似的操作. """ from functools import reduce data = [1, 2, 3, 4] def func(a, b): print(f"a={a}, b={b}") return a*b ret = reduce(func, data) print(ret) # 24 # 把列表中的每一个成员进行累乘 ret = reduce(lambda x,y: x*y, data) print(ret) # 24 |