python 面向对象进阶 1 isinstance(obj,cls)和issubclass(sub,super) 2 反射 3 __setattr__,__delattr__,__getattr__

isinstance(obj,cls) obj是否是cls的对象或cls子类的对象
issubclass(sub,super) sub是否是super的子类
cls.__bases__ 获取cls的父类

class A:
    pass

class B(A):
    pass

print(issubclass(B,A))  #True
print(B.__bases__)

b=B()
print(isinstance(b,B))  #True

2 反射

反射也叫自省,主要设置程序可以访问、检测和修改它本身状态或行为的一种能力。

2.1 python中面向对象的反射

Python中是通过字符串的形式来操作对象相关的属性。python 中一切皆对象(都可以使用反射)
具体的做法是:将字符串映射成命令

四个实现自省的函数

2.1.1 hasattr

hasattr(object,name)

判断object是否有名字是name的属性。
等价于 name in object.__dict__

#!/user/bin/env python
# -*- coding:utf-8 -*-
class Foo:
    camp="foo"
    def __init__(self,name):
        self.name=name

    def test1(self):
        print("test1")

f=Foo("zzz")

#不用反射来,访问属性
# print(f.name)
# f.name="yyy"
# print(f.name)

#使用反射
#hasattr
print(f.__dict__)
print(hasattr(f,"name"))  #判断f对象的名称空间里,有么有"name"这个属性,等价于"name" in f.__dict__
print("name" in f.__dict__)   #True
print("camp" in Foo.__dict__)     #True

2.1.2 getattr

getattr(object, name, default=None)

获取object名称空间里的"name"属性的值,如果存在,就返回该值;不存在,就返回default。
等价于:object.name (object.__dict__["name"])

#getattr
print(f.name)
print(getattr(f,"name"))   #获取f对象名称空间里"name"属性的值,等价于 f.name  <======> f.__dict__["name"]
print(getattr(f,"age",-1))   #获取f对象名称空间里"age"属性的值,如果不存在,就返回-1
#print(f.age)   #AttributeError: 'Foo' object has no attribute 'age'
print(Foo.camp)       #foo
print(Foo.__dict__["camp"])   #foo

### 2.1.3 setattr

setattr(object, name, value)

修改对象object的name属性值为value。
等价于:object.name=value

#setattr
setattr(f,"name","yyy")    #等价于f.name="yyy"   <=====>f.__dict__["name"]="yyy"
print(getattr(f,"name"))
setattr(Foo,"camp","fooo")
Foo.camp="fooo"
#Foo.__dict__["camp"]="fooo"    #TypeError: 'mappingproxy' object does not support item assignment
print(getattr(Foo,"camp"))

2.1.4 delattr

delattr(object,name)

删除object对象的名称空间下的名叫name的属性
等价于:del object.name

#delattr
delattr(f,"name")   #等价于del f.name   <=====>f.__dict__.pop("name")
#print(f.name)


2.2 反射的两种形式

2.2.1 基于类和对象的反射

class Foo(object):
    staticField = "old boy"

    def __init__(self):
        self.name = 'wupeiqi'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return "bar"

print(getattr(Foo,'staticField'))
print(getattr(Foo,'func'))
print(getattr(Foo,'bar'))

结果:

old boy
<function Foo.func at 0x000001EDF525BB70>
<function Foo.bar at 0x000001EDF525BBF8>

2.2.2 基于模块的反射

在python中,每一个.py文件都是一个模块,而模块都是一个个对象。

#!/user/bin/env python
# -*- coding:utf-8 -*-
import sys

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

    def get_name(self):
        return self.name


#获取模块的名称
#print(__name__)
this_module=sys.modules[__name__]
print(this_module)  #获取本文件的模块对象,通过该对象可以调用本文件下的变量、函数、类等
#print(sys.modules)

print(hasattr(this_module,"Foo"))

print(getattr(this_module,"Foo"))

结果:

__main__
<module '__main__' from 'D:/devtools/workspace/python/PycharmProjects/py_fulstack_s4/day32 面向对象的进阶/基于模块的反射.py'>
True
<class '__main__.Foo'>

2.3 反射的用途(好处)

2.3.1 实现可插拔机制

可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能。

#!/user/bin/env python
# -*- coding:utf-8 -*-

class FtpClient:
    def __init__(self, addr):
        print("正在连接服务器[%s]" % addr)
        self.addr = addr

    #def get():
#!/user/bin/env python
# -*- coding:utf-8 -*-

from developer_one import FtpClient
f1=FtpClient("192.168.1.1")
if hasattr(f1,"get"):
    func_get=getattr(f1,"get")
    func_get()
else:
    print("不存在此方法")
    print("处理其他的逻辑")


#即使调用的developer_one中FtpClient模块的功能“get”有没有完成,都不影响程序的正常执行。这就叫可插拔机制。

2.3.2 用字符串导入模块

m=input(">>").strip()   #官方不推荐
m1=__import__(m)
print(m1.time())


import importlib   #官方推荐方案
t=importlib.import_module("time")

2.4 日常中的“反射”

#!/user/bin/env python
# -*- coding:utf-8 -*-

def delete():
    print("delete")

def add():
    print("add")

def search():
    print("search")

def modify():
    print("modify")

file_dict={                #相当于对象的名称空间
    "delete":delete,
    "add":add,
    "search":search,
    "modify":modify,
}

while True:
    choice=input(">>:").strip()
    if not choice:continue
    if choice in file_dict:     #相当于hasattr(file_dict,choice)
        func=file_dict[choice]   #getattr(file_dict,choice)
        func()

3 __setattr__,__delattr__,__getattr__

3.1 __setattr__

__setattr__ #在对象进行赋值(包括类的初始化)操作时,会自动调用

#!/user/bin/env python
# -*- coding:utf-8 -*-
class Foo:
    def __init__(self,name):
        self.name=name


    def __setattr__(self, key, value):   #对对象的属性进行赋值(包括类的初始化)操作时,会自动调用
        print("key : %s , value : %s "%(key,value))  #key : name , value : zhang
        print("key type:",type(key))    #key type: <class 'str'>
                                        #为什么key的类型是str? 我们说了什么时候会自动调用这个函数。是在对象进行赋值的时候,其中有赋值操作的是,初始化f时。f=Foo("zhang"),在内存里面真正执行的是:f.__dict__["name"]="zhang"。所以说key是一个str类型。
        print("value type:",type(value))    #value type: <class 'str'>
        #print(self.key)      #AttributeError: 'Foo' object has no attribute 'key'

        #self.key=value     #RecursionError: maximum recursion depth exceeded
        #setattr(self,key,value)    #RecursionError: maximum recursion depth exceeded
        self.__dict__[key]=value

f=Foo("zhang")

结果:

key : name , value : zhang 
key type: <class 'str'>
value type: <class 'str'>
__getattr__ age


3.2 __delattr__

__delattr__是在执行删除del object.name时,自动调用

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

    def __delattr__(self,item):    #在执行对象属性的删除操作时,会调用
        #del self.item  #RecursionError: maximum recursion depth exceeded while calling a Python object
        #del self.__dict__[item]
        self.__dict__.pop(item)

f=Foo("zhang")
del f.name
#print(f.name)

3.3 __getattr__

__getattr__ 查找的对象属性不存在,才会触发__getattr__;查找的对象属性存在,不会触发

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

    def __getattr__(self,item):      #查找的对象属性不存在,才会触发__getattr__;查找的对象属性存在,不会触发
        print("__getattr__",item)

f=Foo("zhang")
f.name    #未执行__getattr__
f.age     #__getattr__ age
#print(f.name)

结果:

__getattr__ age

3.4 用途

1 二次加工标准类型

关键在于继承。

class List(list):
    def append(self, obj):
        if not isinstance(obj,int):
            raise TypeError("must be int!")
        super().append(obj)

    def insert(self, index, obj):
        if not isinstance(obj,int):
            raise TypeError("must be int!")
        super().insert(index,obj)


li=List((1,2,3))
#li.insert(2,"-1")
li.insert(2,-1)
print(li)

2 以授权的方式实现定制数据类型

#!/user/bin/env python
# -*- coding:utf-8 -*-

class List:
    def __init__(self,li):
        self.x=list(li)

    def append(self,s):
        if isinstance(s,str):
            self.x.append(s)
        else:
            raise TypeError("传入列表的必须是字符串!")

    @property
    def mid(self):
        print("列表的中间值:",self.x[int(len(self.x)/2)])

    def __getattr__(self,item):
        if hasattr(self.x,item):
            return getattr(self.x,item)
        else:
            raise NameError("属性名错误!")

    def __str__(self):   #通过__str__来隐藏.x
        return str(self.x)

li=List((1,2,3,4))
li.append("s")
print(li)
li.mid
li.pop(3)
print(li)

结果:

[1, 2, 3, 4, 's']
列表的中间值: 3
[1, 2, 3, 's']