首页 > 代码库 > 面向对象进阶

面向对象进阶

一、isinstance(obj,cls)和issubclass(sub,super)

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

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

class Bar:
    pass
class Foo(Bar):
    pass
f=Foo()
print(Foo.__bases__)
print(isinstance(f,Foo))
print(issubclass(Foo,Bar))  

二、反射

1、什么是反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改他本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩

2、Python面向对象中的反射:通过字符串的形式操作对象相关的属性。Python中的一切皆对象(都可以使用反射)

  多了一种属性引用方式,之前是通过“.”现在是通过字符串

  反射是指就是把字符串当做属性,就是通过四个attr把对属性的操作变成对字符串的操作

  四个attr过来过去就是操作的__dict__里面的key_str

# class People:
#     country="China"
#     def __init__(self,name):
#         self.name=name
#     def walk(self):
#         print("%s is walking" %self.name)
# p=People("egon")
#检测是否含有某属性
# print(hasattr(p,"name"))#判断对象p有没有这个属性
#相当于if "name" in p.__dict__

#获取属性,通过__dict__的key值获取value
# x=getattr(p,"name")#由下面的打印结果看,她就相当于p.name
# print(x)#egon,相当于print(p.name)
# y=getattr(p,"xxx","找不到")
# print(y)#找不到,getattr可以自定义返回值

#设置属性,__dict__的增,改
# setattr(p,"name","alex")
# setattr(p,"sb",True)
# setattr(p,"bs",lambda self:self.name+"sb")#可以增加函数属性,是函数属性,而不是绑定方法
# s=getattr(p,"bs")#调用增加的函数属性
# print(s(p))
# print(p.__dict__)#就是加到这个字典里面了

#删除属性
# delattr(p,"bs")#删除函数属性就是删除函数名
# delattr(p,"sb")#删除数据属性
# print(p.__dict__)

# 总结-------------------------------------------------------------
#hasattr(object,"name") #查看,object可以是对象,类,模块,name是属性名
#相当于判断能不能找的到"name" 这个名字,如果可以找到print返回True

# getattr(object,"name",default=None) #获取,object可以是对象,类,模块
# p.name

# setattr(x,"y","v") #修改,x可以是对象,类,模块,y是属性名,v是新值
# p.name="alex"

# delattr(x,"y")#删除,x可以对象,类,模块,y是属性名
# del p.name
# -----------------------------------------------------------------
#反射当前模块的属性
#这个文件为test
#模块就是文件,定义文件也是定义一堆名字,一切皆对象,只要是对象就能.出来名字
#名字就对应属性的概念
import sys
#导入一个模块名
x=1111
class Foo:  #定义一个类名
    pass

def s1():  #定义一个函数名
    print("s1")

def s2():  #定义一个函数名
    print("s2")

# print(__name__)#__main__
# __name__能够用来区分文件(模块)是直接运行(作为脚本)文件还是作为模块被导入
#如果是脚本的话,打印__main__,如果作为模块被导入时,打印模块名
#脚本:把一个文件当成一个独立的程序去运行
#如何在自己模块获取自己模块(要结合上面的__name__知识点去理解)
    #不能再自己模块中导入自己模块
    #下面就是正确用法,需要借助sys模块
this_module=sys.modules[__name__] #这就相当于拿到当前的模块test作为对象
print(this_module)#<module ‘__main__‘ from ‘D:/py_fullstack_s4/day31/test.py‘>
print(hasattr(this_module,"s1"))
print(getattr(this_module,"s2"))

#如何在当前模块调用其他模块(比较简单)
    #比如我现在有两个文件,在同一级目录,test和test1我在test1中调用test会出现下面的情况
#这个文件事test1
#如何在当前模块调用其他模块(比较简单)
    #比如我现在有两个文件,在同一级目录,test和test1我在test1中调用test会出现下面的情况
import test
print(test)#<module ‘test‘ from ‘D:\\py_fullstack_s4\\day31\\test.py‘>
#会把test模块的完整路径打印出来
#现在我们把test模块作为一个对象
print(test.x)
print(test.s1)
test.s1()
#上边都是作为理解的,都没有说到反射,下面正式加入反射
print(hasattr(test,x))#判断test模块中有没有x属性
print(getattr(test,s1))#获取test中的s1属性

  

3、为什么用反射和反射的好处

  好处一:可插拔机制

  有两个程序员lili和egon,lili在写程序的时候需要egon的类,但是egon没有完成这个类,lili想到了反射,使用反射机制后可以继续完成自己的代码,egon不会影响lili的工作

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

#egon还没有完成全部功能
#class FtpClient:
#    ‘ftp客户端,但是还没有实现具体功能‘
#    def __init__(self,addr):
#        print(‘正在连接服务器[%s]‘ %addr)
#        self.addr=addr
#不影响lili代码的编写
# import ftpclient
# f1 = ftpclient.FtpClient("192.168.1.1")
# if hasattr(f1,"get"): #get属性就是egon还没有完成的功能
#     func_get=getattr(f1,"get")
#     func_get()
# print("不存在此方法")
# print("处理其他逻辑")

  好处二:动态导入模块(基于当前模块)

#通过字符串导入模块
#把字符串变为模块名也算映射
#之前学的
# m=input("请输入您要导入的模块:")#输入time
# m1=__import__(m)
# print(m1)#<module ‘time‘ (built-in)>
# print(m1.time())

#官方推荐的字符串作为模块的导入方法
# import importlib
# m=input("请输入您要导入的模块:")#输入time
# t=importlib.import_module(m) #这里可以用到反射
# print(t)#<module ‘time‘ (built-in)>
# print(t.time())
#------------------------------------------------------------------------------------
# __import__("import_lib.metaclass") #这是解释器自己内部用的
# importlib.import_module("import_lib.metaclass")#与上面这句效果一样,官方建议用这个

  还有个用途

#之前我们学的是判断字典里有没有key,有的话字典["key"]去运行
# def add():
#     print("add")
# def change():
#     print("change")
# def search():
#     print("search")
# def delete():
#     print("delete")
# func_dic={
#     "add":add,
#     "change":change,
#     "search":search,
#     "delete":delete
# }
# while True:
#     cmd=input(">>: ").strip()
#     if not cmd:continue
#     if cmd in func_dic: #hasattr()
#         func=func_dic.get(cmd)  #func=getattr()
#         func()

#######################################################################################################################

#学完面向对象后,可以通过反射搞
#思想是:借用sys,导入当前模块判断输入的字符串在不在模块中(hasattr),在的话执行(getattr获取后运行)
# import sys
# def add():
#     print("add")
# def change():
#     print("change")
# def search():
#     print("search")
# def delete():
#     print("delete")
# this_moudle=sys.modules[__name__]#导入当前模块
# while True:
#     cmd=input(">>: ").strip()
#     if not cmd:continue
#     if hasattr(this_moudle,cmd): #判断有没有cmd
#         func=getattr(this_moudle,cmd) #获取属性
#         func() #运行属性

  

三、内置函数__attr__系列

#这里和之前的那四个东西,就多了触发运行
#三个attr本质上都是对__dict__去操作
#一碰到给对象设置(增,改)属性就会触发__setattr__(self, key, value),加上类型限制,弥补Python没有类型限制的不足
#一碰到给对象删除属性就会触发__delattr__(self, item),加上限制,满足什么条件再删
#属性不存在的情况下才会触发 __getattr__(self, item)
#内置函数都是操作的内置的东西,__attr__操作__dict__

# 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

  

四、加工定制自己的数据类型

  包装:Python为大家提供了标准的数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工)

 

 

 

 

 

 

 

 

 

  

  

  

面向对象进阶