5.描述器与装饰器

描述器与装饰器

一、__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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值