【python高阶语法知识】
接上篇文章~
一、闭包
使用外部函数变量的内部函数被称为闭包。闭包可以保存函数内的变量,而不会随着调用完函数而被销毁
1、闭包的三个条件:
- 有嵌套:函数嵌套函数
- 有引用:内部函数使用外部函数的变量
- 有返回:外部函数返回了内部函数名
闭包的简单示例
def emp_salary(salary):
def reality_salary(performence):
print(f"员工实际薪资是:{salary + performence}")
return reality_salary
if __name__ == '__main__':
employ_salary_fun = emp_salary(10000) # 外层函数走完之后,他的参数10000没有消失,被内部函数包裹使用了 提高了局部变量的存活时间,让数据更加安全
employ_salary_fun(500)
employ_salary_fun(-500)
2、nonlocal关键字
声明能够让内部函数去修改外部函数的变量,外部函数变量
作为自由变量与闭包函数绑定
def emp_salary(salary):
def reality_salary(add_salary, performence):
nonlocal salary
salary += add_salary
print(f"员工实际薪资是:{salary + performence}")
return reality_salary
if __name__ == '__main__':
employ_salary_fun = emp_salary(10000)
employ_salary_fun(1000, 500)
employ_salary_fun(2000, -500)
这里对变量作用域做一个补充:(Python中变量查找的逻辑)
- 如果是 global x 声明,则 x 来自模块全局作用域,并赋予那个
作用域中 x 的值。- 如果是 nonlocal x 声明,则 x 来自最近一个定义它的外层函
数,并赋予那个函数中局部变量 x 的值。- 如果 x 是参数,或者在函数主体中赋了值,那么 x 就是局部变
量。- 如果引用了 x,但是没有赋值也不是参数,则遵循以下规则。
在外层函数主体的局部作用域(非局部作用域)内查找 x。
如果在外层作用域内未找到,则从模块全局作用域内读取。
如果在模块全局作用域内未找到,则从__builtins__.dict 中读取。
二、装饰器
以
@XXX形式出现。装饰器本质上就是一个闭包函数,它可以在不改变原函数的基础上,给原函数增加额外的功能。
1、装饰器的构成条件:
- 有嵌套:在函数嵌套(函数里面再定义函数)的前提下
- 有引用:内部函数使用了外部函数的变量(还包括外部函数的参数)
- 有返回:外部函数返回了内部函数名
- 有额外功能:给需要装饰的原有函数增加额外功能。
这是装饰器的典型行为:把被装饰的函数替换成新函数,新函数接受的
参数与被装饰的函数一样,而且(通常)会返回被装饰的函数本该返回
的值,同时还会做一些额外操作。
2、装饰器的语法:
方式1: 传统方式:
变量名 = 装饰器名(原有函数名)
变量名()
方式2: 语法糖:
@装饰器名 (这种方式最常见也最常用)
def outer(fun):
def inner():
print("需要先登录!")
fun()
print("评论完成!")
return inner
@outer
def comment():
print("评论中~~~")
if __name__ == '__main__':
# fun1 = outer(comment)
# fun1()
comment()
3、装饰器的使用
1.修饰无参无返回值的函数
def decorate(func):
def inner():
print('无参数')
return inner
@decorate
def none_param():
print("无参数")
if __name__ == '__main__':
none_param()
2.修饰有参无返回值的函数
def decorate(func):
def inner(a):
func(a)
print(f'有参数{a}')
return inner
@decorate
def none_param(a):
print("有参数")
if __name__ == '__main__':
none_param(123)
3.修饰无参有返回值的函数
if __name__ == '__main__':
none_param(123)
def decorate(func):
def inner():
print(f'无参数有返回')
a = func()
return a
return inner
@decorate
def none_param():
print("午餐有返回")
a = 1
return a
if __name__ == '__main__':
print(none_param())
4.修饰有参有返回值的函数
def decorate(func):
def inner(a, b):
print(f'有参数有返回')
func(a, b)
return func(a, b)
return inner
@decorate
def none_param(a, b):
print("有参有返回")
return a + b
if __name__ == '__main__':
print(none_param(7, 3))
5.修饰另外还包括不定长参数的
def decorate(func):
def inner(*args, **kwargs):
print("计算中~~~")
res = func(*args, **kwargs)
return res
return inner
@decorate
def add(*args, **kwargs):
res = 0
for i in args:
res += i
for v in kwargs.values():
res += v
return res
if __name__ == '__main__':
print(add(1, 2, 3, 4, a=1, b=2, c=3))
一句话装饰器的内部函数参数和返回值和原函数保持一致
4、多个装饰器装饰同一函数
多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程(解析),由外向内的执行。
#1.定义装饰器1
def check_user(fn):
def inner():
print("输入用户名和密码")
fn()
return inner
#2.定义装饰器2
def check_code(fn):
def inner():
print("登录验证码")
fn()
return inner
# 3. 被装饰器的函数
@check_user
@check_code
def comment():
print("发表评论")
# 4.调用函数
comment()
如果想理解叠放装饰器,那么需要记住一点:@ 是一种语法糖,其
作用是把装饰器函数应用到下方的函数上。多个装饰器的行为就像
调用嵌套函数一样。
@alpha
@beta
def my_fn():
...
# 等同于
my_fn = alpha(beta(my_fn))
5、带参数的装饰器
使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数(装饰器工厂函数),使用该函数接收参数,返回装饰器。
解析源码中的装饰器时,Python 会把被装饰的函数作为第一个参数传给装饰器函数。
#1.定义装饰器
def logging(flag): # flag = "+"
# 1.1 外部函数
def decorator(fn):
# 1.2内部函数
def inner(num1, num2):
# 判断流程
if flag == "+":
print("--正在努力加法计算--")
elif flag == "-":
print("--正在努力减法计算--")
result = fn(num1, num2)
return result
# 返回内部函数
return inner
# 返回装饰器
return decorator
# 2.被带有参数的装饰器装饰的函数
@logging('+')
def add(a, b):
result = a + b
return result
@logging('-')
def reduce(a, b):
result = a - b
return result
# 3.执行函数
result = add(1, 3)
print(result)
三、深浅拷贝
1、浅拷贝:copy()
首先先讲讲直接赋值的方式:直接赋值变量指向的是相同的内存地址
list1 = [1, 2, 3]
list3 = list1
print(id(list1)) # 2985468620864
print(id(list3)) # 2985468620864
a = (1, 2, 3)
c = a
print(id(a)) # 2985465760576
print(id(c)) # 2985465760576
1.1、不可变类型浅拷贝
不可变类型的浅拷贝如同直接赋值,他不会开辟新的空间存放数据的地址。
a = (1, 2, 3)
b = copy.copy(a)
c = a
print(id(a)) # 2985465760576
print(id(b)) # 2985465760576
print(id(c)) # 2985465760576
1.2、可变类型浅拷贝
可变类型的浅拷贝会重新开辟一个空间,用于存放可变类型的地址,因此这拷贝前后的对象空间是不同的。
此外,浅拷贝对于可变类型是会开辟新空间用于存放地址,但是当可变类型嵌套了可变类型的话,内层的可变类型内存地址并不会拷贝
import copy
# 可变类型 --- 浅拷贝会开辟新的空间(会拷贝一份新内容) 但是深层的容器就不会再次开辟空间了
list1 = [1,2,3]
list2 = copy.copy(list1)
print(id(list1)) # 2585880351040
print(id(list2)) # 2585880352896
print("- "*20)
# 深层的容器就不会再次开辟空间了
mylist = [[1,2,3],[2,3,4]]
mylist2 = copy.copy(mylist)
print(id(mylist)) # 2585880351040
print(id(mylist2)) # 2585880352896
print(id(mylist[0])) # 2895839554304
print(id(mylist2[0])) # 2895839554304
2、深拷贝:deepcopy()
和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。深拷贝出来的对象是一个全新的对象,不再与原来的对象有任何关联。所以改变原有被复制对象不会对已经复制出来的新对象产生影响。
不可变类型深拷贝:不可变类型进行深拷贝不会给拷贝的对象开辟新的内存空间,而只是拷贝了这个对象的引用。
a = (1, 2, 3)
b = copy.deepcopy(a)
print(id(a)) # 2258798857024
print(id(b)) # 2258798857024
可变类型深拷贝:拷贝所有,所有都开辟新内存空间
list1 = [1, 2, 3]
list2 = copy.deepcopy(list1)
print(id(list1)) # 1700910923264
print(id(list2)) # 1700910920768
mylist = [[1,2,3],[2,3,4]]
mylist2 = copy.deepcopy(mylist)
print(id(mylist)) # 2585880351040
print(id(mylist2)) # 2585880352896
print(id(mylist[0])) # 1983958543104
print(id(mylist2[0])) # 1983961386944
虽然不可变类型不会重新开辟空间,但是有个特例:
当不可变类型,如元组中包含可变类型时,由于深拷贝会重新为可变类型开辟空间,这也导致了外层的不可变类型强行的需要开辟空间。
# 不可变类型 嵌套可变类型 --- 深拷贝会开辟新的空间(会拷贝一份新内容)并且 容器中的容器也会单独开辟空间
t1 = (1,2,[1,2,3])
t2 = copy.deepcopy(t1)
print(id(t1)) # 1691062333952
print(id(t2)) # 1691062334016
print(id(t1[2])) # 1955058503616
print(id(t2[2])) # 1955112096064
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)