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是实参
- View Code
7.位置实参必须在关键字实参前面?
- 是的
def foo(x,y,z):
pass
foo(1,y=2,3)
# 报错 SyntaxError: positional argument follows keyword argument
- View Code
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"])
- View Code
- 示例2:
m = 111
def foo(x,y,z=m):
print(x)
print(y)
print(z)
m = 666
foo(1,2) # 考察z在定义阶段
- View Code
9.当往函数里面传参数时,不确定实参的数量,该怎么办?
- 定义函数的时候,用*或者**来解决。
- 实参无非两种形式
- 溢出的位置参数 —— *
- 溢出的关键字参数—— **
- 代码1
# 溢出的位置参数用*
def func(x,*y):
print(x)
print(y)
func(1,2,3,4,5)
# 1
# (2, 3, 4, 5)
# *会以元组的形式接受溢出的位置参数,赋给y
- View Code
- 代码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
- View Code
10.可变参数有什么应用吗?
- 比如,求任意几个数的和
def func(*x):
res = sum(x)
print(res)
func(1,2,3,4,5,999)
- View Code
11.在定义阶段,*的做用时汇集,在调用阶段,*的作用是什么?
- 打散
- 比如:
def fun(x,y,z):
print(x,y,z)
# fun([1,2,3]) 报错
fun(*[1,2,3])
# 1 2 3
- View Code
- ** 的示例
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
- View Code
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)
- View Code
13.函数名可以当做变量来使用吗?
- 可以。
- 示例代码
def func():
print("from func")
# f = func # 当做变量赋值
# f() # 调用
def aaa(x):
print("aaa执行了")
x()
aaa(func) # 函数名可以当做变量来传递
- View Code
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("输入有误!!")
- View Code
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'''
- View Code
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)
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框架,做二次开发。
- 但,回过头来,抄别人的代码,真的是不可取的,是不对的吗?
- 目前的公司,或者业务,都是需要你快速开发,快速产生价值。那么使用框架确实节省时间。那么就是有价值的。
- 比如,目前的社会分工许多,难道说,你吃一顿猪肉白菜的饺子,一定要先从养猪,再种白菜 开始吗?