面向对象进阶(二)

__getattribute__

回顾__getattr__

class Foo:
   def __init__(self,x):
       self.x=x

   def __getattr__(self, item):
       print('执行的是我')
       # return self.__dict__[item]

f1=Foo(10)
print(f1.x)
f1.xxxxxx #不存在的属性访问,触发__getattr__
class Foo:
   def __init__(self,x):
       self.x=x
   def __getattribute__(self, item):
       print('不管是否存在,我都会执行')
f1=Foo(10)
f1.x
f1.xxxxxx

二者同时出现

class Foo:
   def __init__(self,x):
       self.x=x

   def __getattr__(self, item):
       print('执行的是我')
       # return self.__dict__[item]
   def __getattribute__(self, item):
       print('不管是否存在,我都会执行')
       raise AttributeError('哈哈')
f1=Foo(10)
f1.x
f1.xxxxxx
#当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

描述符(__get__,__set__,__delete__)

1 描述符是什么:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

__get__():调用一个属性时,触发
__set__():为一个属性赋值时,触发
__delete__():采用del删除属性时,触发

定义一个描述符

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

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

引子:描述符类产生的实例进行属性操作并不会触发三个方法的执行

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

#包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
f1=Foo()
f1.name='egon'
f1.name
del f1.name
#疑问:何时,何地,会触发这三个方法的执行

描述符应用之何时?何地?

#描述符Str
class Str:
   def __get__(self, instance, owner):
       print('Str调用')
   def __set__(self, instance, value):
       print('Str设置...')
   def __delete__(self, instance):
       print('Str删除...')

#描述符Int
class Int:
   def __get__(self, instance, owner):
       print('Int调用')
   def __set__(self, instance, value):
       print('Int设置...')
   def __delete__(self, instance):
       print('Int删除...')

class People:
   name=Str()
   age=Int()
   def __init__(self,name,age): #name被Str类代理,age被Int类代理,
       self.name=name
       self.age=age

#何地?:定义成另外一个类的类属性

#何时?:且看下列演示

p1=People('alex',18)

#描述符Str的使用
p1.name
p1.name='egon'
del p1.name

#描述符Int的使用
p1.age
p1.age=18
del p1.age

#我们来瞅瞅到底发生了什么
print(p1.__dict__)
print(People.__dict__)

#补充
print(type(p1) == People) #type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__)

3 描述符分两种

一 数据描述符:至少实现了__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')

4 注意事项:
一 描述符本身应该定义成新式类,被代理的类也应该是新式类
二 必须把描述符定义成这个类的类属性,不能为定义到构造函数中
三 要严格遵循该优先级,优先级由高到底分别是
    1.类属性
    2.数据描述符
    3.实例属性
    4.非数据描述符
    5.找不到的属性触发__getattr__()

5 描述符使用

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能

牛刀小试

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
p1=People('egon',18,3231.3)
#调用
print(p1.__dict__)
p1.name

#赋值
print(p1.__dict__)
p1.name='egonlin'
print(p1.__dict__)

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

拔刀相助

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

#疑问:如果我用类名去操作属性呢
People.name #报错,错误的根源在于类去操作属性时,会把None传给instance

#修订__get__方法
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

p1=People(123,18,3333.3)#传入的name因不是字符串类型而抛出异常

大刀阔斧

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)

class People:
   name=Typed('name',str)
   age=Typed('name',int)
   salary=Typed('name',float)
   def __init__(self,name,age,salary):
       self.name=name
       self.age=age
       self.salary=salary

p1=People(123,18,3333.3)
p1=People('egon','18',3333.3)
p1=People('egon',18,3333)

大刀阔斧之后我们已然能实现功能了,但是问题是,如果我们的类有很多属性,你仍然采用在定义一堆类属性的方式去实现,low,这时候我需要教你一招:独孤九剑

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('egon',18,3333.3)

识别图中二维码,欢迎关注python宝典

猜你喜欢

转载自www.cnblogs.com/IT-Scavenger/p/9098546.html