python中的反射功能是由以下四个内置函数提供:hasattr、getattr、setattr、delattr,改四个函数分别用于对对象内部执行:检查是否含有某成员、获取成员、设置成员、删除成员。
1反射:通过字符串的形式导入模块通过字符串的形式,去模块中寻找指定的函数,并执行__import__(字符串)将字符串作为模块名导入赋值的话就相当于as反射:根据字符串的形式取某个模块中寻找东西根据字符串的形式取某个模块中判断东西是否存在根据字符串的形式去某个模中设置东西根据字符串的形式取某个模块中删除的东西根据字符串的形式去对象(某个模块)中操作其成员1classFoo(object):23def__init__(self):4self.name='wupeiqi'56deffunc(self):7return'func'89obj=Foo()1011#####检查是否含有成员####12hasattr(obj,'name')13hasattr(obj,'func')1415#####获取成员####16getattr(obj,'name')17getattr(obj,'func')1819#####设置成员####20setattr(obj,'age',18)21setattr(obj,'show',lambdanum:num+1)2223#####删除成员####24delattr(obj,'name')25delattr(obj,'func')1__import__("模块名的字符串")
from包import模块名as别名===等价于===别名=__import__("模块名的字符串")
1commons模块内容23deff1():4return"f1"56deff2():7return"f2"89deff3():10return"f3"
__import__导入字符串作为模块练习#正常导入importcommonsasCC#==特殊导入(字符串导入)DD=__import__("commons")ret=commons.f1()print(ret)1.1特殊__import__情况
针对该模块同一级下有另一个目录,也就是包,而这个包下有另一个包,而我们需要导入的模块还在其下面,这时候,不能应用包.包.模块作为字符串传入__import__来导入了,因为其只会导入第一层包。需要加入一个参数fromlist=True
1__import__("a.b.c.file.login",fromlist=True)1.2模块导入扩展
1扩展:2导入模块3#单层4a=__import__("模块名")56#多层7#当前目录下有好几层8#19froma.b.c.fileimportlogin10#211__import__("a.b.c.file.login")#是不对的只导入了a目录包12正确写法:13__import__("a.b.c.file.login",fromlist=True)2getattr反射可以传入字符串获取变量名或函数
1#正常导入2importcommonsasCC#==特殊导入(字符串导入)DD=__import__("commons")3ret=commons.f1()4print(ret)5678#应用9#根据用户输入字符串,将字符串作为模块名导入并应用其内的方法10mod_name=input("请输入模块:").strip()11print(mod_name,type(mod_name))12DD=__import__(mod_name)#通过字符串形式导入模块13#ret=DD.f1()#正常调用模块中寻找函数,并执行14fuc_name=input("请输入执行的函数:")1516ret=getattr(DD,fuc_name)()17print(ret)
3hasattr反射可以传入字符串判断模块是否有这个变量名或函数
1#正常导入2importcommonsasCC#==特殊导入(字符串导入)DD=__import__("commons")3ret=commons.f1()4print(ret)5678#应用9#根据用户输入字符串,将字符串作为模块名导入并应用其内的方法10mod_name=input("请输入模块:").strip()11print(mod_name,type(mod_name))12DD=__import__(mod_name)#通过字符串形式导入模块13#ret=DD.f1()#正常调用模块中寻找函数,并执行14fuc_name=input("请输入执行的函数:")15ifhasattr(DD,fuc_name):16ret=getattr(DD,fuc_name)()17print(ret)
4简写输入url尾部判断,反射执行不同的模块的不同函数
1fromlibimportaccount2inp_url=input("请输入url:")3#4#ifinp_url.endswith("login"):5#print(account.login())6#elifinp_url.endswith("logout"):7#print(account.logout())8#9#else:10#print(account.nb())111213#防止出现不能用的场景14#115#inp=inp_url.split("/")[-1]16#ifhasattr(account,inp):17#ret=getattr(account,inp)18#print(ret())19#else:20#print(404)212223#2扩展124target_module,target_func=inp_url.split("/")25m=__import__("lib."+target_module,fromlist=True)2627ifhasattr(m,target_func):28ret=getattr(m,target_func)29print(ret())30else:31print(404)5反射的四中方法介绍
1getattr2#13importcommons4target_func=getattr(commons,"f1")5target_func()6#27DD=__import__("commons)8target_func=getattr(DD,"f1")9#设置默认值,没有找到为默认值10target_func=getattr(DD,"f1",None)11#还可以获取全局变量12str_=getattr(DD,"name",None)13target_func14hasattr15#判断函数或变量存在与否,trueorfalse1617r=hasattr(commons,"name")1819setattr20设置变量或者定义函数2122fuc=setattr(commons,"f4",lambdax:x+2)23str_s=setattr(commons,"name","alex")2425delattr26删除变量或者定义函数27f=delattr(commons,"name")详细解析:
当我们要访问一个对象的成员时,应该是这样操作:
1classFoo(object):23def__init__(self):4self.name='alex'56deffunc(self):7return'func'89obj=Foo()1011#访问字段12obj.name13#执行方法14obj.func()那么问题来了?a、上述访问对象成员的name和func是什么?答:是变量名b、obj.xxx是什么意思?答:obj.xxx表示去obj中或类中寻找变量名xxx,并获取对应内存地址中的内容。c、需求:请使用其他方式获取obj对象中的name变量指向内存中的值“alex”1classFoo(object):23def__init__(self):4self.name='alex'56#不允许使用obj.name7obj=Foo()答:有两种方式,如下:
1classFoo(object):23def__init__(self):4self.name='alex'56deffunc(self):7return'func'89#不允许使用obj.name10obj=Foo()1112printobj.__dict__['name']1classFoo(object):23def__init__(self):4self.name='alex'56deffunc(self):7return'func'89#不允许使用obj.name10obj=Foo()1112printgetattr(obj,'name')d、比较三种访问方式
答:第一种和其他种比,...第二种和第三种比,...
Web框架实例
类也是对象
1classFoo(object):23staticField="oldboy"45def__init__(self):6self.name='wupeiqi'78deffunc(self):9return'func'1011@staticmethod12defbar():13return'bar'1415printgetattr(Foo,'staticField')16printgetattr(Foo,'func')17printgetattr(Foo,'bar')
模块也是对象
1home.py234defdev():5return'dev'
1程序目录:2home.py3index.py45当前文件:6index.py7"""8910importhomeasobj1112#obj.dev()1314func=getattr(obj,'dev')15func()
二、面向对象
1面向对象23面向对象不是所有情况都适用45类里面的方法都不属于模块了,而是属于类。678classOldboy:9deffetch(self,backend):10pass11defadd_record(self,backend,record):12pass1314obj=Oldboy()#实例化类,创建obj15obj.techt("www.oldboy.org")16obj.add_record("www.oldboy.org",XXXXXXX)
面向对象编程a定义类class类名:def方法1(self,参数1,参数2):passb根据类创建对象使用对象取执行类中方法1面向对象的self解释写在前面:self其实就是对象名,实例化成什么对象,self就代表什么对象、打印self的内存地址
1#打印self的内存地址23classOldboy(object):45deffecht(self,backend):6print(backend,self)7defadd_record(self,backend,record):8pass9"""rf103self:对象名11调用方法的时候,python默认会把对象实例赋值个self传入方法12"""13obj1=Oldboy()14print("obj1:",obj1)#obj1:<__main__.Oldboyobjectat0x0000000000B41400>15obj1.fecht("bbbbackend")#bbbbackend<__main__.Oldboyobjectat0x0000000000B41400>
面向过程编程最易被初学者接受,其往往用一长段代码来实现指定功能,开发过程中最常见的操作就是粘贴复制,即:将之前实现的代码块复制到现需功能处。
1类+括号,====》自动执行类中的__init__方法;创建了一个对象23在__init__方法中执行具体封装的操作4__init__有一个特殊的名字:构造方法56=====>>>>初始化789__del__解释器销毁对象的时候自动调用,特殊的名字:析构方法
#2利用构造方法封装因为类实例化为对象默认执行init构造方法
1classOldboy2(object):23def__init__(self,bk):4self.name="alex"#直接封装一个变量到self内,而self则生成那个对象就等于哪个对象,作为所有对象的公共参数5self.backend=bk#这个封装是针对不同对象实例传入不同的参数,而进行的封装,属于各自对象,默认实例化传入参数,self为对象名,而bk是参数,而这里又将参数封装给self.backend,而self等于对象,所以将backend封装给了对象67deffecht(self):8print(self.backend)9defadd_record(self,backend,record):10pass11obj2=Oldboy2("www.obj2.org")12obj2.fecht()1314obj3=Oldboy2("www.obj3.org")15obj3.fecht()
面向对象编程是一种编程方式,此编程方式的落地需要使用“类”和“对象”来实现,所以,面向对象编程其实就是对“类”和“对象”的使用。
类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
ps:类中的函数第一个参数必须是self(详细见:类的三大特性之封装)类中定义的函数叫做“方法”
1#创建类2classFoo:34defBar(self):5print'Bar'67defHello(self,name):8print'iam%s'%name910#根据类Foo创建对象obj11obj=Foo()12obj.Bar()#执行Bar方法13obj.Hello('wupeiqi')#执行Hello方法
面向对象的三大特性是指:封装、继承和多态。
一、封装
封装:使用场景:当同一类型的方法具有形同参数时候,直接封装到对象即可使用场景:把类当做模板,创建多个对象()
封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。
所以,在使用面向对象的封装特性时,需要:
第一步:将内容封装到某处
self是一个形式参数,当执行obj1=Foo('wupeiqi',18)时,self等于obj1
当执行obj2=Foo('alex',78)时,self等于obj2
所以,内容其实被封装到了对象obj1和obj2中,每个对象中都有name和age属性,在内存里类似于下图来保存。
第二步:从某处调用被封装的内容
调用被封装的内容时,有两种情况:
1、通过对象直接调用被封装的内容
上图展示了对象obj1和obj2在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名
1classFoo:23def__init__(self,name,age):4self.name=name5self.age=age67obj1=Foo('wupeiqi',18)8printobj1.name#直接调用obj1对象的name属性9printobj1.age#直接调用obj1对象的age属性1011obj2=Foo('alex',73)12printobj2.name#直接调用obj2对象的name属性13printobj2.age#直接调用obj2对象的age属性1#1封装封装参数到对象里面,而self==对应的对象名所以对象.变量可以通过self.变量调取2classOldboy1(object):34deffecht(self):5print(self.backend)6defadd_record(self,backend,record):7pass8obj2=Oldboy1()9obj2.backend="www.obj2.org"#封装到对象里面的变量10obj2.fecht()1112obj3=Oldboy1()13obj3.backend="www.obj3.org"14obj3.fecht()如上代码第一种封装封装参数到对象里面,而self==对应的对象名所以对象.变量可以通过self.变量调取
如下代码第二种封装利用构造方法封装因为类实例化为对象默认执行init构造方法
1#2利用构造方法封装因为类实例化为对象默认执行init构造方法2classOldboy2(object):34def__init__(self,bk):5self.name="alex"#直接封装一个变量到self内,而self则生成那个对象就等于哪个对象,作为所有对象的公共参数6self.backend=bk#这个封装是针对不同对象实例传入不同的参数,而进行的封装,属于各自对象,默认实例化传入参数,self为对象名,而bk是参数,而这里又将参数封装给self.backend,而self等于对象,所以将backend封装给了对象78deffecht(self):9print(self.backend)10defadd_record(self,backend,record):11pass12obj2=Oldboy2("www.obj2.org")13obj2.fecht()1415obj3=Oldboy2("www.obj3.org")16obj3.fecht()调用被封装的内容时,有两种情况:
1classFoo:23def__init__(self,name,age):4self.name=name5self.age=age67obj1=Foo('wupeiqi',18)8printobj1.name#直接调用obj1对象的name属性9printobj1.age#直接调用obj1对象的age属性1011obj2=Foo('alex',73)12printobj2.name#直接调用obj2对象的name属性13printobj2.age#直接调用obj2对象的age属性2、通过self间接调用被封装的内容
执行类中的方法时,需要通过self间接调用被封装的内容
1classFoo:23def__init__(self,name,age):4self.name=name5self.age=age67defdetail(self):8printself.name9printself.age1011obj1=Foo('wupeiqi',18)12obj1.detail()#Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的self=obj1,即:self.name是wupeiqi;self.age是181314obj2=Foo('alex',73)15obj2.detail()#Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的self=obj2,即:self.name是alex;self.age是78综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容。
练习一:在终端输出如下信息
函数式编程
1defkanchai(name,age,gender):2print"%s,%s岁,%s,上山去砍柴"%(name,age,gender)345defqudongbei(name,age,gender):6print"%s,%s岁,%s,开车去东北"%(name,age,gender)789defdabaojian(name,age,gender):10print"%s,%s岁,%s,最爱大保健"%(name,age,gender)111213kanchai('小明',10,'男')14qudongbei('小明',10,'男')15dabaojian('小明',10,'男')161718kanchai('老李',90,'男')19qudongbei('老李',90,'男')20dabaojian('老李',90,'男')面向对象
1classFoo:23def__init__(self,name,age,gender):4self.name=name5self.age=age6self.gender=gender78defkanchai(self):9print"%s,%s岁,%s,上山去砍柴"%(self.name,self.age,self.gender)1011defqudongbei(self):12print"%s,%s岁,%s,开车去东北"%(self.name,self.age,self.gender)1314defdabaojian(self):15print"%s,%s岁,%s,最爱大保健"%(self.name,self.age,self.gender)161718xiaoming=Foo('小明',10,'男')19xiaoming.kanchai()20xiaoming.qudongbei()21xiaoming.dabaojian()2223laoli=Foo('老李',90,'男')24laoli.kanchai()25laoli.qudongbei()26laoli.dabaojian()上述对比可以看出,如果使用函数式编程,需要在每次执行函数时传入相同的参数,如果参数多的话,又需要粘贴复制了...;而对于面向对象只需要在创建对象时,将所有需要的参数封装到当前对象中,之后再次使用时,通过self间接去当前对象中取值即可。
练习二:游戏人生程序
1、创建三个游戏人物,分别是:
2、游戏场景,分别:
游戏代码
继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。
例如:
猫可以:喵喵叫、吃、喝、拉、撒
狗可以:汪汪叫、吃、喝、拉、撒
如果我们要分别为猫和狗创建一个类,那么就需要为猫和狗实现他们所有的功能,如下所示:
1class猫:23def喵喵叫(self):4print'喵喵叫'56def吃(self):7#dosomething89def喝(self):10#dosomething1112def拉(self):13#dosomething1415def撒(self):16#dosomething1718class狗:1920def汪汪叫(self):21print'喵喵叫'2223def吃(self):24#dosomething2526def喝(self):27#dosomething2829def拉(self):30#dosomething3132def撒(self):33#dosomething3435伪代码上述代码不难看出,吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次。如果使用继承的思想,如下实现:
动物:吃、喝、拉、撒
猫:喵喵叫(猫继承动物的功能)
狗:汪汪叫(狗继承动物的功能)
伪代码
1class动物:23def吃(self):4#dosomething56def喝(self):7#dosomething89def拉(self):10#dosomething1112def撒(self):13#dosomething1415#在类后面括号中写入另外一个类名,表示当前类继承另外一个类16class猫(动物):1718def喵喵叫(self):19print'喵喵叫'2021#在类后面括号中写入另外一个类名,表示当前类继承另外一个类22class狗(动物):2324def汪汪叫(self):25print'喵喵叫'2627伪代码所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。
注:除了子类和父类的称谓,你可能看到过派生类和基类,他们与子类和父类只是叫法不同而已。
学习了继承的写法之后,我们用代码来是上述阿猫阿狗的功能:
1classAnimal:23defeat(self):4print"%s吃"%self.name56defdrink(self):7print"%s喝"%self.name89defshit(self):10print"%s拉"%self.name1112defpee(self):13print"%s撒"%self.name141516classCat(Animal):1718def__init__(self,name):19self.name=name20self.breed='猫'2122defcry(self):23print'喵喵叫'2425classDog(Animal):2627def__init__(self,name):28self.name=name29self.breed='狗'3031defcry(self):32print'汪汪叫'333435##########执行#########3637c1=Cat('小白家的小黑猫')38c1.eat()3940c2=Cat('小黑的小白猫')41c2.drink()4243d1=Dog('胖子家的小瘦狗')44d1.eat()2.1继承的两种情况
继承:1子类可以用父类的所有方法父类==基类子类==派生类2派生类和子类中都有同一个方法,调用派生类时候优先执行派生类的方法3多继承派生类可以继承多个类,这在c#和java是不可以的可以多个父类的所有功能优先级:从左到右,如果本身派生类中有就优先执行本身的功能派生类中没有基类的功能:1#1派生类中没有基类的功能:23classAnimals:45defchi(self):6print("chi")78defhe(self):9print("he")101112classCat(Animals):13def__init__(self,name):14self.Name=name1516defjiao(self):17print("%s叫"%(self.Name))1819classDog(Animals):20def__init__(self,name):21self.Name=name2223defjiao(self):24print("%s叫"%(self.Name))252627mao1=Cat("小花")28mao1.jiao()派生类中有的功能基类中也有
12派生类中有的功能基类中也有:优先执行派生类中的方法。即从上到下23classAnimals:45defchi(self):6print("chi")78defhe(self):9print("he")1011defpiao(self):12print("%s票小泽玛利亚"%self.Name)1314classCat(Animals):15def__init__(self,name):16self.Name=name1718defjiao(self):19print("%s叫"%(self.Name))2021defpiao(self):22print("%s票苍井空"%self.Name)2324classDog(Animals):25def__init__(self,name):26self.Name=name2728defjiao(self):29print("%s叫"%(self.Name))303132mao1=Cat("小花")33mao1.piao()2.2多继承的情况1
多继承,1从左到右去匹配,匹配到就不向右匹配了,2派生类中有的就不用了
1多继承,1从左到右去匹配,匹配到就不向右匹配了,2派生类中有的就不用了23classAnimals:45defchi(self):6print("chi")78defhe(self):9print("he")1011defpiao(self):12print("%s票小泽玛利亚"%self.Name)1314classDog_F():15def__init__(self,name):16self.Name=name1718defjiao(self):19print("%s叫"%(self.Name))2021defpiao(self):22print("%s票苍井空"%self.Name)2324classDog(Animals,Dog_F):25def__init__(self,name):26self.Name=name2728defjiao(self):29print("%s叫"%(self.Name))303132dog1=Dog("小强")33dog1.piao()
2.3多继承的混乱点python3版本的两种情况
三、多态
Pyhon不支持多态并且也用不到多态,多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型”。
如果某个类继承了接口,这个接口有几个方法就要实现几个方法,都需要实现
1代码级别接口:2仅对于接口来说接口用来约束的几个方法就得写几个方法3方法不能写任何功能,pass45如果某个类继承了接口,这个接口有几个方法就要实现几个方法,都需要实现67默认是interfaceIFather:8910interfaceIFather:11deff1(self):pass12deff2(self):pass1314classSon1(IFather):15deff1(self):16print("f1")17deff2(self):18print("f2")19classSon2(IFather):#如果使用了接口类为基类,必须完成基类的所有方法20deff1(self):21print("f1")22deff2(self):23print("f2")24classSon3:25deff1(self):26print("f1")27对于多态:28deffun(interfacearg):#只能传完成了f2接口的类29arg().f2()总结以上就是本节对于面向对象初级知识的介绍,总结如下:
问答专区
问题一:什么样的代码才是面向对象?
答:从简单来说,如果程序中的所有功能都是用类和对象来实现,那么就是面向对象编程了。
问题二:函数式编程和面向对象如何选择?分别在什么情况下使用?
答:须知:对于C#和Java程序员来说不存在这个问题,因为该两门语言只支持面向对象编程(不支持函数式编程)。而对于Python和PHP等语言却同时支持两种编程方式,且函数式编程能完成的操作,面向对象都可以实现;而面向对象的能完成的操作,函数式编程不行(函数式编程无法实现面向对象的封装功能)。
所以,一般在Python开发中,全部使用面向对象或面向对象和函数式混合使用
1classSqlHelper:23def__init__(self,host,user,pwd):45self.host=host6self.user=user7self.pwd=pwd89def增(self):10#使用主机名、用户名、密码(self.host、self.user、self.pwd)打开数据库连接11#dosomething12#关闭数据库连接1314def删(self):15#使用主机名、用户名、密码(self.host、self.user、self.pwd)打开数据库连接16#dosomething17#关闭数据库连接1819def改(self):20#使用主机名、用户名、密码(self.host、self.user、self.pwd)打开数据库连接21#dosomething22#关闭数据库连接2324def查(self):25#使用主机名、用户名、密码(self.host、self.user、self.pwd)打开数据库连接26#dosomething27#关闭数据库连接#dosomething2需要创建多个事物,每个事物属性个数相同,但是值的需求如:张三、李四、杨五,他们都有姓名、年龄、血型,但其都是不相同。即:属性个数相同,但值不相同
1classPerson:23def__init__(self,name,age,blood_type):45self.name=name6self.age=age7self.blood_type=blood_type8910defdetail(self):11temp="iam%s,age%s,bloodtype%s"%(self.name,self.age,self.blood_type)12printtemp1314zhangsan=Person('张三',18,'A')15lisi=Person('李四',73,'AB')16yangwu=Person('杨五',84,'A')1718Demo问题三:类和对象在内存中是如何保存?
答:类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图:
如上图所示,根据类创建对象时,对象中除了封装name和age的值之外,还会保存一个类对象指针,该值指向当前对象的类。