面向对象二

一.类中的内置函数

二.反射

三.包装类型

四.元类

 

一.OOP中的内置函数

首先明确:类中存在一些名字带有__(双下滑线)开头的内置函数,这些函数会在某些时候被自动调用,例如之前学习的迭代器,__next__函数

1.isinstance(obj,cls)

检查是否obj是否是类 cls 的对象

 class Foo(object):
     pass
 obj = Foo()
 isinstance(obj, Foo)

2.issubclass(sub, super)

检查sub类是否是 super 类的派生类

class Foo(object):
    pass
class Bar(Foo):
    pass
issubclass(Bar, Foo)

3.1操作对象属性时自动触发

__setattr__

使用点语法添加/修改属性会触发它的执行

__delattr__

使用点语法删除属性的时候会触发

__getattr__

使用点语法调用属性且属性不存在的时候才会触发

__getattribute__

使用点语法调用属性的时候触发,无论属性是否存在都会执行

​ 注意:当__getattribute____getattr__同时存在时,仅执行__getattribute__

class Foo:
    x=1
    def __init__(self,y):
        self.y=y
    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')
    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #这就无限递归了,你好好想想
        # self.__dict__[key]=value #应该使用它
    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        self.__dict__.pop(item)
​
#__setattr__添加/修改属性会触发它的执行
f1=Foo(10)
print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z=3
print(f1.__dict__)
​
#__delattr__删除属性的时候会触发
f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
del f1.a
print(f1.__dict__)
​
#__getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
​
#三者的用法演示

3.2操作对象属性时自动触发

__setitem__

使用key的形式添加/修改属性时触发

__getitem__

使用key的形式获取属性时触发

__delitem__

使用key的形式删除属性时触发

class Foo:
    def __init__(self,name):
        self.name=name
​
    def __getitem__(self, item):
        print(self.__dict__[item])
​
    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)
​
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)

4.描述符

4.1什么是描述符

描述符本质就是一个类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,

也被称为描述符协议 __get__():调用一个属性时,触发 __set__():为一个属性赋值时,触发 __delete__():采用del删除属性时,触发

4.2为什么需要描述符:

描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中),python底层的很多特性都是使用描述符来完成的,例如实例方法,classmethod,staticmethod等等!

简单的说:描述符可以检测到一个属性的访问和修改,从而对这些操作增加额外的功能逻辑;

#描述符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__)
​
# 描述符应用 以及执行时机

4.3描述的分类

1.数据描述符 ​ 至少实现了__get__()__set__()两个方法

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

2.非数据描述符 ​ 没有实现__set__()方法

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

4.4注意事项

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

4.5描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是slots属性

描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.

 

案列1:利用描述符原理完成一个自定制@property,实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

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
        else:
            print('--->')
            value=self.func(instance)
            setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
            return value
​
class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length
​
    @Lazyproperty #area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
    def area(self):
        return self.width * self.length
​
r1=Room('alex',1,1)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
​
实现延迟计算功能

案例2: 利用描述符原理完成一个自定制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值