1.闭包函数
闭包函数一般用于==解决某一个函数只能传入一个参数==的情况。
def funOut(num1):
def funIn(num2):
#内部函数修改外部函数的变量,需要声明 nonlocal(变量非内部的,也就是外部的意思)
nonlocal num1
num1 += 100
print(f'此时num1的值为:{num1},需要相加的num2的值为:{num2}')
return num2+num1
return funIn
f = funOut(100)
#调用以后外部变量num1,相当于静态变量,会一直保留他的值
print("两个数的和为:",f(200))#第一次调用的时候,外部变量num1为200,200+200=400
print("两个数的和为:",f(300))#第二次调用的时候,外部变量num1为300,300+300=600
#执行结果:
'''
此时num1的值为:200,需要相加的num2的值为:200
两个数的和为: 400
此时num1的值为:300,需要相加的num2的值为:300
两个数的和为: 600
'''
2.基本的装饰器
'''这个结构是装饰器的基本结构,基本函数具有参数的传入,具有返回值的输出'''
#装饰器该函数
def decor(func):
#定义一个内部函数
def inner(*args,**kwargs):
#增加功能1
print('6')
#基本函数
ret = func(*args,**kwargs)
#增加功能2
print('举高高')
return ret
#返回内部函数
return inner
#基本函数
@decor # 这里的@decor完全等同于 love=decor(love)
def love(*args,**kwargs):
print(f"{args}亲亲{kwargs}")
return '基本函数返回值'
print(love('爸爸','妈妈',son='儿子',girl='女儿'))
#执行结果:
'''
6
('爸爸', '妈妈')亲亲{'son': '儿子', 'girl': '女儿'}
举高高
基本函数返回值
'''
3.带有参数的装饰器
==实际上就是在装饰器外面又加了一个闭包函数==,因为装饰器的使用格式:“@装饰函数”是不能传入参数的。
'''实际上装饰器还是不带参数的,带参数的只是在装饰器的基本结构外加了一层。
实际就是根据传入的参数,返回一个装饰器'''
#调用outer返回装饰器
def outer(arg):
#装饰器该函数
def decor(func):
#定义一个内部函数
def inner(*args,**kwargs):
if arg == '抱举1次':
#增加功能1
print('抱抱')
#基本函数
ret = func(*args,**kwargs)
#增加功能2
print('举高高')
return ret
elif arg == '抱举3次':
#增加功能1
for i in range(3):
print('抱抱')
#基本函数
ret = func(*args,**kwargs)
#增加功能2
for i in range(3):
print('举高高')
return ret
#返回内部函数
return inner
#返回装饰器
return decor
#基本函数
@outer('抱举1次') # 这里的@outer('抱举1次') 就等于一个decor
def love(*args,**kwargs):
print(f"{args}亲亲{kwargs}")
return '基本函数返回值'
#基本函数
@outer('抱举3次') # 这里的@outer('抱举1次') 就等于一个decor
def love2(*args,**kwargs):
print(f"{args}亲亲{kwargs}")
return '基本函数返回值'
'''这样实现的效果就是,同一个装饰器,
因为传入参数不用,可以实现装饰不同的函数的效果'''
print(love('爸爸','妈妈',son='儿子',girl='女儿'))
print(love2('爸爸','妈妈',son='儿子',girl='女儿'))
#执行结果:
'''
抱抱
('爸爸', '妈妈')亲亲{'son': '儿子', 'girl': '女儿'}
举高高
基本函数返回值
抱抱
抱抱
抱抱
('爸爸', '妈妈')亲亲{'son': '儿子', 'girl': '女儿'}
举高高
举高高
举高高
基本函数返回值
'''
4.使用类做装饰器
'''
#调用outer返回装饰器
def outer(arg):
#装饰器该函数
def decor(func):
#定义一个内部函数
def inner(*args,**kwargs):
if arg == '':
#增加功能1
#基本函数
ret = func(*args,**kwargs)
#增加功能2
return ret
elif arg == '':
#增加功能1
#基本函数
ret = func(*args,**kwargs)
#增加功能2
return ret
#返回内部函数
return inner
#返回装饰器
return decor
首先看下这个结构,用类实现需要实现3个主要部分
1.可以带有区分或者使用的参数arg 这个用类的__init__实现
2.具有接受基本函数传入的参数func 这个用类的__call__实现
3.定义未来使用的函数,也就是内部函数inner
'''
class Outer:
def __init__(self,arg):
self.arg = arg
#一定要具有__call__魔术方法使得类的实例化对象,可以当作函数使用
def __call__(self, func):#这是装饰器的本体 相当于decor函数
self.func = func
#返回内部函数
return self.inner
#定义一个内部函数
def inner(self,*args,**kwargs):
if self.arg == '抱举1次':
# 增加功能1
print('抱抱')
# 基本函数
ret = self.func(*args, **kwargs)
# 增加功能2
print('举高高')
return ret
elif self.arg == '抱举3次':
# 增加功能1
for i in range(3):
print('抱抱')
# 基本函数
ret = self.func(*args, **kwargs)
# 增加功能2
for i in range(3):
print('举高高')
return ret
#基本函数
@Outer('抱举1次') # @Outer('抱举1次') 就等于一个实例对象,但是装饰器需要一个函数对象,所以用__call__,还是等于一个decor
def love(*args,**kwargs):
print(f"{args}亲亲{kwargs}")
return '基本函数返回值'
#基本函数
@Outer('抱举3次') # @Outer('抱举1次') 就等于一个实例对象,但是装饰器需要一个函数对象,所以用__call__,还是等于一个decor
def love2(*args,**kwargs):
print(f"{args}亲亲{kwargs}")
return '基本函数返回值'
'''这样实现的效果就是,同一个装饰器,
因为传入参数不用,可以实现装饰不同的函数的效果'''
print(love('爸爸','妈妈',son='儿子',girl='女儿'))
print('====================分割线====================')
print(love2('爸爸','妈妈',son='儿子',girl='女儿'))
#执行结果:
'''
抱抱
('爸爸', '妈妈')亲亲{'son': '儿子', 'girl': '女儿'}
举高高
基本函数返回值
====================分割线====================
抱抱
抱抱
抱抱
('爸爸', '妈妈')亲亲{'son': '儿子', 'girl': '女儿'}
举高高
举高高
举高高
基本函数返回值
'''
5.多个装饰器的执行顺序
'''这里是演示多个装饰器的使用'''
#闭包1
def funOut(func):
print("闭包1返回")
def funin(*args,**kwargs):#如果有参数就写这个可变参数
print("闭包1执行")
func(*args,**kwargs)
return funin
#闭包2
def funOut2(func):
print("闭包2返回")
def funin2(*args,**kwargs):#如果有参数就写这个可变参数
print("闭包2执行")
func(*args,**kwargs)
return funin2
'''这里的装饰器起的作用是从下到上的。相当与依次执行了 foo = funOut(foo) 与 foo = funOut2(foo)
所以输出的顺序为:闭包1返回,闭包2返回
'''
@funOut2#等同于:foo = funOut2(foo)
@funOut#等同于:foo = funOut(foo)
def foo():
print("foo函数正在运行")
'''这里调用的时候,闭包函数执行又相反
所以输出顺序为:闭包2执行,闭包1执行
注意:foo函数只执行一次
'''
foo()
#执行结果:
'''
闭包1返回
闭包2返回
闭包2执行
闭包1执行
foo函数正在运行
'''
6.内置装饰器
property
描述符
- 概念
如果一个类中包含了三个魔术方法(==__get__,__set__,__delete__==)之一或者全部的类就是描述符
- 作用
描述符的作用就是对类/对象中某个成员进行详细的管理操作
- 数据描述符
同时具备三个魔术方法的类就是数据描述符
- 非数据描述符
没有同时具备三个魔术方法的类就是非数据描述符
==下面的这个例子可以用于类的属性==,而property装饰器用于类的私有属性
class Descriptor():
def __init__(self,name):
self.temp = name
def __get__(self, instance, owner):
'''instance 就是含有描述符的类的实例 就是下面的 h 对象
owner 就是含有描述符的类 就是下面的 Human 类
'''
# 修改的时候以做一些限制,比如中间的文字用*号代替
result = self.temp[0]+'*'+self.temp[-1]
return result
def __set__(self, instance, value):
# 设置的可以做一些限制,比如修改名字只能在三个字以内
if len(value) < 4:
self.temp = value
def __delete__(self, instance):
# 删除的时候可以做一些判断
if instance.is_allow_del:
del self.temp
class Human:
name = Descriptor('初始名字')
is_allow_del = False#用这个来判断描述符是否可以删除
h = Human()
print(h.name) # 会调用 __get__
h.name = '范大哥' # 会调用 __set__
print(h.name) # 会调用 __get__
del h.name # 会调用 __delete__
print(h.name)
#执行结果:
'''
初*字
范*哥
范*哥
'''
property方法的描述符
1.必须赋值给私有属性,因为下面使用property返回的名字和这个名字同名,不是私有属性,直接可以调用,就没有意义了,理解不了可以看下面装饰器的例子
2.使用property(get,set,delete)返回给该属性值
class Human():
def __init__(self,name):
#必须赋值给私有属性
self.__name = name
self.is_allow_del = False # 用这个来判断描述符是否可以删除
def my_get(self):
# 修改的时候以做一些限制,比如中间的文字用*号代替
result = self.__name[0]+'*'+self.__name[-1]
return result
def my_set(self, value):
# 设置的可以做一些限制,比如修改名字只能在三个字以内
if len(value) < 4:
self.__name = value
def my_delete(self):
# 删除的时候可以做一些判断
if self.is_allow_del:
del self.__name
#使用property返回给该属性值
name = property(my_get,my_set,my_delete)
h = Human('张三')
print(h.name) # 会调用 __get__
h.name = '李四' # 会调用 __set__
print(h.name) # 会调用 __get__
del h.name # 会调用 __delete__
print(h.name)
#执行结果:
'''
张*三
李*四
李*四
'''
property装饰器的描述符
说明:
- property装饰器装饰一个方法以后,就使得==像属性一样调用==这个方法,不需要加括号(实例对象.方法)
- 实际的使用,我们一般用于对==私有属性==的获取,设置,删除操作,因为私有属性是==外部实例对象==无法获取的。
- 所以我们把获取,设置,删除的==方法名==设置的与==私有属性名==一致,那么在外部实例对象调用的时候,就像是对私有属性的直接调用(获取,设置,删除)。在调用(获取,设置,删除)之前,还可以加入我们的判断
class Person(object):
def __init__(self,age):
# 必须赋值给私有属性
self.__age = age
self.is_allow_del = False # 用这个来判断描述符是否可以删除
@property
def age(self):
print('get_age_fun触发了')
return self.__age
@age.setter
def age(self, value):
print('set_age_fun触发了')
if not isinstance(value, int):
raise ValueError('年龄必须是数字!')
if value < 0 or value > 100:
raise ValueError('年龄必须是0-100')
self.__age = value
@age.deleter
def age(self):
print('del_age_fun触发了')
if self.is_allow_del:
del self.__age
p = Person(20)
print(p.age)
p.age = 17
print(p.age)
del p.age
print(p.age)
#执行结果:
'''
get_age_fun触发了
20
set_age_fun触发了
get_age_fun触发了
17
del_age_fun触发了
get_age_fun触发了
17
'''
classmethod
描述
- classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等
作用
- 可以不需要实例化,直接类名.方法名()来调用。这有利于组织代码,把某些应该属于某个类的函数给放到那个类里去,同时有利于命名空间的整洁
```python class A():
@classmethod
def a(cls):
print(f'这个是类方法:{cls}') # 传入的就是类对象本身
print(f'这个是对象A:{A}') A.a() # 可以直接通过类调用,如果是self方法会报错(必须传入实例对象)
A().a() # 当然也允许实例化的对象去调用
#执行结果:
'''
这个是对象A:
- @classmethod的作用实际是可以在class内实例化class,一般使用在有==工厂模式==要求时。作用就是比如输入的数据需要清洗一遍再实例化,可以把清洗函数定义在class内部并加上@classmethod装饰器已达到减少代码的目的。总结起来就是:==@class method可以用来为一个类创建一些预处理的实例==。
```python class Data_test(object): day = 0 month = 0 year = 0
def __init__(self, year=0, month=0, day=0):
self.day = day
self.month = month
self.year = year
@classmethod
def get_date(cls, data_as_string):
# 这里第一个参数是cls, 表示调用当前的类名
year, month, day = map(int, data_as_string.split('-'))
date1 = cls(year, month, day) # 返回的是一个初始化后的类
return date1
def out_date(self):
print("year :", self.year)
print("month :", self.month)
print("day :", self.day)
r = Data_test.get_date("2020-1-1") r.out_date()
#执行结果: ''' year : 2020 month : 1 day : 1 ''' ```
staticmethod
描述
- staticmethod叫做静态方法,在类里面加上@staticmethod装饰器的方法不需要传入self,同时该方法不能使用类变量和实例变量。在类内部可以调用加上装饰器@staticmethod的方法,同时也不需要实例化类调用该方法
实例
class A:
@staticmethod
def static_fun():
'''静态方法一般封装一些和类无关的函数,比如一些算法等等'''
print('static_fun')
A.static_fun() # 一般可以通过类调用
A().static_fun() # 也可以通过类实例对象调用