面向对象-反射和__getattr__系列 isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__getattr__ 定制自己的数据类型

面向对象-反射和__getattr__系列
isinstance(obj,cls)和issubclass(sub,super)
反射
__setattr__,__delattr__,__getattr__
定制自己的数据类型

isinstance(obj,cls)检查是否obj是否是类 cls 的对象

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

issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo:
    pass

class Bar(Foo):
    pass

issubclass(Bar,Foo)

反射

python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射).

# 反射
class People:
    country = "china"
    def __init__(self,name):
        self.name = name

    def walk(self):
        print("%s is walking" % self.name)

p = People("egon")
print(p.__dict__)           #     {'name': 'egon'}
print(People.__dict__)       # {'__weakref__': <attribute>,...... '__module__':  'country': 'china'}

# hasattr检测是否含有某属性
print(hasattr(p, "name"))          # True
print(hasattr(p, "country"))       # True
print(hasattr(p, "walk"))          # True
print(hasattr(People, "name"))     # False
print(hasattr(People, "country"))  # True
print(hasattr(People, "walk"))     # True


# getattr(obj,name)获取属性
n1 = getattr(p,"country")  # china
n2 = getattr(p,"name")     # egon
n3 = getattr(p,"walk")     # bound method
n4 = getattr(People,"country") # china
n5= getattr(People,"name")  # AttributeError,没这个属性
n6= getattr(People,"walk")    # function method


# setattr(x, y, v)设置属性
setattr(p, "age", 18)
setattr(p, "func", lambda self:self.name + "_pirate")
print(type(p.func))  # <class 'function'>,setattr设置的是类属性,不是绑定对象的方法
print(p.func(p))     # egon_pirate
print(p.age)         # 18


# delattr(x, y) 删除属性
delattr(p, "name")
delattr(p, "walk") # AttributeError,不能删除绑定方法
delattr(People, "walk")
delattr(p,"fa")      # 没有属性也报错

反射当前模块的属性

##反射当前模块的属性
import sys
x = 1111
class Foo:
    pass
def s1():
    print("from function s1")

def s2():
    print("S2")

this_module = sys.modules[__name__]  # 拿到当前模块的属性

print(this_module)                # <module '__main__' from 'C:/workspace/test.py'>
print(hasattr(this_module,"s1"))  # True
print(hasattr(this_module,"s2"))  # True
print(hasattr(this_module,"s3"))  # False
print(hasattr(this_module, "Foo")) # True
f = getattr(this_module,"s1")    
f()    # from function s1

反射当前模块属性应用之增删改查

# 应用之增删改查
def add():
    print("---add function---")


def delete():
    print("---delete function---")


def modify():
    print("---modify function---")


def search():
    print("---search function---")


import sys
this_module = sys.modules[__name__]
# print(this_module.__dict__)      # 'search': <function search at 0x017A5228>...
while True:
    cmd = input(">>:").strip()
    if hasattr(this_module, cmd):      # 判断当前模块是否有这个属性
        f = getattr(this_module, cmd)  # 得到该属性
        f()   # 运行函数
    else:
        print("没有该功能请继续输入")

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

# FtpClient.py文件
class FtpClient:
    ' ftp客户端,但是还么有实现具体的功能 '
    def __init__(self,addr):
        print('正在连接服务器[%s]' %addr)
        self.addr=addr


    # def download(self):
    #     print("download...")
# FtpServer文件
from FtpClient import FtpClient
data = FtpClient("192.168.1.123")
if hasattr(data, "download"):
    func = getattr(data, "download")
    func()   # FtpClient存在该属性,则拿到执行,不存在执行else
else:
    print("不存在此方法")
    print("处理其他逻辑")

通过字符串导入模块

# 通过字符串导入模块
# 推荐使用
import importlib
t = importlib.import_module("time")
print(t.strftime("%Y-%m-%d %X")) # 2017-04-24 15:53:42

# 不推荐
t = __import__("time")
print(t.strftime("%Y-%m-%d %X"))  # 2017-04-24 15:54:26

__setattr__,__delattr__,__getattr__

#**__setattr__,__delattr__,__getattr__基本用法**
class Foo:
    def __init__(self,name):
        self.name = name

    def __getattr__(self, item):   # 属性不存在才触发
        print("getattr--->%s %s" % (item, type(item)))

    def __setattr__(self, key, value):
        if not isinstance(value,str):  # 限制了所有设置属性必须为str,和@property不同
            raise TypeError("must be str")
        print("----------setter")
        self.__dict__[key] = value

    def __delattr__(self, item):   # __delattr__删除属性的时候会触发
        print("-------------deltter")
        self.__dict__.pop(item)


f = Foo("egon")
# __getattr__只有在使用点调用属性且属性不存在的时候才会触发
print(f.name)   # egon, 正常执行
print(f.fjkad)  # getattr--->fjkad <class 'str'>,找不到触发__getattr__


# __setattr__添加/修改属性会触发它的执行
f.nickname = "邹润成"
print(f.__dict__)  # {'name': 'egon', 'nickname': '邹润成'}
f.__dict__['a']=3  # 可以直接修改属性字典,来完成添加/修改属性的操作


# __delattr__删除属性的时候会触发
del f.nickname
print(f.__dict__)  # {'name': 'egon'}

定制自己的数据类型

python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识
使用继承定制自己的数据类型

# 基于继承标准类型定制自己的数据类型
class List(list):
    def append(self, p_object):
        if not isinstance(p_object, int): # 为append方法,进行类型限制 
            raise TypeError("must be int")
        super().append(p_object)


    @property
    def mid(self):
        "新增加的属性,取中间值"
        index = len(self)//2
        return self[index]


a = List([1, 2, 3])
a.append(4)
a.append(5)
# a.append("5") # TypeError: must be int
print(a)      # [1, 2, 3, 4, 5]
print(a.mid)  # 3

使用授权的方法定制数据类型

# 作业:
# 	基于授权定制自己的列表类型,要求定制的自己的__init__方法,
# 	定制自己的append:只能向列表加入字符串类型的值
# 	定制显示列表中间那个值的属性(提示:property)
# 	其余方法都使用list默认的(提示:__getattr__加反射)
class LIST:
    def __str__(self):
        return str(self.seq)
    def __init__(self,*args):
        self.seq = list(*args)


    def append(self, value):
        if not isinstance(value, str):
            raise TypeError("必须传字符串")
        self.seq.append(value)

    @property
    def mid(self):
        index = len(self.seq)//2
        return self.seq[index]


    def __getattr__(self, item):
        return getattr(self.seq, item)


l = LIST(["1","2","3"])
l.append("4")
l.append("5")
print(l)        # ['1', '2', '3', '4', '5']
print(l.mid)    # 3
print(l.pop())  # 5
print(l)        # ['1', '2', '3', '4']

使用授权的方法模拟日志

# 不使用继承模拟日志
import time
class Open:
    def __init__(self,filepath,mode="r",encoding="utf8"):
        self.x = open(filepath, mode=mode, encoding=encoding)
        self.filepath = filepath
        self.modde = mode
        self.encoding = encoding

    def write(self, line):
        print("from f ", line)
        t = time.strftime("%Y-%m-%d %X")
        self.x.write("%s %s
" % (t, line))


f = Open("a.txt", "w", "utf8")
f.write("111111111")
f.write("222222222")
f.write("333333333")

增强版

# 加强版
import time
class Open:
    def __init__(self,filepath,mode="r", encoding="utf8"):
        self.x = open(filepath, mode=mode, encoding=encoding)
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding

    def write(self, line):
        print("from f ", line)
        t = time.strftime("%Y-%m-%d %X")
        self.x.write("%s %s
" % (t, line))


    def __getattr__(self, item):
        return getattr(self.x, item)


f = Open("a.txt")
a = f.read()
print(a)
# 2017-04-24 17:15:36 111111111
# 2017-04-24 17:15:36 222222222
# 2017-04-24 17:15:36 333333333