面向对象高级(上) 1 判断类型和子类 2 反射 3 __getattr__等 4 授权 5 包装

  判断对象是否是属于某个类的

isinstance(对象名, 类名)

    注意: 如果是它的父类, 同样也返回True; 但是 (父类的对象, 子类) 返回的是Fale

  查看父类, 注意是使用类名而不是对象

类名.__bases__

  判断是否是某个类的子类

issubclass(子类名, 父类名)

2 反射

  反射是指程序可以访问、检测和修改它本身状态或行为的一种能力  

2.1 反射参数

  可以通过不同的字符串来反射不同的参数, 进而进行一定的操作; 或者反射一些函数来执行

  在Python中主要是使用内置函数hasattr, getattr, setattr, delattr三个函数

  hassattr(), 判断对象或者类是否具有摸个参数, 第一个参数是类或者对象, 第二个参数是字符串类型的名字, 返回值是True或者False

class People:
     def run(self):
         print('running..')

print( hasattr(People() , 'run') )
print( hasattr(People , 'run') )

  getattr(), 获取对象或者类的参数, 第一个参数是类或者对象, 第二个参数是字符串类型的名字,但是使用对象和类室友区别的

  另外还可以设置第三个参数, 当该属性不存在的时候返回那个值

whc = People()
func = getattr(whc, 'run')
func()
func = getattr(People, 'run')
func(whc)
print( getattr(whc, 'running', '该属性不存在') )

  setattr(), 设置属性, 同样可以给对象和类绑定

whc = People()
setattr(whc, 'name', 'weihuchao')
setattr(People, 'country', 'China')
print(whc.__dict__)
print(People.__dict__)

  delattr(), 删除属性

delattr(whc, 'name')
delattr(People,'country')
print(whc.__dict__)
print(People.__dict__)

  使用反射的好处, 可插拔机制

    因为可以查看类中是否含有某属性, 在不同模块写程序的时候, 只需要判断该属性有没有就可使程序健壮的完成, 而不必要等待相关联的模块完成

2.2 反射模块

  在正常使用的导入模块是使用import或者from, 但是要反射某个模块, 同样是根据输入的字符串的来反射  

  具体的反射有两种操作, 一个是内置函数__import__(), 另一个是使用模块importlib

  具体模式如下

返回值 = __import__(模块名字字符串)
#或者
import importlib
返回值 = importlib.import_module(模块名字字符串)

  返回值就是相当于反射回来的命名空间的名字

  一般建议使用第二种方法

name = 'time'
nameSpace = __import__(name)
print(nameSpace.time())
#或者
import importlib
name = 'time'
nameSpace = importlib.import_module(name)
print(nameSpace.time())

  另外反射的例子

3 __getattr__等

  在类中, 还可以定义__getattr__(), __setattr__() 和 __deltattr__()

  定义了__setattr__() 和 __deltattr__()函数之后, 类中所有的设置, 删除属性和方法的时候都会调用它

    具体来说, 由于设置了这两个函数, 所以在完成设置和删除功能的时候, 不能再使用赋值语句和setattr()这个内置函数的处理了

    要处理结果需要使用__dict__属性来完成

    __setattr__() 默认有三个参数, 一个是self, 第二个是键, 第三个是值

    __deltattr__()认有两个个参数,一个self, 另一个是需要删除的属性的名字

  但是__getattr__()函数特殊, 它是在调用attr不存在的时候才调用, 基于这个特性有很强的使用效果

  具体的实例如下

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

4 授权

  授权是__getattr__()函数的应用

  具体实例是实现一个记录日志的Open类

  在__init__()函数中, 可以通过传入的内容来获得文件句柄, 再编写一个write()函数, 通过文件句柄写入日志信息即可

  但是除了wirte()函数功能之外别的文件函数像close()和flush()等都不需要修改的, 但是总不能每一个都写一遍

  所以利用__getattr__()不存在属性调用的特性, 利用__getattr__()有两个参数一个是self另一个就是获取的属性名这个特性, 通过内置函数getattr()来具体取得那些相关属性 

  具体代码如下

import time
class LogFile:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename,mode,encoding=encoding)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %T')
        self.file.write('%s %s' %(t,line))
        
    def __getattr__(self, item):
        print("aaa")
        return getattr(self.file,item)

f1=LogFile('log', 'w+')
f1.write('create logs..')
f1.seek(0)
print(f1.read())
f1.close()

5 包装

  通过继承基本类型list, 来获得list的全部功能, 通过重写append()方法, 来加入对添加进来的值进行类型判断, 从而实现自定义功能

  具体代码如下

class List(list):
    def append(self, p_object):
        if not isinstance(p_object,int):
            raise TypeError('插入的值必须是int类型的!')
        super().append(p_object)

myList = List([1,2,3,4])
print(myList)
myList.append(5)
print(myList)
try:
    myList.append('test')
except:
    print("输入值类型不正确")
print(myList)