1、闭包
闭包Closure特点:
[闭包函数的本质其实是一个嵌套函数],
[内部函数对外部函数作用域里变量的引用]
外层函数为内层函数提供了一个运行环境, [内层函数被当成返回值返回给外层函数]
优点:加强封装、避免全局变量污染、[变量长期存储用于缓存]
缺点:内存消耗大、内存泄露(避免滥用)
使用场景:变量活下来、装饰器
举例1-1:
def outer(x):
def inner(y):
'''引用外部函数作用域变量x'''
return x+y
return inner
var=outer(2)
print(var(3))
print(outer.__closure__)#outer不是闭包 None
print(var.__closure__)#inner是闭包
for i in var.__closure__:
print('closure',i.cell_contents)#x=2 保留了外部函数的x值,让它活了下来
del var
print(var(3))#报错应,del了
举例1-2:
def func(obj):
def wrapper():
obj[0]+=1
#print('wrapper:',obj)
return obj
return wrapper
mylist=[1,2,3]
var=func(mylist)
var() #[2,2,3]
var() #[3,2,3]
举例2:修改闭包的局部变量 =====nonlocal
实现计数器功能temp=0
def outer(arg):
temp = 10
def inner():
nonlocal temp
_sum = temp + arg
temp += 1 #在内函数中尝试改变temp的值 nonlocal
return _sum
return inner
var=outer(5)
print(var())
print(var())
======================其他global
def outer1():
temp = 10
def inner1():
nonlocal temp
global arg
_sum = temp + arg
temp += 1 #在内函数中尝试改变temp的值 nonlocal
arg+=10
return _sum
return inner1
arg=0
var=outer1()
print(var())#agr=0 temp=10 10
print(var())#agr=10 temp=11 21
print(var())#agr=20 temp=12 32
==============局部、全局、nonlocal变量 区别=============
def scope_test():
def do_local():
spam = "local spam"
def do_nonlocal():
nonlocal spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "new spam"
# do_local()#局部变量已死 不影响
# print("After local assignmane:", spam)#new spam
# do_nonlocal()#nonlocal修改了非全局变量spam
# print("After nonlocal assignment:",spam)#nonlocal spam
# do_global()#修改全局变量spam为global,但是你在函数里打印的还是非全局变量
# print("After global assignment:",spam)#nonlocal spam
举例3:闭包陷阱 循环
不要返回任何循环变量,或者后续会发生变化的变量
def count():
fs = []
for i in range(1, 4):
def func():
return i*i
fs.append(func)
return fs
f1, f2, f3 = count()
print(f1())#9
print(f2())#9
print(f3())#9
一定要利用循环,实现1平方 2平方 3平方:
def count():#再嵌套1层
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被执行,因此i的当前值被传入f()
return fs
f1,f2,f3=count()
print(f1())
print(f2())
print(f3())
2、装饰器
【装饰器】:不改变函数/类原有功能的情况下,给它添加新功能。
比如:第三方接口(不允许改动),但需要优化:给他加装饰器添加功能。
装饰器本身是个闭包,将前面的闭包 变量改成函数名func。
使用场景:log打印、性能测试(统计每个程序的运行时间、调用了多少接口)、权限校验
分类:普通装饰器、带参数装饰器
被装饰函数带参数、不带参数
装饰器demo样式:add函数添加新功能decorator
@decorator
def add(a,b):
return a+b
============================================================
举例1:(普通装饰器-被装饰函数不带参数)
外层参数 函数名func=被装饰函数函数名mysum
里层参数 没有
里层return func() 被装饰函数调用
def decorator(func):
def wrapper():
print(f"调用的被装饰函数方法:{func.__name__}")
return func()
return wrapper
@decorator
def myprint():
print('print ok')
myprint()
============================================================
举例2:(普通装饰器-被装饰函数带参数)
外层参数 函数名func=被装饰函数函数名mysum
里层参数 被装饰函数参数 a,b
里层return func() 被装饰函数调用
[不改变函数原有功能,对入参做了变换]
def mysum(a,b):
return a+b
print(mysum(1,2))
'''下面改成平方和'''
def decorator(func):
def wrapper(a,b):
a=a**2
b=b**2
return func(a,b)
return wrapper
@decorator
def mysum(a,b):
return a+b
print(mysum(1,2))#装饰器调用or
print(decorator(mysum)(1,2))#非装饰器调用,注释@decorator。
补充*args **kwargs知识:
def func(*args,**kwargs):
print(args,'\n',kwargs)
func(1,2,name='nancy',age='20')#(1,2) {'name': 'nancy', 'age': '20'}
普通装饰器通用格式:
import time
def decorator(func):
def wrapper(*args,**kwargs):
fn=func(*args,**kwargs)
return fn
return wrapper
@decorator
def add(a,b):
return a+b
print(add(2,3))
===========================================================
举例3:装饰器带参数,外面再包一层
def arg_func(sex):#多包一层接受装饰器参数
def outer(func):
def inner(*args,**kwargs):
if sex=='woman':
print("你可以生娃")
elif sex=='man':
print("你不可以生娃")
return func(*args,**kwargs)
return inner
return outer #多包一层 return outer
@arg_func(sex='man')
def man():
print("好好上班")
@arg_func(sex='woman')
def woman():
print("好好上班")
@arg_func(sex=None)
def add(a,b):
print(a+b)
man()
woman()
add(3,4)
============装饰器修改被装饰函数属性问题 ====functools.warps函数===================
举例4:函数属性问题
因为使用装饰器, 原函数(即被装饰函数)的地址指向了装饰器所定义的内部inner函数
可以借助functools.warps函数, 避免属性篡改问题
from functools import wraps
def mylog(func):
@wraps(func)#如果注释 {add.__name__}=inner, 加上是add.
def inner(*args, **kwargs):
res = func(*args, **kwargs)
print(f"执行结果为: {res}")
return res
return inner
@mylog
def add(a, b):
return a + b
print(f"add函数的名字: {add.__name__}")
print(f"add函数的注释信息: {add.__doc__}")
add(1, 2)
===========================================================
举例5:多个装饰器 执行顺序:有里到外
from functools import wraps
def mylog1(func):
@wraps(func)#里面那层一定要加wrapps
def inner(*args, **kwargs):
res = func(*args, **kwargs)
print(f"执行结果为: {res}")
return res
return inner
def mylog2(level="INFO"):
def outter(func):
@wraps(func)
def inner(*args, **kwargs):
res = func(*args, **kwargs)
print(f"[{level}]函数{func.__name__}的执行结果为: {res}")
return res
return inner
return outter
@mylog2(level="DEBUG")
@mylog1
def add(a, b):
return a + b
add(1, 2)
#mylog2(level="ERROR")(mylog1(add))(1, 2)#注释掉装饰@@
===========================================================
多装饰器执行顺序
from functools import wraps
def login(func):
print("login")
@wraps(func)
def wrapper(*args,**kwargs):
print("登录前代码")
fn=func(*args,**kwargs)
print("登录后代码")
return fn
return wrapper
def authority(func):
print("authority")
@wraps(func)
def wrapper(*args,**kwargs):
print("鉴权前代码")
fn=func(*args,**kwargs)
print("鉴权后代码")
return fn
return wrapper
# @authority
# @login
def buy(a,b):
'''装饰器:鉴权,判断是否登录'''
return a+b
buy=authority(login(buy))
print(buy(2,4))#先执行login装饰器再执行authority装饰器,先调用authority的wrapper,再执行login的wrapper
===========================================================
>>>>>>>>>>>>>>>>>常见使用场景 举一反三<<<<<<<<<<<<<<<<<
举例:[log打印]
略 举例5
举例:[每个程序运行时间统计]
import time
def runtime(func):
def inner():
start = time.time()
func()
end = time.time()
print('[{}]函数执行用时:{}秒'.format(func.__name__,(end-start)))
return inner
@runtime
def case():
time.sleep(2)
case()
举例:[调用接口数统计]
举例3:被调用次数
def counter(func):
count=0
def wrapper(*args,**kwargs):
nonlocal count # nonlocal改变了外部(非全局)变量的值
count+=1
print(f"函数{func.__name__}被调用了{count}次")
return func(*args,**kwargs)
return wrapper
def add(a,b):
return a+b
def say_hello():
return "hello"
sum=counter(add)
hello=counter(say_hello)
hello()
hello()
sum(1,2)
sum(2,3)
举例:类装饰器__call__ 类装饰器:带/不带参数
A-函数装饰器装饰函数
B-函数装饰器装饰类
C-类装饰器装饰函数
D-类装饰器装饰类
总结:函数作为装饰器,可以装饰函数、可以装饰类;
类作为装饰器, 可以装饰函数、可以装饰类。
#####################################
'''1、函数-装饰函数'''
import time
from functools import wraps
def timer(fun):
@wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res=fun(*args,**kwargs)
end_time = time.time()
print("函数运行时间为:{}".format(end_time-start_time))
return res
return wrapper
@timer
def testfun(a,b):
time.sleep(2)
return a+b
print(testfun.__name__,testfun(1,2))
'''2、函数-装饰类'''
import time
from functools import wraps
def timer2(fun):
@wraps(fun)
def wrapper(*args,**kwargs):
start_time = time.time()
res=fun(*args,**kwargs)
end_time = time.time()
print("函数运行时间为:{}".format(end_time-start_time))
return res
return wrapper
class ObjFun():
@timer2
def testfun(self,a,b):
time.sleep(2)
return a+b
@timer2
class ObjFun2():
def testfun(self):
pass
o=ObjFun()
print(ObjFun.testfun.__name__,o.testfun(2,5))
oo=ObjFun2()
print(ObjFun2.__name__)
import time
from functools import wraps
class Timer():
def __init__(self,func):
self.func=func
def __call__(self, *args, **kwargs):
start=time.time()
res=self.func(*args,**kwargs)
end = time.time()
print(f'函数{self.func.__name__}执行耗时:{(end-start)}')
return res
@Timer
def testfun(a,b):
time.sleep(1)
return a+b
print(testfun(11,22))
'''3、类函数(不带参数)-装饰函数'''
import time
from functools import wraps
class Timer():
def __init__(self,func):
self.func=func
def __call__(self, *args, **kwargs):
start=time.time()
res=self.func(*args,**kwargs)
end = time.time()
print(f'函数{self.func.__name__}执行耗时:{(end-start)}')
return res
@Timer
def testfun(a,b):
time.sleep(1)
return a+b
# print(testfun(11,22))
'''4、类函数(带参数)-装饰类'''
class Timer2():
def __init__(self,coder):
self.coder=coder
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
start=time.time()
res=func(*args,**kwargs)
end = time.time()
print(f'方法{func.__name__}执行耗时:{(end-start)}',self.coder)
return res
return wrapper
class ObjFun2():
@Timer2(coder='12345')
def testfun2(self,a,b):
time.sleep(1)
return a+b
print(ObjFun2().testfun2(11,22))
print(ObjFun2().testfun2.__name__)
#####################################
类装饰-1:
import time
class timer():
'''类装饰器和函数装饰器实现的效果是一样的,只不过类装饰器在内部的装饰函数是用__call__ 方法实现的'''
def __init__(self, func):
self.func = func
def __call__(self):
'''__call__作用:能够使类的实例像函数调用那样被调用'''
star_time = time.time()
self.func()
end_time = time.time()
total = end_time - star_time
print("函数运行时间:", total)
@timer
def fun():
time.sleep(2)
fun()
类装饰-2:
from functools import wraps
class Hint():
'''类装饰器 不带参数'''
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('{} is running.'.format(self.func.__name__))
return self.func(*args, **kwargs)
class Hint2():
'''类装饰器 带参数'''
def __init__(self, coder=None):
self.coder = coder
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
print('{} is running..Coder: {}'.format((func.__name__),self.coder))
return func(*args, **kwargs)
return wrapper
@Hint
@Hint2('12345')
def add(a,b):
return a+b
print(add(1,2))
###############################################
何时执行装饰器:
被装饰函数和装饰器在同一个模块时,只有在明确调用被装饰函数时装饰器才被执行。
当被装饰函数和装饰器在不同的模块时,只要被装饰函数一经定义,装饰器就会立即执行,这一般在import导入时发生。
import time
def timer(fun):
print("timer-out")
def wrapper():
start_time = time.time()
fun()
end_time = time.time()
print("函数运行时间为:{}".format(end_time-start_time))
return wrapper
@timer
def fun():
time.sleep(1)
fun()#打印*1+运行时间 注释后打印*1
改成跨目录:
from temp import timer
import time
@timer
def fun():
time.sleep(1)
# fun()#打印*2+运行时间 注释后打印*2
###############################################
#装饰器在自动化测试中运用:
log、登录token鉴权
举例:[权限校验] 暂略?
3、内置装饰器
-@property
实现功能: 为类中的属性(私有的属性)提供设置和获取的方法(公有的方法)
@property最大的好处就是在类中把一个方法变成属性调用.
python类没有真正意义的私有属性:
一种用单下划线,表示这个属性是类的私有属性,不希望被外部访问到,但还是可以被访问的;
第二种是双下划线,表示这个属性就是类的私有属性,只能在类中被使用,不可以在实例化的对象中去使用。但可以通过对象名._类名__xxx的特殊方式访问。所以@property运营而生。
class person():
def __init__(self,name,age,sex):
self.name=name #共有
self._age=age #表示私有成员
self.__sex=sex #私有成员
def __private(self):
print(self.name)
def public(self):
print(self.name)
p=person('nancy',20,'female')
print(p.name,p._age,p._person__sex)#都可以实现访问、篡改
p.public()
p._person__private()
========================保证私有属性只读 不可篡改========
class Person():
def __init__(self, name, age=18):
self.name = name
self.__age = age#私有属性
@property
def age(self):
#属性只读
return self.__age
xm = Person('xiaoming')
print(xm.name,xm.age)
# xm.age = 14 # 报错无法给年龄赋值
xm.name='xiaoli'
print(xm.name,xm.age)
=====================类属性 预处理=====================
class User():
def __init__(self, name, age):
self.name = name
self._age = age
# print(self.tag,self.age)
@property
def tag(self):
return self.name + str(self._age)
@property
def age(self):
return self._age + 5
user = User('xiao',5)
print(user.tag)
print(user.age)
===========================类属性 预处理前(+5)==============================
class User():
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):#类属性 预处理
return self._age
@age.setter
def age(self,n):#预处理前 操作+5
self._age = n + 5
return self._age
user = User('xiao',0)#
user.age = 1#n
print(user.age)#6
=======================综合:明文输入 明文输出=================================
#用户输入明文->转化为密文后存入->用户读取时先转化为明文再输出
class User():
def __init__(self):
self._password = '' # 密文存储
@property
def password(self):
'''2、预处理操作'''
return self._password.lower() # 解密
@password.setter
def password(self,word):
'''1、入参,预处理前 操作'''
self._password = word.upper() # 加密
user = User()
user.password = 'abc' #0、明文输入
print(user.password) #3、明文输出
==================动态属性统一调用方式======================
1、非动态属性
class Student:
def __init__(self,name):
self.name=name
self.score=None
def set_score(self,new_score):
if not isinstance(new_score,int):
raise ValueError("score must be int")
if new_score>=0 and new_score<=100:
self.score=new_score
return self.score
else:
raise ValueError("invald score")
mike=Student('mike')
mike.set_score(88)#[1、函数调用方法] set_score 限制分数输入范围
print(mike.name,mike.score)
mike.score=999#[2、实例属性调用方法] 还是有办法误输入999且调用方式不统一
print(mike.name,mike.score)
2、动态属性
class Student2:
def __init__(self,name):
self.name=name
self._score=None
@property
def myscore(self):
'''预处理操作'''
return self._score
@myscore.setter
def myscore(self,new_score):
'''预处理前操作'''
if not isinstance(new_score,int):
raise ValueError("score must be int")
if new_score>=0 and new_score<=100:
self._score=new_score
return self._score
else:
raise ValueError("invald score")
Nancy = Student2('nancy')
Nancy.myscore=81#统一了调用方式 (有点牵强?Nancy._score还是可以调用和修改值呀?)
print(Nancy.myscore)
==========================================================
私有属性访问:
1、类方法获取
2、装饰器的函数调用
3、语法糖 @装饰器
class Person():
def get_name(self):
return self._name
def set_name(self, name):
self._name = name
p = Person()
p.set_name("chinablue")#1、常规方法类方法实现私有属性的改变
print(p.get_name()) #获取私有属性
p.name = "djtest" #2、装饰器的函数调用
print(p.name)
class Person():
def __init__(self, name, age=18):
self.name = name
self.__age = age#私有属性
@property
def age(self):
#属性只读
return self.__age
xm = Person('xiaoming')
print(xm.name,xm.age)
# xm.age = 14 # 报错无法给年龄赋值
xm.name='xiaoli'
print(xm.name,xm.age)
=========================================================
python中,类变量又叫做类属性
然而类属性有分为3个不同的类型
类属性,实例属性,局部变量。
class a:
# 类属性
hobby = 'play sport'
def asdf(self):
#实例变量
self.age = 19
#局部变量
Name = 'Wu'
=======================***类属性 实例属性的 访问大全***==========
class Person:
'''类属性的 私有/共有 访问'''
country='China'
__identity='gcd'
def __init__(self,name,age):
'''实例属性的 私有/共有 访问'''
self.name=name
self.__age=age
self.sf='人'
def get_info(self):
print(f"姓名{self.name},年龄{self.__age},秘密身份{self.__class__.__identity},国籍{self.__class__.country}")
print(f"姓名{self.name},年龄{self.__age},秘密身份{Person.__identity},国籍{Person.country}")
print(f"姓名{self.name},年龄{self.__age},秘密身份{self._Person__identity},国籍{self.country}")
print(Person.country,Person._Person__identity)
Jack=Person('jack',20)
print(Jack.country,Jack._Person__identity)
print(Jack.name,Jack._Person__age)
Jack.get_info()
#print(student.__class__.country)#调用父类的 类属性(子obj.__class__.父类属性)
#print(self.__class__.country,self.country)#类里调用父类的 类属性
=========== 构造方法和实例方法 访问大全=====================
class Person:
'''私有 共有 方法的调用'''
def __init__(self, name, age):
self.name = name
self.age = age
self.sf='gcd'
def get_info(self):
print(f"public 姓名:{self.name},年龄:{self.age}")
def __get_private_info(self):
print(f"private 姓名:{self.name},年龄:{self.age}")
def get_un(self):
self.get_info()
self.__get_private_info()
# super()._Person__get_private()###############
def get_notice(self, notice):
return notice
class Student(Person):
'''包括 在继承里 方法调用
子类 无法调用私有属性和私有方法
'''
def __init__(self, name, age, sno):
# super().__init__(name,age)
# super(Student, self).__init__(name,age)
Person.__init__(self, name, age) # 调用父类init 3法
self.sno = sno
def s_get_un(self):
print(self.sf) # 继承父类的初始化属性
self.get_un()
print(self.get_notice('西施'))
# 调用父类方法的2种方式: 后面再说区别!!!
# super(子类, self).父类方法(参数)和父类.父类方法(self, 参数)'''
print(super(Student, self).get_notice('无名氏')) #print(super().get_notice('无名氏'))
print(Person.get_notice(self, '无名氏'))
# super().get_un()##########
super()._Person__get_private_info() # [强制调用父类私有方法]
Jack = Person('jack', 20)
Jack.get_info()
Jack._Person__get_private_info()#[强制调用父类私有方法]
Jack.get_un()
tom = Student('tom', 18, '07010801')
print(tom.name, tom.age, tom.sno)
print(Jack.name, Jack.age)
tom.s_get_un()
===================属性调用例2==============================
class Person:
country="China"
__identity="gcd"
def __init__(self,name,age):
self.name=name
self.__age=age
def print_attribute(self):
# 1、类内属性各种调用
print(self.name,self.__age)
print(Person.country,Person.__identity)
print(self.country,self.__identity)
print(self.__class__.country,self.__class__.__identity)
class Teacher(Person):
def __init__(self,name,age):
'''如果子类有构造函数 记得要初始化父类的属性,不然子类调用父类属性报错!!'''
super().__init__(name,age)
def print_attribute(self):
#3、类内-类内-调用父类属性
print(self.name)
print(self._Person__age)
print(self.country,self.__class__.country,super().country)
print(self._Person__identity)
p1=Person('ls',20)
p1.print_attribute()
#2、类外属性各种调用
print(p1.name,p1._Person__age)
print(Person.country,Person._Person__identity)
print(p1.country,p1._Person__identity)
jack=Teacher('jack',30)
jack.print_attribute()
#4、子类-类外-调用父类属性
print(jack.name,jack._Person__age)
print(jack.country,jack.__class__.country,jack._Person__identity)
========================__私有属性 类外可通过:对象名._类名__XXX访问,不建议====
一般私有属性规范访问方法:1、2、3
'''
①get( ) 和 set( )
②property 方法 (此方法需要借助于get、set方法)
③通过装饰器 装饰器:@property
'''
class Person():
__country="China"
def __init__(self,name):
self.__name=name
def get(self):
'''1-1、get获取私有属性'''
return self.__name
def set(self,name):
'''1-2、set重新赋值 get获取'''
self.__name=name
def __str__(self):
return "hello"
#2、借助property(方法1,方法2)
namer = property(get, set) # property里面放两个参数,get方法和set方法
#@property装饰器
@property
def updatename(self):
return self.__name
@updatename.setter
def myname(self,newname):
self.__name = newname
p1=Person('nancy')
# print(p1)
# print(p1._Person__name,p1._Person__country,Person._Person__country)#不建议
# p1.get()
# p1.set('helen')
# p1.get()
# print(p1.namer)#2-调用get方法 获取对象属性
# p1.namer="王五"#2-调用set方法 设置对象属性
# print(p1.namer)
#3、@property来调用和修改私有属性
p1.myname='jessica'
print(p1.myname)
==========================多继承看super()和父类.父类方法(self, 参数) 区别===============
super保证多继承的时候,公共父类只调用1次,mro顺序
待拓展:装饰器 鉴权举例
上下文管理器:with
上下文管理器介绍 TODO