ITPub博客

首页 > 数据库 > 数据库开发技术 > python面向对象编程篇(23)补充-@property和描述器批量属性绑定

python面向对象编程篇(23)补充-@property和描述器批量属性绑定

原创 数据库开发技术 作者:程序员小钟同学 时间:2021-03-30 14:54:56 0 删除 编辑

前言

以下是用我自己立即的方式去记录关于描述符的认识笔记。纯属整理和实践笔记记录。如有错误,还希望多多指教!

本文内容

  • 1.关于@property的应用
  • 2:描述符是property的升级的理解

1 @property的应用

@property() 函数的作用是在新式类中返回属性值(把一个方法变成属性调用)。

示例:

class People:

    def __init__(self, name=None):
        self._name = name

    # 使用property,解决显式的类似get_budget和set_budget方法。
    @property
    def name(self):
        """I'm the 'x' property."""
        print("我可以先处理一下相关逻辑再返回")
        return self._name

    @name.setter
    def name(self, value):
        print("我可以先处理一下相关逻辑再设置")
        self._name = value

    @name.deleter
    def name(self):
        del self._name

    # 标记为一个只读的属性值
    @property
    def age(self):
        return 201

p = People()
print(p.name)
print(p.age)
p.name = 345



上面的示例中我们的使用了@property,

property优点:

  • @property 实现了类似我们的之前提到关于描述符的作用,接管了相关的变量设置。实现了和我们的描述符一样的__ set__、__ get__、__ delete__的需求管理。
  • 在相关属性操作的时候可以对参数进行必要的检查。

property缺点:

  • @property来实现了类似java中的setX和getX等一系列的属性的设置。但是看起来又好像没什么大的区别,实际如果属性很多的时候,也很复杂!也不够简洁了!内部满屏都是@xx.setter和其他
总结:
  • 使用@property装饰器一个方法时则方法变为一个属性
  • 使用@property装饰器一个方法时,@property本身又创建了另一个装饰器@属性名.setter,可以把一个setter方法变成属性赋值

@property使用流程:

纯可读属性:定义@property(只实现getter方法,不定义setter方法)

可读书属性:

  • 1 - 先定义@property(是getter方法)

都定义,是读写属性

  • 先定义@property(是getter方法)
  • 再定义@属性名.setter (是setter方法)
  • 需要删除的话  再定义@属性名.delete

2 描述符应用示例

1)把属性委托给另一个类进行代理校验,类的方式实现:

#!/usr/bin/evn python
# coding=utf-8

# 描述的类
class Describe:

    def __init__(self,a_value):
        print("传值:",a_value)
        self.key = '我是代理被描述类的属性的代理者'

    def __get__(self, instance, value):
        print("返回属性", instance, value)
        return self.key

    # def __set__(self, instance, value):
    def __set__(self, instance, value):
        print("设置属性", instance, value)

        return value

    def __delete__(self, instance):
        print("删除对象", instance)
        del self.key


# 被描述的类
class People:
    # 注意这个了类变量==把一个描述类的对象给赋值到被描述的类的属性的变量上
    name = Describe("传递参数")
    sex = Describe("传递参数sex")
    def __init__(self):
        # 这里引发了设置属性,但是调用的描述类的属性值,也就是说,是调用了类属性的值,而不是给对象建立一个实例属性
        self.name = "上面什么啊"
        self.sex = "男"


p = People()
print(p.name)

2)结合setattr和类的装饰器模式+描述符类实现对类属性代理:


class Describe:
    '''
    描述符类
    '
''
    def __init__(self,attri,data_type):
        # 具体的属性
        self.attri = attri
        # 属性的类型
        self.data_type = data_type

    def __get__(self, instance, owner):
        print(f'get触发了')
        if instance is None:
            return '当前未通过对象调用描述符的代理'
        print("返回属性属性")
        return instance.__dict__[self.attri]

    def __set__(self, instance, value):
        if not isinstance(value, self.data_type):
            raise TypeError(f'{self.attri}属性传递的不是:{self.data_type}类型数据')
        print("开始设置属性")
        instance.__dict__[self.attri] = value

    def __delete__(self, instance):
        print('执行了delete方法:')


def arrti_datatype_decorate(*args,**kwargs):
    '''
    类装饰器
      :param args:
    :param kwargs:
    :return:
    '
''
    def decorate(cls):
        print(args)
        # 设置相关的属性的类型
        for attri, data_type in kwargs.items():
            # setattr(cls, attri, data_type)
            print('有参装饰器!-> %s--类似是:%s' % (attri,data_type))
            # 注意这里,将传给类的属性变成代理描述符的类属性
            # cls:People这个类
            # attri:给People类设置的属性名
            # An(attri,data_type) 将属性名与数据类型传递到描述符里的__init__内
            # 得到一个代理描述符的对象,然后赋给cls,也就是People类
            setattr(cls, attri, Describe(attri, data_type))
            # setattr就是给对象增加属性的,类也可以是一个对象,也可以进行属性增加
        return cls
    return decorate

@arrti_datatype_decorate(435,name = str,age = int,sex = str)
class People:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

# print(People.__dict__)
# 涉及到了三个属性的设置和校验
p = People('jack',18,'444')
p.age = 345

# @arrti_datatype_decorate(435,name = str,age = int,sex = str)
# class People:
#     pass
# 
# # print(People.__dict__)
# # 涉及到了三个属性的设置和校验
# p = People()
# p.age = 345
# p.name = "sadsad"

输出结果为:

(435,)
有参装饰器!-> name--类似是:<class 'str'>
有参装饰器!-> age--类似是:<class 'int'>
有参装饰器!-> sex--类似是:<class 'str'>
开始设置属性
开始设置属性
开始设置属性
开始设置属性

分析:上面的示例是通过一个装饰器的形式进行批量属性的进行绑定描述符。

从上面看。上面的模式其实和我们的pydantic类型检查工具是差不多的性质的,甚至可以嵌套!

总结:描述符的应用场景

描述符的本质对属性的个性定制与控制:所以通常相关的场景如:

  • (1)属性设置和获取时候检测,会修改。
  • (2)属性检测时候自定义异常描述
  • (3)标记相关属性为只读属性
  • (4)property的本质和描述符的本质是一样

参考资料:

https://blog.csdn.net/qq_27825451/article/details/84848341

https://blog.csdn.net/m0_46958731/article/details/111935453(非常好的参考)

个人其他博客地址

简书:https://www.jianshu.com/u/d6960089b087

掘金:https://juejin.cn/user/2963939079225608

小钟同学 | 文 【原创】| QQ:308711822

  • 1:本文相关描述主要是个人的认知和见解,如有不当之处,还望各位大佬指正。
  • 2:关于文章内容,有部分内容参考自互联网,如有链接会声明标注;如没有及时标注备注的链接的,如有侵权请联系,我会立即删除处理哟。


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111709