• \__get__\ __set__\__delete__\ \@staticmethod\@property\@classmethod


    描述符

    描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,也被叫做描述符协议。
    __get__():调用一个属性时候,触发
    __set__():为一个属性赋值时,触发
    __delete__():采用del删除属性时候,触发

    python3和python2的区别

    # 在python3中Foo是新式类,它实现了__get__(),__set__(),__delete__()中的一个三种方法的一个,这个类就被称作一个描述符
    class Foo:
        def __get__(self, instance, owner):
            pass
        def __set__(self, instance, value):
            pass
        def __delete__(self, instance):
            pass

    描述符的作用

    重点

    描述符是干什么的:描述符的作用是用来代理另外一个类的属性的,必须把描述符定义成这个类的类属性不能定义到构造函数。

    例子

    class Int:
        def __get__(self, instance, owner):
            print('Int的get')
    
        def __set__(self, instance, value):
            print('Int的set')
    
        def __delete__(self, instance):
            print('Int的delete')
    
    
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        name = Str()
        age = Int()
    
    p1 = People('alex',12)
    
    p1.name
    p1.name = 'jc'
    del p1.name


    Str的set
    Int的set
    Str的get
    Str的set
    Str的delete

    到底为什么呢

    # 省略以山步骤
    
    
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            
        # name 是Str的一个实例
        name = Str()
        # age 是 Int的一个实例
        age = Int()
    
    p1 = People('alex',12)
    
    # p1.name
    # p1.name = 'jc'
    # del p1.name
    
    print(p1.__dict__)

    {}
    print(People.__dict__)

    {'__module__': '__main__', '__init__': <function People.__init__ at 0x000001FE39192378>, 'name': <__main__.Str object at 0x000001FE391AE048>, 'age': <__main__.Int object at 0x000001FE391AE080>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

    两种描述符

    数据描述符(至少实现了__get__()和__set__())

    class Foo:
        def __set__(self, instance, value):
            print('set')
    
        def __get__(self, instance, owner):
            print('get')

    非数据描述符(没有实现__set__)

    class Foo:
        def __get__(self, instance, owner):
            print('get')

    描述符注意事项

    描述符本身应该定义成新式类,被代理的类也是新式类
    必须把描述符定义成这个类的类属性,才是描述符,不能够定义到构造函数里面去

    注意

    优先级由高到低分别是
    
    1、类属性   类里面的属性,name,age,gender之类的
    2、数据描述符 实现了__get__() 和 __set__()
    3、实例属性 p1.name
    4、非数据描述符 没有实现__set__()方法
    5、找不到的属性触发__getattr__()

    使用描述符

    总所周知、python是弱类型语言,即参数的赋值没有类型限制。

    例子1

    class Str:
        def __init__(self, name):
            self.name = name
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            print(instance.__dict__)
            instance.__dict__[self.name] = value
            print(instance.__dict__)
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Str('name')
    
        def __init__(self, name, age, salary):
            self.name = name # 触发__set__
            self.age = age
            self.salary = salary
    
    
    p1 = People('JAC', 18, 3231.3)

    #实例的属性 __dict__
    print(p1.__dict__)

    {'name': 'randy', 'age': 18, 'salary': 3231.3}

    # 赋值
    p1.name = 'jackson'

    # 删除
    print(p1.__dict__)
    del p1.name
    print(p1.__dict__)

    {'name': 'jackson', 'age': 18, 'salary': 3231.3}
    delete---> <__main__.People object at 0x000002EC6CCFE240>
    {'age': 18, 'salary': 3231.3}



    例子2

    class Str:
        def __init__(self, name):
            self.name = name
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Str('name')
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    # 疑问:如果我用类名去操作属性呢
    try:
        People.name  # 报错,错误的根源在于类去操作属性时,会把None传给instance
    except Exception as e:
        print(e)


    'NoneType' object has no attribute '__dict__'

    修改例2

    class Str:
        def __init__(self, name):
            self.name = name
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Str('name')
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    print(People.name)  # 完美,解决

    例三

    class Str:
        def __init__(self, name, expected_type):
            self.name = name
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            if not isinstance(value, self.expected_type):  # 如果不是期望的类型,则抛出异常
                raise TypeError('Expected %s' % str(self.expected_type))
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Str('name', str)  # 新增类型限制str   牛逼的描述符
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    try:
        p1 = People(123, 18, 3333.3)  # 传入的name因不是字符串类型而抛出异常
    except Exception as e:
        print(e)

    set---> <__main__.People object at 0x00000167DC2FE208> 123
    Expected <class 'str'>

    例四

    基本我们已经实现全部功能了,但是问题是,类有很多属性怎么办呢,一堆一堆去实现是不是显得很low

    class Type:
        def __init__(self, name, expected_type):
            self.name = name
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            if not isinstance(value, self.expected_type):  # 如果不是期望的类型,则抛出异常
                raise TypeError('Expected %s' % str(self.expected_type))
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    class People:
        name = Type('name', str)  # 新增类型限制str
        age = Type('name', int)  # 新增类型限制str
        salary = Type('name', float)  # 新增类型限制str
    
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    try:
        p1 = People('jac', 12, 3333.0)  # 传入的name因不是字符串类型而抛出异常
    except Exception as e:
        print(e)

    例5

    使用一个类的装饰器(无参数)

    def decorate(cls):
        print('类的装饰器开始运行啦------>')
        return cls
    
    
    @decorate  # 无参:People = decorate(People)
    class People:
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    p1 = People('randy', 18, 3333.3)
    # 类的装饰器开始运行啦------>

    使用一个类的装饰器(有参数)

    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>', kwargs)
            return cls
    
        return decorate
    
    # # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    @typeassert(
        name=str, age=int, salary=float
    )
    class People:
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    p1 = People('randy', 18, 3333.3)
    # 类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
    print(p1.__dict__)
    # {'name': 'randy', 'age': 18, 'salary': 3333.3}

    例6

    class Typed:
        def __init__(self, name, expected_type):
            self.name = name
            self.expected_type = expected_type
    
        def __get__(self, instance, owner):
            print('get--->', instance, owner)
            if instance is None:
                return self
            return instance.__dict__[self.name]
    
        def __set__(self, instance, value):
            print('set--->', instance, value)
            if not isinstance(value, self.expected_type):
                raise TypeError('Expected %s' % str(self.expected_type))
            instance.__dict__[self.name] = value
    
        def __delete__(self, instance):
            print('delete--->', instance)
            instance.__dict__.pop(self.name)
    
    
    def typeassert(**kwargs):
        def decorate(cls):
            print('类的装饰器开始运行啦------>', kwargs)
            for name, expected_type in kwargs.items():
                setattr(cls, name, Typed(name, expected_type))
            return cls
    
        return decorate
    
    
    @typeassert(
        name=str, age=int, salary=float
    )  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
    class People:
        def __init__(self, name, age, salary):
            self.name = name
            self.age = age
            self.salary = salary
    
    
    print(People.__dict__)
    p1 = People('Jac', 18, 3333.3)

    描述符总结

    描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod、@staticmethod、@property甚至是__slots__属性
    描述符是很多高级库和框架里面的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件

     自定义Property()

    class Lazyproperty:
        def __init__(self, func):
            self.func = func
    
        def __get__(self, instance, owner):
            print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
            if instance is None:
                return self
            return self.func(instance)  # 此时你应该明白,到底是谁在为你做自动传递self的事情
    
    
    class Room:
        def __init__(self, name, width, length):
            self.name = name
            self.width = width
            self.length = length
    
        # area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
        @Lazyproperty
        def area(self):
            return self.width * self.length
    
    
    r1 = Room('alex', 1, 1)
    print(r1.area)
    # 触发了__get__
    # 这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
    # 1

    自定义classmethod

  • 相关阅读:
    CODEVS 3137 栈练习1
    CODEVS 3138 栈练习2
    线段树———模板
    深度优先搜索与广度优先搜索———模板
    犯罪团伙 codevs 3554
    嘟!数字三角形 W WW WWW集合!
    寻找子串位置 codevs 1204
    流输入练习——寻找Sb.VI codevs 3096
    C++之路进阶——codevs3287(货车运输)
    c++之路进阶——codevs4543(普通平衡树)
  • 原文地址:https://www.cnblogs.com/jackson669/p/12594919.html
Copyright © 2020-2023  润新知