\\__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

 

posted @ 2020-06-03 23:05  我和姚明一样高  阅读(229)  评论(0编辑  收藏  举报