一、类的装饰器
装饰器并不是在调用该函数/对象的时候调用的,而是在代码编译的过程中已经返回函数/对象,当改函数/对象被调用的时候,调用的是已经返回的函数/对象
类作为一个对象,也可以被装饰。
——通过给类添加装饰器的方式,给类增加类属性
def wrap(obj):
print("装饰器-----")
obj.x = 1
obj.y = 3
obj.z = 5
return obj
@wrap #将Foo类作为一个参数传入装饰器函数wrap,返回同时返回该对象,把新对象重新命名为Foo
#即 Foo = wrap(Foo)
class Foo:
pass
print(Foo.__dict__) #输出结果可以看到,新的Foo类新增了x,y,z属性
>>> 装饰器-----
>>> {'__module__': '__main__', '__init__': <function Foo3.__init__ at 0x0000029C089ABBF8>, '__dict__': <attribute '__dict__' of 'Foo3' objects>, '__weakref__': <attribute '__weakref__' of 'Foo3' objects>, '__doc__': None, 'x': 1, 'y': 3, 'z': 5}
函数可以作为一个对象,也有__dict__
方法
def wrap(obj):
print("装饰器-----")
obj.x = 1
obj.y = 3
obj.z = 5
return obj
@wrap #test = wrap(test)
def test():
print("test-----")
test.x = 10 #test的x属性被重新赋值
print(test.__dict__) #输出结果可以看到,test作为一个函数也有__dict__方法,
# 新的test函数新增了x,y,z属性
>>>{'x': 10, 'y': 3, 'z': 5}
类的装饰器应用——通过给类加带参数的装饰器,给类增加一个类属性,该类属性是描述符类的实例对象
class Type:
def __init__(self,key,except_type): #People对象的key,和期望的数据类型
self.key = key
self.except_type = except_type
def __get__(self, instance, owner):
return isinstance.__dict__[self.key]
def __set__(self, instance, value):
print("instance---",instance)
if not isinstance(value,self.except_type):
print("您输入的类型不是%s"%self.except_type)
raise TypeError
instance.__dict__[self.key] = value
def __delete__(self, instance):
isinstance.__dict__.pop(self.key)
def deco(**kwargs): ##这个装饰器的作用相当于给类添加了name=Type('name',str) age=Type('age',int)
def wrapper(obj): #类的装饰器
for key,val in kwargs.items():
setattr(obj,key,Type(key,val)) #设置people类对象的每个参数的描述符
return obj
return wrapper
@deco(name=str,age=int)
class People:
def __init__(self,name,age):
self.name = name
self.age = age
if __name__=='__main__':
print('start1')
p = People("nick", 18)
print(p.__dict__)
print('start2')
p1 = People("jerry", 20)
print(p1.__dict__)
print(p.__dict__)
#输出
______
start1
instance--- <__main__.People object at 0x000001BCBE39FD30>
instance--- <__main__.People object at 0x000001BCBE39FD30>
{'name': 'nick', 'age': 18}
start2
instance--- <__main__.People object at 0x000001BCBE39FB38>
instance--- <__main__.People object at 0x000001BCBE39FB38>
{'name': 'jerry', 'age': 20}
{'name': 'nick', 'age': 18}
二、自定义property
装饰器也可以是一个类,在自定义property中要使用一个类作为装饰器
class LazyProperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
print("执行__get__")
if not instance: # 如果是用原类.属性来调用,,这时instance(对象)值为None,直接返回描述符对象
return self
res = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入
return res
class Room:
def __init__(self, name, length, width):
self.name = name
self.length = length
self.width = width
@LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符,相当于给这个类新增了一个类属性,该类属性是数据描述符类LazyProperty的实例对象
# 新的area已经是经过类LazyProperty装饰过的函数地址
def area(self):
return self.length * self.width
if __name__=='__main__':
print(Room.__dict__)
r = Room("nick", 18, 10)
print(r.area)
print(r.__dict__)
print(Room.__dict__)
输出:
{'__module__': '__main__', '__init__': <function Room.__init__ at 0x0000024858FF6048>, 'area': <__main__.LazyProperty object at 0x0000024858F7F6A0>, '__dict__': <attribute '__dict__' of 'Room' objects>, '__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
执行__get__
180
{'name': 'nick', 'length': 18, 'width': 10}
{'__module__': '__main__', '__init__': <function Room.__init__ at 0x0000024858FF6048>, 'area': <__main__.LazyProperty object at 0x0000024858F7F6A0>, '__dict__': <attribute '__dict__' of 'Room' objects>, '__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
class LazyProperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
print("执行__get__")
if not instance: # 如果是用原类.属性来调用,这时instance(对象)值为None,直接返回描述符对象
return self
value = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入
setattr(instance,self.func.__name__,value) #将每次调用的函数属性名字和值存入对象的__dict__,相当于给instace(实例)新增实例属性area,由于LazyProperty是个非数据描述类,所有实例属性优先级更高
# self.func.__name__是获取被调用函数属性的名字
return value
class Room:
def __init__(self, name, length, width):
self.name = name
self.length = length
self.width = width
@LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符,
# 新的area已经是经过类LazyProperty装饰过的函数地址
def area(self):
return self.length * self.width
r = Room("nick", 18, 10)
print(r.__dict__)
print(r.area)
print(r.area) #这里将不再调用__get__
setattr(instance,self.func.__name__,value) #将每次调用的函数属性名字和值存入对象的__dict__,相当于给instace(实例)新增实例属性area,由于LazyProperty是个非数据描述类,所有实例属性优先级更高。当第二次调用area时,调用实例属性而不是非数据描述类的__get__
三、property补充
一个静态属性property本质就是实现了get,set,delete三种方法
用语法糖可以实现property的类似属性的设置和删除,与一般的属性设置删除没有区别
class People:
def __init__(self):
self.study = "8h"
@property
def study(self):
pr