Python学习笔记--函数
1.如何理解函数?
- 函数就是一个容器,把原来的代码放到一起。
2.如何运行函数?
- 函数是分为两个阶段 定义和调用。
- 调用是函数名+() 来调用。
3.如果直接调用函数名,不加()会发生什么?
- 会打印出函数名所在的内存地址。
-
def foo(): print("hello ") print(foo) # 打印foo的内存地址 foo() # 调用函数() ====结果为==== <function foo at 0x000001526568D3A0> hello
- 这里面涉及到一个隐藏的知识点,
- 就是当我们定义一个x=10的时候,是把10的内存地址给了x,当我们打印x的时候,却没有打印出内存地址,打印出的是数值10,为什么?
- 这是因为Python对这种简单的做了特殊处理,因为90%人们是希望通过x获取到10的值,而不是打印出10的内存地址。
- 而函数为什么又打印内存地址呢?
- 如果调用函数的名字,就打印出函数体里面的内容,这种场景90%的人们都用不上。所以,就没有进行特殊处理。
- (这里的90%是我瞎说的)
4.如果理解函数先定义后调用?
-
def foo(): print("hello ") bar() # foo() 在这执行会报错 def bar(): print("123") foo() # 充分理解, 函数先定义后执行
5.函数可以分为自定义函数和内置函数?
- 对的
6.函数的形参和实参如何区分?
- 定义阶段是形参
- 调用阶段是实参
-
def foo(x,y): # x和y是形参 return x+y foo(1,2) # 1和2是实参 m = 5 n = 6 foo(m,n) # m和n是实参
7.位置实参必须在关键字实参前面?
- 是的
-
def foo(x,y,z): pass foo(1,y=2,3) # 报错 SyntaxError: positional argument follows keyword argument
8.默认形参该怎么用?
- 注意:
- 1.位置形参必须在默认形参前面
- 2.默认形参应该是不可以变类型
- 3.默认形参的值是在定义阶段进行赋值的
- 示例1:
-
def foo(name,hobby,hobbies = None): if hobbies is None: hobbies = [] hobbies.append(hobby) print(f"{name} 爱好是 {hobbies}") foo("liqi","eat") foo("tom","read",["movie"])
- 示例2:
-
m = 111 def foo(x,y,z=m): print(x) print(y) print(z) m = 666 foo(1,2) # 考察z在定义阶段
9.当往函数里面传参数时,不确定实参的数量,该怎么办?
- 定义函数的时候,用*或者**来解决。
- 实参无非两种形式
- 溢出的位置参数 —— *
- 溢出的关键字参数—— **
- 代码1
-
# 溢出的位置参数用* def func(x,*y): print(x) print(y) func(1,2,3,4,5) # 1 # (2, 3, 4, 5) # *会以元组的形式接受溢出的位置参数,赋给y
- 代码2
-
# 溢出的关键字参数** def func(m,**kwargs): print(m) print(kwargs) func(3,x=3,y=10,z=8) # 3 # {'x': 3, 'y': 10, 'z': 8} # **会将溢出的关键字参数合并为一个字典,赋给kwargs
10.可变参数有什么应用吗?
- 比如,求任意几个数的和
-
def func(*x): res = sum(x) print(res) func(1,2,3,4,5,999)
11.在定义阶段,*的做用时汇集,在调用阶段,*的作用是什么?
- 打散
- 比如:
-
def fun(x,y,z): print(x,y,z) # fun([1,2,3]) 报错 fun(*[1,2,3]) # 1 2 3
- ** 的示例
-
def fun(x,y,z): print(x,y,z) fun(**{"x":1,"y":3,"z":5}) # **会打散为关键字参数 x=1,y=3,z=5 # 1 3 5
12.函数调函数,*和**可以把参数原封不动的转交给另外一个函数吗?
- 可以
- 示例
-
def index(x,y,z): print(x,y,z) def wrapper(*args,**kwargs): print(args) print(kwargs) index(*args,**kwargs) # 会报错,相当于index(1,2,4,5,6,a=1,b=2,c=3) wrapper(1,2,4,5,6,a=1,b=2,c=3)
13.函数名可以当做变量来使用吗?
- 可以。
- 示例代码
-
def func(): print("from func") # f = func # 当做变量赋值 # f() # 调用 def aaa(x): print("aaa执行了") x() aaa(func) # 函数名可以当做变量来传递
14.如何优雅的改下if的多分支功能?
- 把函数名当做容器的元素
-
1 def withdraw(): 2 print("取款") 3 def deposit(): 4 print("存款") 5 def check_balance(): 6 print("查询余额") 7 def transfer(): 8 print("转账".center(20,"=")) 9 10 bank_dict = { 11 "1":["取款",withdraw], 12 "2":["存款",deposit], 13 "3":["查询余额",check_balance], 14 "4":["转账",transfer], 15 } 16 while True: 17 print("0 退出") 18 for k,v in bank_dict.items(): 19 print(k,v[0]) 20 21 choice = input("输入要执行的编号:") 22 if choice == "0": 23 break 24 25 if choice in bank_dict: 26 bank_dict[choice][1]() 27 else: 28 print("输入有误!!")
15.什么是名称空间,顶级名称空间有哪些?
- Namespaces are one honking great idea -- let's do more of those!
- 名称空间,是python的一个伟大的思想。
- 顶级名称空间最简单的划分就是,观察是否在函数体内。
- 例如,
x = 10 if x >2: y = 20 with open("a.txt","wt",encoding="utf-8")as f: date = f while True: Z = 99
16.名称空间的一些区别?
- 内置名称 (跨模块名称)
- 全局名称
- 局部名称
- 范围由大到小是: 1 > 2 > 3
- 查找循序是:根据目前所在的位置由下(3)至上(1)查找。
- LEGB是什么?(面试会问)
- 是名称空间的访问顺序,local --从局部开始查找..
17. 名字空间与作用域的关系是在定义阶段就决定了?
- 是的
-
x = 111 def fun_1(): print(x) def func_2(): x = 222 fun_1() func_2() #答案是 111
-
x = 111 def func(): print(x) # 这个报错是,在语法扫描的时候,在本函数内找到了,绑定在本函数内。 # 但执行的时候,是先执行,后定义,所以执行错误。 x = 222 func()
18.闭包函数是什么?
- 闭:该函数定义在函数内的函数
- 包:该函数引用了一个外层函数作用域的名字
-
def outter(): x = 111 def wrapper(): print(x)
19.闭包函数,可以把内部的函数扔出来吗?
- 可以
-
def outter(): x = 111 def wrapper(): print(x) return wrapper # 不加括号,把内部函数扔出来
20.闭包函数如何传参数?
- 最简单的一种
-
def outter(x): def wrapper(): print(x) return wrapper f = outter(111) f()
21.什么是装饰器?
- 给原有的工具增加的新的功能
22.为何要用装饰器?
- 开放封闭原则:一旦项目上线后,应该对修改源代码封闭,对外扩展开发
- 原则1:不修改源代码
- 原则2:不改变源代码的调用方式
- 装饰器就是在遵循原则1和原则2的前提下,实现给原来的函数增加新功能的东西。
23.如何实现装饰器?
- 构造一个简单的装饰器的功能
- 第一步,正常加功能,
- 第二步,构造外层函数
- 第三步,返回未闭合的内部函数名
- 第四步,偷梁换柱
-
def index(): time.sleep(1) print("from index") def outter(index): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 def wrapper(): # 第一步 构造新功能 start_time = time.time() index() end_time = time.time() print(end_time-start_time) return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 index = outter(index) # 偷梁换柱 index()
24.装饰器的精髓是什么?
- 装
- 伪装
25.装饰器如何加参数?
- 在23的基础上,增加参数呗
-
def index(x,y,z): # 加了参数 time.sleep(1) print("from index",x,y,z) def outter(func): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 def wrapper(x,y,z): # 第一步 构造新功能 ==加了参数 start_time = time.time() func(x,y,z) # == 加了参数 end_time = time.time() print(end_time-start_time) return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 index = outter(index) # 偷梁换柱 index(1,22,33)
- 通用写法:
-
def index(x,y,z): time.sleep(1) print("from index",x,y,z) def outter(func): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 def wrapper(*args): # 第一步 构造新功能 # 更进一步 start_time = time.time() func(*args) # 更进一步 end_time = time.time() print(end_time-start_time) return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 index = outter(index) # 偷梁换柱 index(4,5,6)
26.装饰器25在参数层面已经ok,如何拿到返回值呢?
- 加个res接受就ok了
-
def index(x,y): time.sleep(1) print("from index",x,y) return 123 def outter(func): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 def wrapper(*args): # 第一步 构造新功能 # 更进一步 start_time = time.time() res = func(*args) ##拿到返回值 end_time = time.time() print(end_time-start_time) return res ##拿到返回值 return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 index = outter(index) # 偷梁换柱 res = index(4,5) print(res)
27.装饰器可以在26的基础上更炫酷一点吗?
- 用语法糖简化
-
import time def outter(func): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 def wrapper(*args): # 第一步 构造新功能 # 更进一步 start_time = time.time() res = func(*args) ##拿到返回值 end_time = time.time() print(end_time - start_time) return res ##拿到返回值 return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 @outter # 语法糖的方式 index=outter(index) def index(x, y): time.sleep(1) print("from index", x, y) return 123 @outter # 语法糖 home=outter(home) def home(name): time.sleep(2) print(f"welcome {name} !!!") return name # index = outter(index) # 偷梁换柱 这一步可以改成语法糖的方式 if __name__ == '__main__': res = index(4, 5) print(res) res_name = home("liqi") print(res_name)
28.如何把函数的属性也都装起来,伪装的更像?
- 笨方法是用# wrapper.__doc__ = func.__doc__
- 更彻底的是用@wraps取代
-
import time from functools import wraps # 导入自带的wraps def outter(func): # 第二步 构造外函数 -为后面第三步构造未闭合机制做准备 @wraps(func) # 把所有属性都给warpper 等同于下面的__doc__赋值 def wrapper(*args): # 第一步 构造新功能 # wrapper.__doc__ = func.__doc__ # 用wraps取代 start_time = time.time() res = func(*args) end_time = time.time() print(end_time - start_time) return res return wrapper # 第三步,构造未闭合的机制 必须借助这个return来实现 @outter # 语法糖的方式 def index(x, y): """这是index""" time.sleep(1) print("from index", x, y) return 123 if __name__ == '__main__': res = index(4, 5) print(res) print(index.__name__) print(index.__doc__)
29.默写一个装饰器?
-
from functools import wraps import time def outter(func): @wraps(func) # 这里要传参数 def wrapper(*args): start = time.time() res = func(*args) end = time.time() print("用时:",end - start) return res return wrapper # 测试一下 @outter def index(x,y): """你好 index""" print(x,y) time.sleep(0.5) return x,y if __name__ == '__main__': res = index(1,2) # print(index.__doc__) # print(res)
30.装饰器模板是?
-
from functools import wraps def outter(func): @wraps(func) def index(*args,**kwargs): res = func(*args,**kwargs) return res return index
- 思路是
- 先写个闭合函数
- 加上参数
- 加上返回值
- 同步属性
31.用装饰器写一个认证功能?需要,认证通过才能执行代码,不通过不执行。
- 代码
-
# 装饰器的认证功能 from functools import wraps def login(func): @wraps(func) def index(*args,**kwargs): username = input("请输入账号") password = input("请输入密码") if username == "liqi" and password == "123": res = func(*args, **kwargs) return res else: print("认证失败!") return index @login def home(x): print("执行home") return x home(9)
32.多层装饰器的执行顺序是?
- 执行前1-->执行前2-->执行前3-->home-->执行后3-->执行后2-->执行后1
-
-
def outter1(func): def index(*args,**kwargs): print("执行1") res = func(*args,**kwargs) print("执行1后") return res return index def outter2(func): def index(*args,**kwargs): print("执行2") res = func(*args,**kwargs) print("执行2后") return res return index def outter3(func): def index(*args,**kwargs): print("执行3") res = func(*args,**kwargs) print("执行3后") return res return index @outter1 @outter2 @outter3 def home(): print("home") home() # 执行顺序是 ''' 执行前1-->执行前2-->执行前3-->home-->执行后3-->执行后2-->执行后1'''
33.什么是迭代器?
- 迭代器是迭代取值的工具
- 什么是迭代?
- 迭代是一个重复的过程,但每次重复都在上次的基础上有变化。
- (爸爸是迭代1号,我是迭代2号,我孩子是迭代3号)
-
l = [11,22,33] i = 0 while i<len(l): print(l[i]) i+=1 #迭代的每次变化
34.为何要用迭代器?
- 迭代器提供了一种不依赖索引,通用的迭代取值的方案
- 节省内存
35.如何使用迭代器?
- 可迭代对象
- 内置有__iter__方法的对象的都叫可迭代对象
- 调用可迭代对象的__iter__方法,返回的是对应的迭代器
- 迭代器对象
- 内置有__next__方法
- 内置有__iter__方法
- 调用迭代器对象的__iter__方法,返回的是它自己。
- 调用迭代器对象的__next__方法,返回的是下一个值,不依赖索引。
- (扩展)内置的函数都是可迭代对象
- 元组,列表,字典,字符串,集合。是可迭代对象,仅有__iter__方法。
- 注意:文件是迭代器对象,不仅有__iter__,还有__next__方法。
36.迭代器的示例?
- 代码
-
# l = [1,2,3,4] l = {"name":"123","age":99} n = l.__iter__() # 返回一个迭代器 print(n.__next__()) print(n.__next__())
37.for循环可以用迭代器实现?
- 是的
-
nums_iter = nums.__iter__()
while True:
try:
print(nums_iter.__next__())
except StopIteration:
break -
nums_iter = iter(nums) while True: try: print(next(nums_iter)) except StopIteration: break
38.生成器是什么?
- 是自定义的一种迭代器。
- 利用yield关键字构造生成器
- 示例
-
def func(): yield "xxx" yield "yyy" yield "zzz" res = func() print(res.__next__()) print(res.__next__())
39.用生成器,生成一个可以产生无穷值的生成器?
- so easy
-
def endless(): i = 0 while True: i+=1 print(i) yield i # 测试 g = endless() while True: next(g)
40.生成器有缺点么?
- 单向流通,不能回退取值
- 不能指定索引取值。
- 不能知道里面的长度
- 同一个迭代器,有记忆性,取干净就没了。
41.下面的代码是,有什么区别?
-
nums = [11,22,33] x = iter(nums) # for i in x: # x.__iter__() # print(i) # print("="*20) # for i in x: # x.__iter__() # print(i) for i in nums: # nums.__iter__() 新迭代器 print(i) print("="*20) for i in nums: # nums.__iter__() print(i) # 下面的是每次产生一个新的迭代器,上面的是每次都用用一个迭代器
- 下面的每次产生一个新的迭代器,上面的是用同一个迭代器。
42.三元表达式是什么?
- 取代简单函数判断的方式
-
x = 11 y = 22 # def func(): # if x > y: # return x # else: # return y # res = func() # print(res) # 三元表达式 res_3 = x if x > y else y print("三元表达式", res_3)
- res = x if x >y else y
43.列表生成器是什么?
- 可以快速生成列表的
-
# 列表生成器 # l = [] # for i in range(10): # l.append(i) # print(l) l = [i for i in range(10)] # l = ["x" for i in range(10)] 前面"x"可以任意,是个函数也可以,只要是可以产生值的就可以。 print(l)
43.1 字典生成器怎么写?
res={i:i**2 for i in range(5)} print(res)
44.列表生成器可以执行列表?(常用)
- 是的
-
def func(): return [i for i in range(10)] l = [func() for i in range(8)] print(l)
- (扩展)也有字典生成器,集合生成器,不常用。
45.简单代码的原则是什么?
- 在不丧失代码可读的情况下,可以简化。
46.需求,给列表里的元素加个烧饼?
-
names = ["lxx","wxx","lili","jjj"] l = [i+"_烧饼" for i in names] print(l)
47.需求,把列表里没有烧饼的筛选出来
- 先写循环【i for i in names】,后面写条件 【if “烧饼” not in i】
-
names = ['lxx_烧饼', 'wxx_烧饼', 'lili_烧饼', 'jjj_烧饼',"egon"] # new_names = [i for i in names if i.endswith("烧饼")] new_names = [i for i in names if "烧饼" not in i] print(new_names)
48.如何写一个生成器表达式?
- 用小括号
-
res = (i for i in range(10)) print(res) # 这里是个老母鸡 print(next(res)) print(next(res)) print(next(res))
49.什么是函数的递归调用?
- 递归调用就是反复调用自己。
- 大前提示:递归调用一定要在某一层结束
- 递归的两个阶段:
- 1、回溯:向下一层一层挖井
- 2、递推:向上一层一层返回
50.如何把数学表达式写成递归的形式?
- age(5) = age(4) + 10
- age(4) = age(3) + 10
- age(3) = age(2) + 10
- age(2) = age(1) + 10
- age(1) = 18
-
def age(n):
if n ==1:
return 18 return age(n-1) +10
51.如何把像下面的列表里的数字都取出来
- 列表是 l = [1,[2,[3,[4,[5,[6,[7]]]]]]]
-
l = [1,[2,[3,[4,[5,[6,[7]]]]]]] # 第一步 写个简单for循环 # for i in l: # if type(i) is list: # pass # else: # print(i) # 第二步 缩进到函数里,把pass替换为函数 def get(l): for i in l: if type(i) is list: get(i) else: print(i) get(l)
52.写一个二分查找?
- 第一步:写一个if 条件
- 第二步:用函数封装,循环调用自身
- 第三步:异常处理
-
l = [-3, 2, 4, 6, 7, 9, 11, 14, 15, 27, 34, 46, 57, 62, 68, 69, 71] find_num = 2 def get(l, find_num): try: mid_num = len(l) // 2 if l[mid_num] == find_num: print("找到了",) elif l[mid_num] < find_num: # 如果要找的数大于中位数,继续在右边找 new_l = l[mid_num + 1:] get(new_l, find_num) # 继续找 elif l[mid_num] > find_num: # 如果要找的数小于中位数,继续在左边找 new_l = l[:mid_num] get(new_l, find_num) # 继续找 except IndexError: print("找不到!!") get(l,find_num)
53.写一个二分法?(方式二)
- 用Len判断长度为0
-
l = [-3, 2, 4, 6, 7, 9, 11, 14, 15, 27, 34, 46, 57, 62, 68, 69, 71] find_num = 71 def get(l, find_num): # print(l) mid_num = len(l) // 2 if mid_num == 0: print("not exits") return if l[mid_num] == find_num: print("找到了",) elif l[mid_num] < find_num: # 如果要找的数大于中位数,继续在右边找 new_l = l[mid_num + 1:] get(new_l, find_num) # 继续找 elif l[mid_num] > find_num: # 如果要找的数小于中位数,继续在左边找 new_l = l[:mid_num] get(new_l, find_num) # 继续找 get(l,find_num)
54.算法是什么?
- 高效解决问题的一种方式,
- 在你月薪3万以下,基本用不到算法
55.什么是匿名函数?
- 匿名匿名,就是没有名字的函数。
- 有名函数,写完了,可以多次调用。
- 那匿名函数,写完了,只能调用一次。适用于,临时用一次
- 下面的用法可以,但很少这样用。
-
# 匿名函数 # f= lambda x,y:x+y # res = f(1,2) # print(res) # res = (lambda x,y:x+y)(1,4) # print(res)
- lambda(条件: 返回的结果)
56.匿名函数的应用
- 跟max适用,拿到薪资最高人的名字
-
salaries = { "lxx":3000, "egon":100000, "zxx":1000 } # def func(k): # return salaries[k] # print(max(salaries,key=func)) # max会遍历字典的人名,每遍历一次人名告诉后面的max的比较依据key说比较的是字典值 print(max(salaries,key=lambda k :salaries[k]))
- (扩展)min 和sorted:
-
salaries = { "lxx":3000, "egon":100000, "zxx":1000 } print(max(salaries,key=lambda k :salaries[k])) print(min(salaries,key=lambda k :salaries[k])) print(sorted(salaries,key=lambda k :salaries[k])) # 从小到大 排列薪资 print(sorted(salaries,key=lambda k :salaries[k],reverse=True)) # 从大到小 排列薪资
57.什么是函数式编程?
- 类似上面56的这种
- sorted(salaries,key=lambda k :salaries[k],reverse=True)
- 函数套函数去解决问题的,是函数式编程
- 类似的还有map ,filter ,reduce
- Python支持函数式编程,但不是常用的选项。
- 参考资料 : 阮一峰,知乎
58.介绍几种函数式编程的关键词的用法
- map(函数,可迭代的对象)
-
names = ["lxx","zxx","exx","yxx"] res = list(map(lambda name:name+"_你好",names)) # map要转换为list,本身应该是个map对象,是个迭代器,打印不出来结果。 print(res)
-
- filter(函数(筛选出布尔值为真的),可迭代对象)
-
names = ["lxx_ii","jxx_ii","egon","wxx_ii"] print([ i for i in names if i.endswith("ii")]) # 筛选出ii res = filter(lambda name:name.endswith("ii"),names) print(list(res))
-
- reduce(合并函数,可迭代对象,[可选初始值]) 注意 这个本身有返回值
-
from functools import reduce res = reduce(lambda x,y:x+y ,[11,22,33]) print(res) # 两两合并,减少序列值为一个值 res = reduce(lambda x,y:x+y ,[11,22,33],100) print(res) # 增加可选初始值100
-
59.面向过程的编程思想
- 核心是过程。
- 那什么是过程?就是做事的一系列步骤。
- 基于该思想,好比在设计流水线。
- 好处:
- 复杂的问题简单化,流程化。
- 计算机的底层思想也是过程的思想,可以说过程思想是一切思想的基石。
- 缺点:
- 牵一发而动全身,扩展性差
60.面向过程的编程思想跟面向对象的有高低之分吗?
- 没有。
- 武林门派,没有高低之分,习武之人,有高低之分。
- 也就是,编程思想,看你用在合适的场景里。没有什么low不low的,low的只是人们的无知。
61.什么是框架?
- 本质上,就是有人发现大量的人都在写重复的代码。有人跳出来说,我帮你们把这部分公共的写好,你们在我的基础上再写自己的东西。
- 可以认为是一种抄别人代码的事情。
- 只不过,面试的时候,不能直接说,我的工作就是抄别的代码,然后在修修改改。
- 所以,面试的时候,说是用xx框架,做二次开发。
- 但,回过头来,抄别人的代码,真的是不可取的,是不对的吗?
- 目前的公司,或者业务,都是需要你快速开发,快速产生价值。那么使用框架确实节省时间。那么就是有价值的。
- 比如,目前的社会分工许多,难道说,你吃一顿猪肉白菜的饺子,一定要先从养猪,再种白菜 开始吗?
参考资料: