Intermediate Python
函数参数 (*args 和 **kwargs)
- 标准参数(fargs)、*args、*kwargs 使用顺序:`func_name(fargs, *args, *kwargs)`
- *args、*kwargs使用场景:函数装饰器、猴子补丁
调试(debugging)
- 命令行运行:`python -m pdb xxx.py`
- 脚本内部运行:`import pdb pdb.set_trace()`
- pdb命令列表:
`c` - 继续执行
`w` - 显示当前正在执行的代码行的上下文信息
`a` - 打印当前函数的参数列表
`s` - 执行当前代码行,并停在第一个能停的地方(单步进入)
`n` - 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)
- 单步跳过(next)和单步进入(step)的区别在于:
1. 单步进入会进入当前行调用的函数内部并停在里面
2. 单步跳过(几乎)全速执行完当前行调用的函数,并停留在当前函数的下一行
生成器(generators)
生成器是只能迭代一次的迭代器。在python中,使用yield
的函数被称为生成器;大多数生成器是以函数实现的,但并不返回一个值,而是yield
(生出)一个值。
迭代器(iterators)是一个让程序员可以遍历一个容器的对象,只要定义了一个next
(python2)或__next__
方法的对象就是一个迭代器
可迭代对象(iterable):可以返回迭代器的__iter__
方法或支持下标索引的__getitem__
方法的任意对象。
迭代(iteration)就是从某个地方(e.g. 列表)取出一个元素的过程
调用一个生成器函数,返回的是一个迭代器对象 (生成器是返回迭代器的函数)
def gen():
for i in range(10):
yield i
ite = gen() # gen()-生成器函数,ite-迭代器对象
for i in range(5):
print(f"next(ite): {next(ite)}")
print("-"*5)
for j in range(5):
print(f"ite.__next__(): {ite.__next__()}")
生成器最佳应用场景:不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。
生成器只能用于迭代,生成器都是迭代器,但迭代器不一定是生成器。
yield所有的值之后,next()会触发一个StopIteration的异常,for循环会自动捕捉到这个异常并停止调用next()
内置函数(Map和Filter、set数据结构、三元运算)
iter()
,根据可迭代对象返回一个迭代器对象map()
:将一个函数映射到输入列表的所有元素上;大多数情况下,使用lambda(匿名函数)来配合map
语法:map(function, iterable, ...)
参数:
function - 函数
iterable - 一个或多个序列
返回值:
python2.x:返回列表
python3.x:返回迭代器
def square(x) : # 计算平方数
return x ** 2
a = map(square, [1,2,3,4,5]) # 计算列表各个元素的平方
# python2.x 结果:a = [1,4,9,16,25]
# python3.x 结果:a = <map object at 0x000001929B8E9FA0>
print(list(a)) # [1,4,9,16,25] python3.x 可以将map转换为list
filter()
:过滤器;创建一个列表,其中每个元素都是对一个函数能返回true。
语法:filter(function, iterable)
function是判断函数,返回true or false;iterable是可迭代对象,可以是列表、元组、集合、字典等等
def judge(x):
if x > 5:
return True
else:
return False
ll = [2,5,7,8,9,10]
b = filter(judge, ll)
c = list(b)
print(b, c)
# b = <filter object at 0x0000020F36779FA0>
# c = [7, 8, 9, 10]
'''lambda'''
d = filter(lambda x: x>5, ll)
print(list(d)) # [7, 8, 9, 10]
map和filter可以被 列表、字典、元组推导式代替
- 集合
set()
set()是一个无序且不重复的元素序列
可以使用大括号{}或者 set() 函数创建集合,但创建一个空集合必须用set() 而不是{},因为 {}是用来创建一个空字典的。
交集:set1.intersection(set2)
差集:set1.difference(set2)
s1 = set([1,2,3,4,5])
s2 = {2,3,4,6,7}
print(s1.difference(s2)) # {1, 5}
print(s1.intersection(s2)) # {2, 3, 4}
- 三元运算符
在python中称为 条件表达式
result = condition_is_true if condition else condition_is_false
如果condition为真,result = condition_is_true;如果condition为假,result = condition_is_false
装饰器 (decorators)
- 装饰器是一个接受函数作为参数的闭包函数;是被修饰函数的功能的扩展,但不改变被装饰函数的源代码;在被修饰函数的前、后去执行代码。
- 装饰器本质上是一个函数,使用 @ + 函数名 实现绑定给函数的第二个功能 。
被装饰的函数作为参数传入装饰器函数,最后返回一个新的函数
“函数中定义函数”、“函数中返回函数”、“函数作为另一个函数的参数”
闭包函数: 一个函数的返回值是另外一个函数,返回的函数调用父函数内部的变量,如果返回的函数在外部被执行,就产生了闭包。
闭包函数的作用:使函数外部能够调用函数内部的属性和方法
闭包函数的优缺点:
优点:使函数外部能够调用函数内部的属性和方法
缺点:闭包操作导致整个函数的内部环境被长久保存,占用大量内存
wraps装饰器
@wraps 接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等功能;可以让我们在装饰器里面访问在装饰之前的函数的属性和方法。
wraps
装饰器本身也是一个装饰器,它接受一个函数作为参数,并返回一个包装函数。wraps 装饰器会将包装函数的名称、文档字符串、参数列表等元数据信息更新为原函数的元数据信息,从而确保被修饰的函数可以正确地保留原有的元数据信息。
使用方法:导入 from functools import wraps
在装饰器的外部与内部函数之间定义 @wraps(func)
装饰器使用场景
插入日志、性能测试、事务处理、缓存、权限校验
无参装饰器实现
装饰器的简易实现
import time
# 第一种:直接使用函数对象来实现
print(time.time()) # 1685333745.048791 时间戳:它是从1970.01.01 开始,到当前时间的秒数
def index():
time.sleep(3) # 2.调用 time.sleep(3) 函数,暂停程序的执行 3 秒钟。
print('Welcome to the index page') # 3.打印一条欢迎消息 "Welcome to the index page"。
return 200 # 4.返回状态码 200。
res = index # 1.执行 index() 函数。
# 5.函数执行完毕,程序继续往下执行。
start_time = time.time() # 函数未执行前起始计算时间
res() # 函数执行
stop_time = time.time() # 函数执行之后结束时间
print('run time is %s' % (stop_time - start_time))
#################################################################
# 第二种:使用参数的形式传入
def wrapper(func): # 通过函数接收外部的值
start_time = time.time()
func()
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
wrapper(index) # 如果考虑到还有可能要统计其他函数的执行时间就在下方继续wrapper(其他函数)
"""但是使用参数的话就改变了调用方式,违反了不能修改被装饰对象调用方式的原则,所以我们使用闭包函数,将值报给函数"""
# 第三种:使用闭包函数的形式
def timer(func):
def wrapper():
start_time = time.time()
res = func()
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
return res
return wrapper
asd = timer(index)
asd()
# 第四种:装饰器的进阶版本解决参数问题
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
return res
return wrapper
使用语法糖实现
import time
def timer(func): #定义一个名为 timer() 的装饰器函数,它接受一个参数 func这个 func 就是即将要被修饰的函数
def wrapper(*args, **kwargs): #在 timer() 函数内部,定义一个名为 wrapper() 的闭包函数
start_time = time.time() #在调用 wrapper() 函数时,它会根据传入的参数来调用被修饰的函数 func,并在函数执行前后记录时间
res = func(*args, **kwargs)
stop_time = time.time()
print('run time is %s' % (stop_time - start_time))
return res #同时输出函数执行时间。最后,它将 func 函数的返回值返回
return wrapper
@timer #使用装饰器语法来对函数进行装饰,在函数定义前加上 @timer 这样的语句
def index():
time.sleep(1)
print('Welcome to the index page')
return 200
#index()#调用 index() 函数时,会自动将其转换成 timer(index)() 这样的形式,将 index 函数作为参数传递给 timer() 函数,并将返回值再次作为函数调用。由于 timer() 函数返回了一个闭包函数 wrapper(),所以最终的函数调用结果就是执行了闭包函数 wrapper()
装饰器模板
语法糖的书写规范:紧贴在被装饰对象的上方书写,语法糖与被装饰对象之间不可以有代码
语法糖的内部原理:语法糖会自动把被装饰对象的名字当成参数传给装饰器函数调用
#定义一个装饰器函数,它接受一个函数作为参数并返回一个新的函数
def decorator(func):
# 定义一个内部函数,它接受任意数量和类型的位置参数和关键字参数
def wrapper(*args, **kwargs):
# 在调用被装饰函数之前执行的代码
print("Before calling the function")
# 调用被装饰函数,并保存其返回值
result = func(*args, **kwargs)
# 在调用被装饰函数之后执行的代码
print("After calling the function")
# 返回被装饰函数的返回值
return result
# 返回内部函数,以便它可以被调用
return wrapper
# 定义一个函数,并使用装饰器来增加其功能
@decorator #my_function = decorator(my_function)my_function是decorator函数的返回值(内部函数的函数名)
def my_function(a, b):
# 模拟一个运算,并返回结果
result = a + b
return result
# 调用被装饰函数
print(my_function(1, 2))
'''
执行结果:当调用 my_function(1, 2) 时,输出结果如下:
Before calling the function
After calling the function
3
'''
双层语法糖
import time
def login_auth(func):
def auth(*args, **kwargs):
username = input('username:').strip()
password = input('password:').strip()
if username == 'kevin' and password == '123':
print('登录成功')
res = func(*args, **kwargs)
return res
else:
print('用户名密码错误')
return auth
def all_time(func1):
def time1(*args, **kwargs):
start_time = time.time()
res = func1(*args, **kwargs)
stop_time = time.time()
print('功能执行了%s秒' % (stop_time - start_time))
return res
return time1
@all_time # index = all_time(all_time内部的 time1 的函数名) 当装饰器执行到最后一层这个变量就使用跟最初装饰器同名的函数名同名
@login_auth # all_time内部的 time1 的函数名 = login_auth(index) 语法糖会把被装饰对象的函数名当成第一个参数传给装饰器的第一个参数
def index():
print('执行完成')
index() # 最后一层就是 auth()
多层语法糖
def outter1(func1):
print('加载了outter1')
def wrapper1(*args, **kwargs):
print('执行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2):
print('加载了outter2')
def wrapper2(*args, **kwargs):
print('执行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3):
print('加载了outter3')
def wrapper3(*args, **kwargs):
print('执行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
@outter1 # index(outter1的内层函数名,一般最后一层我们使用跟功能函数同名的函数名 index) = outter1(wrapper2) ---------------wrapper1
@outter2 # wrapper2(outter2的内层函数名) = outter2(wrapper3)
@outter3 # wrapper3(outter3的内层函数名) = outter3(index)
"""
只要加了语法糖,装饰器一定会执行,不调用被装饰的函数也会执行装饰器;
执行装饰器是从下往上执行,执行到最后一个装饰器之后,会把最后一个装饰器的返回值的变量名与被装饰对象的函数名同名;
语法糖在没有调用装饰对象之前,是不会执行内部函数的
"""
def index():
print('from index')
index()#只要不调用被装饰的函数就不会执行装饰器内层函数
'''
执行顺序分析:
加载了outter3
加载了outter2
加载了outter1
执行了wrapper1
执行了wrapper2
执行了wrapper3
from index
执行顺序原理解释如下:
1.定义 outter3 函数时,先打印出 "加载了outter3";
2.进入 outter2 函数,先打印出 "加载了outter2";
3.在 outter2 函数中创建了 wrapper2 函数并返回,但并不执行 wrapper2 函数,此时将 wrapper2 函数作为参数传递给 @outter1 装饰器;
4.进入 outter1 函数,先打印出 "加载了outter1";
5.在 outter1 中将 wrapper2 函数作为参数传递给 outter1 的内部函数 wrapper1,并返回 wrapper1 函数,此时 index 函数被全局变量所指向;
6.当调用 index() 函数时,实际上是调用了经过装饰器增强后的 wrapper1 函数;
7.调用 wrapper1 函数,首先打印出 "执行了wrapper1",然后调用 wrapper2 函数;
8.调用 wrapper2 函数,首先打印出 "执行了wrapper2",然后调用 wrapper3 函数;
9.调用 wrapper3 函数,首先打印出 "执行了wrapper3",然后执行函数体中的 print('from index') 语句;
最终输出函数的执行结果 "from index"。
'''
有参装饰器的实现
- 有参装饰器本质上就是在普通装饰器头上再嵌套一层函数用来传递参数
- 当我们使用最外层的函数传递参数时,语法糖后面的括号中也需要传递相应的参数
#定义一个带参数的装饰器函数 repeat(),它接受一个整数类型的参数 count,表示被修饰函数需要重复执行的次数:
def repeat(count):
def decorator_func(func):
def wrapper_func(*args, **kwargs):
for i in range(count):
res = func(*args, **kwargs)
return res
return wrapper_func
return decorator_func
"""
在repeat()函数内部,定义了一个闭包函数decorator_func(),用于接受被修饰函数 func。在decorator_func()函数内部,再定义了一个闭包函数wrapper_func(),用于实现装饰器的具体功能。
在wrapper_func()函数内部,使用一个循环来控制被修饰函数的执行次数。在每次循环中,尝试执行被修饰函数,并返回其结果。最终,将 wrapper_func()函数作为修饰器的返回值。
"""
@repeat(3)
def greet(name):
print("Hello, %s!" % name)
"""
当执行 greet('Alice') 函数时,它实际上已经被修饰成了 repeat(3)(greet)('Alice') 这样的形式,即先将 greet 函数传递给 decorator_func(),再将返回的 wrapper_func 函数作为结果返回,并在其基础上执行函数 greet('Alice')。
由于装饰器 repeat() 是一个带参数的装饰器,所以要在装饰器名称后面加上括号并传入参数值,来指定装饰器的参数值。在这个例子中,使用 @repeat(3)来指定函数 greet()需要重复执行3次。
"""
res1 = greet
res()
Hello, Alice!
Hello, Alice!
Hello, Alice!
__slots__
魔法
默认情况下,python用一个字典来保存一个对象的实例属性。
这对已知属性的小类来说,可能是一个瓶颈,因为字典浪费了很多内存。
python不能再对象创建时直接分配一个固定量的内存来保存所有的属性;__slots__
方法告诉python不要使用字典,而且只给一个固定集合的属性分配空间。
容器(collections)
defaultdict
、counter
、deque
、namedtuple
、enum.Enum
(python3.4+)
-
defaultdict与dict的不同点:
- defaultdict会为不存在的key生成默认值;
- dict遇到key不在时,会触发keyError异常
-
deque提供了一个双端队列,可以从头、尾两端添加、删除元素表
pop()
、append()
、extend()
、insert()
、appendleft()
、extendleft()
、popleft()
-
namedtuple (命名元组)
- 像字典一样访问namedtuple
- namedtuple是不可变的
- 有两个必须的参数:元组名称、字段名称
- 不必用整数索引来访问
- namedtuple的每个实例没有对象字典
- 比普通元组节省内存,比字典更快
collections.namedtuple(typename, field_names, verbose=False, rename=False)
枚举(Enumerate)
对象自省
推导式(Comprehension)
异常
lambda表达式
一行式
for - else
open函数
协程
函数缓存
上下文管理器
Tip: 先记录这些,有时间陆续更新。。。