反射
绑定方法与非绑定方法
1.绑定方法
绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数传入
绑定给对象的方法:
类中定义的函数默认就是绑定给对象的
绑定给类的方法:
为类中定义的函数加上一个装饰器classmethod
2.非绑定方法(staticmethod)
非绑定方法: 既不与类绑定,又不与对象绑定,意味着对象和类都可以来调用,
无论谁来调用都是一个普通的函数,没有自动传值的效果;为类中定义的函数加上一个装饰器staticmethod。
class Foo: def f1(self): print(self) @classmethod def f2(cls): print(cls) # <class '__main__.Foo'> @staticmethod def f3(x,y): print('f3',x+y) obj=Foo() print(Foo.f1) # 就是一个普通函数 <function Foo.f1 at 0x0000000002965620> print(obj.f1) # 对象绑定方法 <bound method Foo.f1 of <__main__.Foo object at 0x0000000009F83C88>> obj.f1() # <__main__.Foo object at 0x0000000009F83C88> print(Foo.f2) # 类绑定方法 <bound method Foo.f2 of <class '__main__.Foo'>> Foo.f2() print(obj.f2) # <bound method Foo.f2 of <class '__main__.Foo'>> obj.f2() print(Foo.f3) # <function Foo.f3 at 0x0000000009FF4F28> print(obj.f3) # <function Foo.f3 at 0x0000000009FF4F28> Foo.f3(1,2) # f3 3 obj.f3(3,4) # f3 7
# settings.py ip='127.0.o.1' port='3306' # 当前文件 import settings class MySql: def __init__(self, ip, port): self.id = self.create_id() self.ip = ip self.port = port def tell_info(self): print('<id:%s ip:%s port:%s>' % (self.id, self.ip, self.port)) @classmethod def from_conf(cls): return cls(settings.IP, settings.PORT) @staticmethod def create_id(): import uuid return uuid.uuid4() # obj1=MySql('1.1.1.1',3306) # obj1.tell_info() # <id:9b13ad59-10e0-4b65-af15-b5d7d97034e1 ip:1.1.1.1 port:3306> obj2 = MySql.from_conf() obj2.tell_info()
class cal: cal_name = '计算器' def __init__(self,x,y): self.x = x self.y = y @property #在cal_add函数前加上@property,使得该函数可直接调用,封装起来 def cal_add(self): return self.x + self.y @classmethod #在cal_info函数前加上@classmethon,则该函数变为类方法,该函数只能访问到类的数据属性,不能获取实例的数据属性 def cal_info(cls): #python自动传入位置参数cls就是类本身 print('这是一个%s'%cls.cal_name) #cls.cal_name调用类自己的数据属性 @staticmethod #静态方法 类或实例均可调用 def cal_test(a,b,c): #改静态方法函数里不传入self 或 cls print(a,b,c) c1 = cal(10,11) cal.cal_test(1,2,3) #>>> 1 2 3 c1.cal_test(1,2,3) #>>> 1 2 3
isinstance和issubclass 内置函数
1.isinstance
判断一个对象是否是一个类的实例。
isinstance(参数1,参数2):用于判断参数1是否是参数2的一个实例。
isinstance(对象,类)
2.issubclass
判断一个类是否是另一个类的子类
issubclass(参数1, 参数2):用与判断参数1是否是参数2的子类。
print(isinstance(10,int)) # True class Foo: pass class Goo(Foo): pass foo_obj = Foo() print(isinstance(foo_obj, Foo)) # True print(isinstance(foo_obj, Goo)) # False print(issubclass(Goo, Foo)) # True foo_obj = Goo() print(isinstance(foo_obj, Goo)) # True print(isinstance(foo_obj, Foo)) # True
反射
反射定义
反射就是反省,自省的意思,指的是一个对象应该具备,可以检测,修改,增加自身属性的能力 ;通过字符串的形式操作对象相关的属性。
4个可以实现反省的函数(内置函数)
hasattr (参数1(对象),参数2) 判断某个对象是否存在某个属性;
getattr(参数1(对象),参数2,参数3) 从参数1中取出某个属性,第三个参数是默认 值,当属性不存在时,没有第三个参数报错,有的话,返回第三个参数;
setattr(参数1(对象),参数2(属性),参数3(属性值)),为对象添加新的属性
delattr(参数1(对象),参数2(属性名)):从对象中删除属性;
class People: country = 'China' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex p = People('tank', 17, 'male') # 普通方式 print('name' in p.__dict__) # True print('country' in People.__dict__) # True print('country2' in People.__dict__) # False # hasattr print(hasattr(p, 'name')) # True print(hasattr(People, 'country')) # True # 普通方式 print(p.__dict__.get('name')) # tank print(p.__dict__.get('level', '9')) # 9 # getattr print(getattr(p, 'name', 'jason_sb')) # tank print(getattr(p, 'name1', 'jason_sb')) # jason_sb print(getattr(People, 'country2', 'China')) # setattr # 普通 p.level = 10 print(p.__dict__) # {'name': 'tank', 'age': 17, 'sex': 'male', 'level': 10} # 反射 setattr(p, 'sal', '3.0') print(hasattr(p, 'sal')) # True print(p.__dict__) # {'name': 'tank', 'age': 17, 'sex': 'male', 'level': 10, 'sal': '3.0'} # delattr # 普通 del p.level print(hasattr(p, 'level')) # False # 反射 delattr(p, 'sal') print(hasattr(p, 'sal')) # False
class Movie: def input_cmd(self): print('输入命令:') while True: cmd = input('请输入执行的方法名:').strip() # 若用户输入的cmd命令是当前对象的属性 if hasattr(self, cmd): # if cmd in self.__class__.__dict__: method = getattr(self, cmd) method() else: print('命令错误,请重新输入!') def upload(self): print('电影开始上传...') def download(self): print('电影开始下载...') movie_obj = Movie() movie_obj.input_cmd()
反射使用场景
反射其实就是对属性的增删改查,但是如果直接使用内置的__dict__来操作,语法繁琐,不好理解
另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对 象是否满足要求,是否是我需要的属性和方法
应用:一般用在框架设计,反射被称为框架的基石;因为框架的设计者,不可能提前知道 你的对象到底是怎么设计的,所以你提供给框架的对象,必须通过判断验证之后才能正常 使用,判断验证就是反射要做的事情,当然通过__dict__也是可以实现的, 其实这些方法也就是对 __dict__的操作进行了封装
''' 需求:要实现一个用于处理用户的终端指令的小框架 框架就是已经实现了最基础的构架,就是所有项目都一样的部分 ''' # libs.plugins.py # libs文件夹下的plugins文件 # 插件部分 class WinCMD: def cd(self): print("wincmd 切换目录....") def delete(self): print("wincmd 要不要删库跑路?") def dir(self): print("wincmd 列出所有文件....") class LinuxCMD: def cd(self): print("Linuxcmd 切换目录....") def rm(self): print("Linuxcmd 要不要删库跑路?") def ls(self): print("Linuxcmd 列出所有文件....") # settings.py 文件 '''作为框架的配置文件''' # 作为框架使用者,在配置文件中指定你配合框架的类是哪个框架的使用者提供一个配置文件,要求对方将类的信息写入配置文件,然后框架自己去加载需要的模块 class_path = 'libs.plugins.WinCMD' # libs是plugins.py文件所在的文件夹 # myframework.py 文件 #框架的设计者 import importlib import settings # 框架已经实现的部分 def run(plugin): while True: cmd = input("请输入指令:") if cmd == "exit": break # 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测 # 判断对象是否具备处理这个指令的方法 if hasattr(plugin,cmd): # 取出对应方法方法 func = getattr(plugin,cmd) func() # 执行方法处理指令 else: print("该指令不受支持...") print("see you la la!") # 创建一个插件对象 调用框架来使用它 # wincmd = plugins.WinCMD() # 框架之外的部分就有自定义对象来完成 # 框架得根据配置文件拿到需要的类 path = settings.CLASS_PATH # 从配置中单独拿出来 模块路径和 类名称 module_path,class_name = path.rsplit(".",1) # libs.plugins,WinCMD #拿到模块 mk = importlib.import_module(module_path) # 拿到类 cls = getattr(mk,class_name) # 实例化对象 obj = cls() #调用框架 run(obj)