描述器与装饰器
一、__new__
方法
1.概念
__new__
方法会在初始化函数 __init__
之前执行,类每次实例化时,都会创建一个新的对象,然后在初始化
__new__
方法合理改写可以带来方便,常利用 __new__
实现类的单例模式(完全相同的实例多次创建时,要求类只被实例化一次)
2.__new__
:单例模式
(1).正常模式
class A:
def __init__(self):
self.name = "aaa"
one = A()
print(one, id(one)) # <__main__.A object at 0x0000011CC3F22E10> 1223058140688
two = A()
print(two, id(two)) # <__main__.A object at 0x0000011CC3FD0160> 1223058850144
(2).单例模式
class A:
def __new__(cls, *args, **kwargs):
# hasattr():函数用于判断对象是否包含对应的属性、cls:类、"instance":实例
if not hasattr(cls, "instance"): # 此类中如果没有实例存在
cls.instance = super().__new__(cls) # 新建一个实例
return cls.instance
def __init__(self):
self.name = "aaa"
one = A()
print(one, id(one)) # <__main__.A object at 0x0000017420B30080> 1598276436096
two = A()
print(two, id(two)) # <__main__.A object at 0x0000017420B30080> 1598276436096
二、定制属性访问(自定义属性的增删改查)
1.方法
(1).hasattr()
-
描述:
hasattr()
函数用于判断对象是否包含对应的属性 -
语法:
hasattr(object, name)
-
参数:object – 对象;name – 字符串,对象的属性
-
返回值:
bool
值
(2).getattr()
-
描述:
getattr()
函数用于返回一个对象属性值 -
语法:
getattr(object, name[, default])
-
参数:object – 对象;name – 字符串,对象的属性;default – 默认返回值,如果不提供该参数,在没有对应属性时,将报错
-
返回值:对象属性值
(3).setattr()
-
描述:
setattr()
函数用于设置属性值,该属性不一定是存在的 -
语法:
setattr(object, name, value)
-
参数:object – 对象;name – 字符串,对象的属性;value – 属性值
-
返回值:无
(4).delattr()
-
描述:
delattr()
函数用于删除属性 -
语法:
delattr(object, name)
-
参数:object – 对象;name – 字符串,对象的属性
-
返回值:无
2.实例
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
areas = self.length * self.width
return areas
a = Rectangle(11, 22)
# 查
print(hasattr(a, "length")) # True
print(getattr(a, "length")) # 11
print(a.__getattribute__("length")) # 11
# 改
setattr(a, "length", 33)
print(a.length) # 33
a.__setattr__("length", 44)
print(a.length) # 44
# 增
a.num = 55
print(a.num) # 55
setattr(a, "num", 66)
print(a.num) # 66
a.__setattr__("num", 77)
print(a.num) # 77
# 删
delattr(a, "num")
a.__delattr__("num")
del a.num
print(a.num) # AttributeError: num
3.当属性不存在时,实现不报错
当属性不存在时,如果定义了__getattr__()
魔术方法,则调用方法
class Rectangle:
def __init__(self,length,width):
self.length = length
self.width = width
def __getattr__(self, item):
print("no attribute")
a = Rectangle(11, 22)
a.num # no attribute
三、描述器(了解)
在类里面实例化另一个类,对这个实例做访问时,需要定义 __get__
、__set__
、 __delete__
魔术方法
class A:
'''
self 指当前实例,调用者
instance是owner的实例
owner是属性的所属的类
'''
def __get__(self, instance, owner):
print("__get__")
def __set__(self, instance, value):
print("__set__")
def __delete__(self, instance):
print("__delete__")
class B:
a = A() # 把A的实例对象拿来做了B的类属性,描述符
def __init__(self):
self.name = "zzh"
b = B() # 类的实例化
print(b.name) # 获取对象属性
B.a # 获取B的类属性(A的实例),会去执行__get__
b.a = 66 # 重新赋值,会去执行__set__
del b.a # 删除,会去执行__delete__
**********************************************************************
zzh
__get__
__set__
__delete__
四、装饰器
- 写代码要遵循开放封闭原则,所以装饰器十分重要
- 装饰器是指在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出
- 装饰器的本质还是闭包,接受了一个函数名作为参数,返回出了一个函数名
- 装饰器的语法是将@装饰器名,放在被装饰对象上面
- 装饰器包括函数装饰器和类装饰器
- 装饰器的语法规则是:被装饰函数的名字会被当作参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰函数的名字
语法糖:@
执行@后的outer函数并将下方的函数名作为参数赋值给outer函数,然后将outer函数的返回值重新赋值给了下方的函数
被装饰的函数的名字会被当作参数传递给装饰函数。装饰函数执行它自己内部的代码后,会将它的返回值赋值给被装饰的函数
0.装饰器过程分析
以下面的单个装饰器为例
- 程序开始运行,读取
outer(func)
函数并将其存入内存 - 读到
@outer
时,被@语法糖吸住,声明outer是装饰器,要立即执行装饰器 - 执行装饰器:被装饰函数的名字
f1
被当做参数传给outer(f1)
,outer(f1)
执行过程中读取了inner()
函数并将其存入内存,执行完内部代码,将返回值inner
赋值给被装饰函数的名字f1
,即f1=inner
,(实际上是f1
这个函数名更改成指向inner
这个函数名指向的函数体内存地址,f1
不再指向它原来的函数体的内存地址)。此过程即定义f1 = outer(f1)
,装饰完毕 - 程序向下走,读取
f1()
函数并将其存入内存 - 调用
f1()
函数,此时执行的就不再是旧的f1()
函数的代码,而是inner()
函数的代码。即outer(f1)()
1.单个装饰器(不含参数)
def outer(func):
def inner():
print("前增功能")
re = func()
print("后增功能")
return re
return inner
@outer # f1 = outer(f1)
def f1():
print("原功能")
f1()
**********************************************************************
前增功能
原功能
后增功能
2.单个装饰器(含参数)
def outer(func):
def inner(*args, **kwargs):
print("前增功能")
func(*args, **kwargs)
print("后增功能")
return inner
@outer
def f2(a):
print(a)
f2(3)
@outer
def f4(a, b, c):
print(a + b + c)
f4(3, 5, c = 6)
...
3.多个装饰器装饰一个函数
def outer_0(func):
def inner(*args, **kwargs):
print("start")
func(*args, **kwargs)
print("end")
return inner
def outer(func):
def inner(*args, **kwargs):
print("前增功能")
func(*args, **kwargs)
print("后增功能")
return inner
@outer_0
@outer
def f1():
print("原功能")
f1()
**********************************************************************
start
前增功能
原功能
后增功能
end
4.内置装饰器
(1).@classmethod
(类方法)
class Cat:
name = "猫"
def __init__(self, color, eat):
self.color = color
self.eat = eat
def print_cat(self):
print("{},{}".format(self.color, self.eat))
@classmethod # 类方法关键字
def func1(cls): # 类方法#cls 代表类本身
print(cls.name) # 打印类属性
kitty = Cat("write", "milk")
kitty.print_cat()
kitty.func1() # 实例调用类方法
Cat.func1() # 类方法可以用类名直接调用
**********************************************************************
write,milk
猫
猫
(2).@property
(方法变属性)
class Cat:
name = "猫"
def __init__(self, color, eat):
self.color = color
self.eat = eat
@property # 方法变属性
def print_cat(self):
print("{},{}".format(self.color, self.eat))
@classmethod # 类方法关键字
def func1(cls): # 类方法
print(cls.name) # 打印类属性
kitty = Cat("write", "milk")
# kitty.print_cat() # 原来的调用方法
kitty.print_cat
**********************************************************************
write,milk
(3).@staticmethod
(静态方法(解绑))
class Cat:
name = "猫"
def __init__(self, color, eat):
self.color = color
self.eat = eat
@property # 方法变属性
def print_cat(self):
print("{},{}".format(self.color, self.eat))
@classmethod # 类方法关键字
def func1(cls): # 类方法
print(cls.name) # 打印类属性
@staticmethod # 静态方法(解绑)
def func():
print("不需要self参数也能运行")
print("不需要实例化")
Cat.func()
kitty = Cat("write", "milk")
kitty.func()
**********************************************************************
不需要self参数也能运行
不需要实例化
不需要self参数也能运行
不需要实例化
5.类做装饰器
本质与函数的装饰器一样,但是注意参数通过__init__
传递,装饰内容由__call__
魔术方法添加
class TextClass:
"""用来做装饰器的类"""
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("start")
self.func()
print("end")
@TextClass
def f1():
print("原方法")
f1()
**********************************************************************
start
原方法
end