第一种:面向过程1.过程:没有返回值的函数2.面向过程:找到解决问题的入口,按照一个固定的流程去模拟解决问题的流程3.面向过程举例:第一步:搜索目标,按照要求到数据结构(字典)内检索合适的人物第二步:表白,表白成功进入第三步,否则进入第二步。第三步:恋爱,恋爱成功进入第四步,否则退回到第一步。第四步:家长同意进入第五步,家长说她是你失散多年的妹妹,返回第一步第五步:结婚4.面向对象总结:把一个大的问题分解成N个小的问题,每次解决问题从头开始一步一步执行这个流程,中间加上逻辑的判断,基本单位就是函数第二种:函数式编程1.函数式编程:函数式编程=编程语言定义的函数+数学意义的函数函数式就是用编程语法去实现数学函数。这种函数内对象是永恒不变的,要么参数是函数,要么返回值是函数,没有for和while循环,所有的循环都由递归去实现,无变量的赋值(即不用变量去保存状态),无赋值即不改变。(1)数学模型(数学模型代表一个数学逻辑):
y=2*x+1(2)用python当中的def去实现上面的数学逻辑:
defcal(x):return2*x+12.函数式编程的特征:(1)不可变数据:不用变量保存状态,不修改变量#非函数(面向过程)
a=1defincr_test1():globalaa+=1returnaincr_test1()print(a)#打印结果:2#函数式编程
n=1defincr_test(n):returnn+1print(incr_test(1))#返回结果:2(2)第一类对象:函数即“变量”<1>函数名可以当作参数传递
deffoo(n):print(n)defbar(name):print('mynameis%s'%name)foo(bar)输出:
deffoo(n):print(n)defbar(name):print('mynameis%s'%name)foo(bar('xixi'))输出:mynameisxixiNone详解:foo(bar('xixi'))传值xixi给%name打印mynameisxixi,下面没有返回值默认返回一个None,相当于把None放到foo()这个位置传给foo了
defbar():print('frombar')deffoo():print('fromfoo')returnbarn=foo()n()输出:fromfoofrombar详解:n=foo()运行foo()函数return返回值bar赋值给n(),n()相当于执行bar()函数
defhanle():print('fromhandle')returnhanleh=hanle()h()输出:fromhandlefromhandl详解:运行h=hanle()直接打印fromhandle返回值返回自己h(hanle)<3>尾调用优化(尾递归):在函数的最后一步调用另外一个函数(最后一行不一定是函数的最后一步)#非尾递归
defcal(seq):iflen(seq)==1:returnseq[0]head,*tail=seqreturnhead+cal(tail)print(cal(range(100)))输出:4950详解:head+cal(tail)把cal(tail)拿到结果做一个+head在返回需要保存状态#尾递归
defcal(l):print(l)iflen(l)==1:returnl[0]first,second,*args=ll[0]=first+secondl.pop(1)returncal(l)x=cal([iforiinrange(100)])print(x)输出:[0,1,2,3,4,5,6,7,8,9][1,2,3,4,5,6,7,8,9][3,3,4,5,6,7,8,9][6,4,5,6,7,8,9][10,5,6,7,8,9][15,6,7,8,9][21,7,8,9][28,8,9][36,9][45]45高阶函数1.函数:firstclassobject(一等对象)(1)可以被赋值给一个变量(2)可以嵌入到数据结构中(3)可以作为参数传递给函数(4)可以作为值被函数返回2.高阶函数(1)数学概念y=g(f(x))(2)在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数条件一:接受一个或多个函数作为参数(自变量可以多)函数接收的参数是一个函数名
importtimedeffoo():#定义foo函数:第五步:执行foo()time.sleep(3)print('西西')deftest(func):#定义tes函数---第二步:test是高阶函数,形参就收的值是一个函数名funcprint(func)#第三步:打印func,这个func是foo的内存地址func()#第四步:有了这个内存直接运行func()这个函数就可以执行foo()函数test(foo)#第一步:把foo这个函数名传给了test函数的形参#输出:
deffoo():#定义foo函数:print('西西')deftest(func):#定义test函数:returnfunc#第三步:这个return出个了func,这个fun就是foo,foo传进去直接返回值就是foo这个函数名res=test(foo)#第二步:把foo这个函数名传给了test(func)函数的形参,同时这个foo传进去这个函数返回值就是函数名res=test的运行结果由return控制print(res)#第四步:print(res)输出了foo的内存地址res()#第一步:执行res()函数#输出:
deffoo():#定义foo函数print('西西')deftest(func):#定义test函数returnfunc#foo传进去直接返回值就是foo这个函数名foo=test(foo)#把foo这个函数名传给了test函数的形参,test的运行结果由return控制,赋值给foofoo()#输出:西西详解:原来执行函数1就是foo(),现在执行还是foo()3.高阶函数实现计数器
l=[3,2,1,5,7]#排序本质就是比较大小print(sorted(l))#返回:[1,2,3,5,7]功能2:字典排序
people=[{'name':'xixi','age':1000},{'name':'wang','age':1111},{'name':'shi','age':111},{'name':'yao','age':11},]print(sorted(people,key=lambdadic:dic['age']))#for循环people把取出来的值都给lambda表达式,值都是小字典#返回:[{'age':11,'name':'yao'},{'age':111,'name':'shi'},{'age':1000,'name':'xixi'},{'age':1111,'name':'wang'}]2.filter过滤函数:(1)filter(function,iterable)(2)过滤可迭代对象的元素,返回一个迭代器(3)function一个具有一个参数的函数,返回布尔值举例1:过滤掉名字不是_yi结尾的匿名函数lambda方式写法
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']deffilter_test(func,array):ret=[]forpinarray:ifnotfunc(p):ret.append(p)returnretres=filter_test(lambdan:n.endswith('yi'),movie_people)print(res)#输出:['shihsi']内置函数filter写法:第一步:
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']print(filter(lambdan:notn.endswith('yi'),movie_people))#输出:
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']res=filter(lambdan:notn.endswith('yi'),movie_people)print(list(res))#输出:['shihsi']filter函数功能:前面一个函数lambdan:notn.endswith('yi')是得一个布尔值,根据后面for循环movie_people可迭代对象,如果得到的是True保留,所以需要加一个not第三步:
movie_people=['xixi_yi','wangwang_yi','shihsi','yaoyao_yi']print(list(filter(lambdan:notn.endswith('yi'),movie_people)))#输出:['shihsi']filter的用法详解:俩个参数:lambdan:notn.endswith('yi')和movie_people第一个参数:lambdan:notn.endswith('yi')是一个函数,第二个参数movie_people是一个可迭代对象,它会把可迭代对象以此进行for循环遍历,遍历完了拿出每一个元素交给前面的函数进行处理,函数处理的结果是一个布尔值,布尔值等于True保留下来举例2:过滤到age小于18的
people=[{'name':'xixi','age':1000},{'name':'shishi','age':10000},{'name':'yaoyao','age':9000},{'name':'shishi','age':18},]print(list(filter(lambdap:p['age']<=18,people)))#输出:[{'name':'shishi','age':18}]3.map映射函数:(1)处理序列中的每个元素,返回一个迭代器,该迭代器里元素个数及位置与原来一样(2)语法:map(func,*iterables)举例:完成列表[1,2,10,5,3,7]加1,减1,平方的操作匿名函数lambda写法:
num_l=[1,2,10,5,3,7]defmap_test(func,array):#func=lambdax:x+1arrary=[1,2,10,5,3,7]ret=[]foriinnum_l:res=func(i)#add_one(i)ret.append(res)returnretprint(map_test(lambdax:x+1,num_l))#加1print(map_test(lambdax:x-1,num_l))#减1print(map_test(lambdax:x**2,num_l))#平方#输出:[2,3,11,6,4,8][0,1,9,4,2,6][1,4,100,25,9,49]内置函数map的用法:(1)内置函数map方式写法1:(加1)
num_l=[1,2,10,5,3,7]res=map(lambdax:x+1,num_l)#传的是内置函数print(res)print(list(res))#返回:
num_l=[1,2,10,5,3,7]defreduce_one(x):returnx-1print(list(map(reduce_one,num_l)))#传的是有名函数#输出:[0,1,9,4,2,6]分析:最后map得到的也是一个结果也是可迭代对象,用list转化成一个列表,map处理的结果就是一个列表第二个参数不一定是列表,只要是可迭代对象就可以(3)函数map方式:(转大写)
classClassName:'类的文档字符串'类里面的函数和方法注意:①必须使用class关键字②类名必须使用大驼峰命名③类定义完成后,就产生了一个类对象,绑定到了ClassName上举例:
class类名:pass(2)新式类:新式类和经典类实名的最大不同在于,所有新式类必须继承至少一个父类
class类名(父类):pass3.类的属性:类是用来描述一类事物,类的对象指的是这一类事物中的一个个体,是物就要有属性,属性分为:(1)数据属性:就是变量(特征)(2)函数属性:就是函数,在面向对象里通常成为方法注意:类和对象均用点来访问自己的属性举例:
classMyClass:"""示例类"""x=123#类属性或类变量deffoo(self):#类的函数属性foo,也是方法return'MyClass'#dir加上类名查看类的属性列(表方式显示属性名字)print(dir(MyClass))#返回结果:['__class__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__getattribute__','__gt__','__hash__','__init__','__le__','__lt__','__module__','__ne__','__new__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__','__weakref__','foo','x']#通过.__dict__查看类属性字典print(MyClass.__dict__)#返回结果:{'__weakref__':
classMyClass:"""示例实例"""x=123#类属性或类变量deffoo(self):#类的属性foo,也是方法,这个方法本身是函数对象returnself#由一个抽象概念虚拟出一个实例出来赋给标识符a,相当于把a作为参数扔到的self,对这个实例做初始化的工作a=MyClass()#a变量就对应内存中的一个地址,这个地址放了实例化后的对象,打印对象的内存地址print(a)#打印结果:<__main__.MyClassobjectat0x00000000006DADD8>#调用foo方法:相当于把a作为参数扔到self,self指代的就是a实例,打印a实例的内存地址print(a.foo())#打印结果:<__main__.MyClassobjectat0x00000000007CADD8>#实例拿类属性print(a.x)#打印结果:123总结:①使用上面的语法,在类的对象名称后面加上一个括号,就调用类的实例化方法,完成实例化。实例化就是真正创建一个该类的对象(实例)。②实例化后获得的实例,是不同的实例,即使使用同样的参数实例化,也得到不一样的对象。③MyClass()实际上调用的是__init__(self)方法,可以不定义,如果没有定义会在实例化后隐式调用3.__init__方法:对实例进行初始化Python类实例化后,会自动调用__init__方法。这个方法第一个参数必须留给self,其它参数随意。(1)无参数:
classMyClass:"""__init__始化实例"""def__init__(self):#初始化print('init')#MyClass()实例化构造出自己的实例,到__init__里初始化后赋值给aa=MyClass()#返回:init<2>有参数:一般初始化函数可以多个参数给实例添加属性(实例的属性),请注意第一个位置必须是self,例如init(self,name,age)
classPerson:"""self"""def__init__(self):print('selfininit={}'.format(id(self)))#获取self对象的地址#Person()实例化构造出自己的实例,到__init__里初始化后赋值给aa=Person()#打印:selfininit=11708008#打印a实例的地址print('c={}'.format(id(a)))#打印:c=11708008总结:上例子说明,self就是调用者,就是a对应的实例对象4.实例变量和类变量:实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法
classChinese:country='China'def__init__(self,name):self.name=namedefplay_ball(self,ball):print('%s正在打%s'%(self.name,ball))#创建实例p1p1=Chinese('xixi')#查看实例属性字典print(p1.__dict__)#返回:{'name':'xixi'}#通过实例p1查看name属性print(p1.name)#实例数据属性返回:xixi#通过实例p1访问函数属性(是类的属性)返回类的绑定的方法print(p1.play_ball)#返回结果:
classChinese:country='China'def__init__(self,name):self.name=namedefplay_ball(self,ball):print('%s正在打%s'%(self.name,ball))#实例化p1=Chinese('xixi')print(p1.country)#返回结果:China#在p1字典里新增了一个韩国人,跟类的字典没有关系p1.country='韩国人'print('类的--->',Chinese.country)#返回结果:类的--->China#在p1自己的字典里面加东西了,所以实例的修改了print('实例的-->',p1.country)#返回结果:实例的-->韩国人(2)不是实例属性就是普通变量
country='普通变量'classChinese:country='类属性'def__init__(self,name):self.name=name#没有加点既不是类的属性,也不是实例属性就是普通变量print(country)defplay_ball(self,ball):print('%s正在打%s'%(self.name,ball))#实例化触发def__init__(self,name)函数运行p1=Chinese('xixi')#返回结果:中国#p1实例调用Chinese类的country属性print(p1.country)#返回结果:类属性(3)类内部和类外部的应用
country='最外层'classChinese:country='中国'def__init__(self,name):self.name=nameprint(country)#没有加点既不是类的属性,也不是实例属性就是普通变量,如果找不到会到上层节点找defplay_ball(self,ball):print('%s正在打%s'%(self.name,ball))#实例化触发def__init__(self,name)函数运行返回触发到p1=Chinese('xixi')#返回结果:最外层#调用Chinese类加点调用实例countryprint(Chinese.country)#返回结果:中国(4)改类里的新增属性
classChinese:country='China'#列表放入俩个值l=['a','b']def__init__(self,name):self.name=namedefplay_ball(self,ball):print('%s正在打%s'%(self.name,ball))#实例化p1=Chinese('xixi')#p1实例调l属性自己位置没有找类print(p1.l)#返回结果:['a','b']#p1实例修改类属性l不是新增属性p1.l.append('c')#调用类属性lprint(Chinese.l)#返回结果:['a','b','c']五.通过函数装饰一个类1.为一个类通过装饰器,修改属性
def____--_name(name):#name接收参数XIXIdefwrapper(cls):#cls接收类Personcls.NAME=name#第二步:动态的给Person加了一个类属性,类属性赋了个值NAME=XIXIreturncls#返回的还是Person类returnwrapper@add_name('XIXI')#第一步:动态传参把XIXI传到add_name函数里的name。见到装饰器把Person扔到wrapper函数里的clsclassPerson:NAME='XI'#把类本身的属性XI替换成XIXIprint(Person.__dict__)#打印结果:{'__dict__':
classMyClass:deffoo(self):#foo这个静态属性于类,可以给对象用,对象一旦用了,相当于把对象作为第一个位置参数传到self里print("foo")defbar():#bar这个函数是普通函数,放在MyClass类内,没有位置参数就不能用实例调用print('bar')#查看类对象Person的属性的字典print(MyClass.__dict__)#返回结果:{'__weakref__':
classRoom:#定义房间的类def__init__(self,name,owner,width,length,heigh):#房间的属性(初始化函数)self.owner=owner#房间的主人self.name=name#房间的名字self.width=width#房间的宽度self.length=length#房间的长度self.heigh=heigh#房间的高度#求面积r1=Room('主卧','宝宝',10,10,10)#把参数传到房间属性里print('%s住在%s总面积是%s'%(r1.owner,r1.name,r1.width*r1.length))(2)封装方法方式把公共部分放到类里实现求面积
classRoom:#定义房间的类def__init__(self,name,owner,width,length,heigh):#房间的属性(初始化函数)self.owner=owner#房间的主人self.name=name#房间的名字self.width=width#房间的宽度self.length=length#房间的长度self.heigh=heigh#房间的高度defcal_area(self):#公共的计算面积函数(函数属性)print('%s住在%s总面积是%s'%(self.owner,self.name,self.width*self.length))#求面积#实例化1把参数传到房间属性里r1=Room('主卧','宝宝',10,10,10)#实例化2把参数传到房间属性里r2=Room('次卧','西西',5,5,10)#r1实例调用cal_area()方法实现r1.cal_area()#返回结果:宝宝住在主卧总面积是100#r2实例调用cal_area()方法实现r2.cal_area()#返回结果:西西住在次卧总面积是252.类方法:①在类定义中,使用@classmethod装饰器修饰的方法②必须至少有一个参数,切第一个参数留给了cls,cls指代调用者即类对象自身③cls这个标识符可以是任意合法名称,但是为了易读,请不要修改④通过cls可以直接操作类的属性。无法通过cls操作类的实例
classMyClass:tag=1@classmethod#@classmethod专门供类使用的类方法defclsmtd(cls):#cls这个参数不再是实例本身,接收的是类MyClass本身,这个cls参数必须有#通过__name__获取到类名。cls就是MyClass类调用类的的类变量tag=1print('类名字={}tag={}'.format(cls.__name__,cls.tag))#查看类对象Person的属性的字典print(MyClass.__dict__)#打印结果:{'clsmtd':
classRoom:#定义房间的类tag=1#定义一个数据属性def__init__(self,name,owner,width,length,heigh):#房间的属性self.owner=owner#房间的主人self.name=name#房间的名字self.width=width#房间的宽度self.length=length#房间的长度self.heigh=heigh#房间的高度@classmethod#@classmethod专门供类使用的类方法deftell_info(cls,x):#cls这个参数接收的是一个类名Room,x是自己加的参数#打印的是Room类print(cls)#返回结果:
classMyClass:tag=1@staticmethod#静态方法defstaticmtd():#定义staticmtd属性(没有self调用不了类属性和实例属性)print('static')#查看类对象Person的属性的字典没有staticmtd方法print(MyClass.__dict__)#返回结果:{'tag':1,'__module__':'__main__','staticmtd':
#定义类HandclassHand:pass#定义类FootclassFoot:pass#定义类TrunkclassTrunk:passclassPerson:#定义一个大类Person(里面由一个个小类组合而成的)def__init__(self,id_num,name):self.id_num=id_num#定义自己身份证属性self.name=name#定义自己名字属性self.hand=Hand()#直接调用Hand类self.foot=Foot()#直接调用Foot类self.trunk=Trunk()#直接调用Trunk类#创建实例p1用Person类实例化传id_num和namep1=Person('123456','xixi')#查看p1实例属性字典包含小类print(p1.__dict__)#返回结果:{'hand':<__main__.Handobjectat0x0000000000DD2B00>,'trunk':<__main__.Trunkobjectat0x0000000000DD2B70>,'name':'xixi','foot':<__main__.Footobjectat0x0000000000DD2B38>,'id_num':'123456'}<2>定义一个组合一个大的类由一些小的类组合而成:
classPerson:def__init__(self,name,age=18):self.name=nameself.__age=age#私有defgrowup(self,incr=1):self.__age+=incr#加到p1=Person('xixi')#print(p1.__age)#访问不到了(2)不过知道了这个名字,还是可以访问也可以直接修改
classPerson:def__init__(self,name,age=18):self.name=nameself.__age=age#私有defgrowup(self,incr=1):self.__age+=incr#加到p1=Person('xixi')#print(p1.__age)#访问不到了#打印对象字典print(p1.__dict__)#打印结果:{'name':'xixi','_Person__age':18}#知道名字后可以访问到print(p1._Person__age)#打印结果:18#也可以直接修改私有变量p1._Person__age=50#修改后访问print(p1._Person__age)#打印结果:50总结:从上例子可以看出,知道了私有变量的新名称,就可以直接从外部访问到,并可以修改它2.保护变量:在变量名前使用一个下划线,称为保护变量
classPerson:def__init__(self,name,age=18):self.name=nameself._age=age#私有defgetage(self):returnself._agep1=Person('xixi')p1._age='28'print(p1._age)#返回:28总结:可以看出,这个_age属性根本没有改变名称,和普通的属性一样,解释器不做任何特殊处理。这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用3.双下划线开头的名字
classPerson:def__init__(self,chinese,english,history):self.chinese=chineseself.eng=englishself.his=historydefgetscore(self):return(self.chinese,self.eng,self.his)<2>test3.py补丁
defgetscore(self):returndict(chi=self.chinese,eng=self.eng,his=self.his)<3>test1.py执行文件通过执行补丁函数调用补丁
fromtest2importPersonfromtest3importgetscore#补丁函数defmonkeypatch4Person():#test2里Person类里的getscore函数被等号后面的test3里的getscore函数替换掉Person.getscore=getscore#实例化student1student1=Person(80,85,90)#调用test2里Person类下的getscoreprint(student1.getscore())#打印结果:(80,85,90)#调用补丁函数monkeypatch4Person()#当它调用getscore的时候就变成调用test3里的getscoreprint(student1.getscore())#打印结果:{'his':90,'chi':80,'eng':85}九.属性装饰器一般好的设计是:把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性1.property装饰器:后面跟的函数名就是以后的属性名。它就是getter。这个必须有,有了它至少是只读属性2.setter装饰器:与属性名同名,且接收2个参数,第一个是self,第二个是将要赋值的值。有了它,属性可写3.deleter装饰器:可以控制是否删除属性。很少用
classFoo:def__init__(self,name):self.name=namedef__del__(self):print('我执行啦')f1=Foo('xixi')#由Foo产生个实例传namedelf1#删除实例会触发__del__#delf1.name#删除实例的属性不会触发__del__print('--------------------->')#程序运行完毕会自动回收内存,触发__del__####打印结果:我执行啦--------------------->十一.面向对象的三要素(封装-继承-多态)
1.封装-继承-多态结合人类就是封装;人类继承自动物类,孩子继承父母的特征。分为单一继承,多继承;多态,继承自动物类的人类,猫类的操作“吃”不同。封装(Encapsulation):面向对象三要素之一一.基本概念:1.封装:抛开面向对象,单去想什么是装,就是拿一个麻袋,把小猫小狗小兔子一起装进麻袋,封就是把麻袋封上口子。在面向对象中这个麻袋就是你的类或者对象,类或者对象这俩麻袋内部装了数据属性和函数属性,那么对于类和对象来说,封的概念从何而来,其实封的概念代表隐藏2.组装:将数据和操作组装到一起。3.将数据隐藏起来,给使用者提供操作。使用者通过操作就可以获取或者修改数据getter和setter。通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来。保护成员或私有成员。4.一个对象,要从它的特征上抽象它。抽象完之后,在计算机中,就要把它转成class,而class里面真正封装的东西是什么呢?你关心的数据和操作。你为什么关心它因为业务需要5.封装现实举例:比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行了,踩了油门就能跑,可以不了解后面的机动原理。二.封装的实现1.第一个层面的封装:定义一个封装类
#定义一个类People人classPeople:#双下划线开头隐藏数据属性earth地球__star='earth'#定义初始化函数def__init__(self,id,name,age,salary):#把数据属性封装到类的内部变成私有的了print('私有',self.__star)self.id=idself.name=nameself.age=ageself.salary=salary#定义一个可以获取id号和名字defget_id(self):print('我是私有方法,我的id是[%s],我的名字是[%s]'%(self.id,self.name))#访问函数(接口函数)defget_star(self):#外部要调用的时候无法直接使用,需要在内部类上面挖了一个坑print(self.__star)#这个坑帮忙实现了一个访问的操作#外部在使用的时候p1=People('18','xixi','18',999)#返回结果:私有earth##p1调用外部方法get_star曲线找到隐藏的属性p1.get_star()#返回结果:earth(2)接口函数
#类的实现着把内部隐藏起来classRoom:#定义一个Room类完成封装def__init__(self,name,owner,width,length,high):self.name=nameself.owner=ownerself.__width=widthself.__length=lengthself.__high=high#定义了一个接口函数deftell_area(self):#求的是体积returnself.__width*self.__length*self.__high#求体积r1=Room('卫生间','xixi',100,100,100)#类的使用者想访问长宽高报错#arear=r1.__width*r1.__length*r1__high#外面作用调用者可以直接调用接口函数完成求面积print(r1.tell_area())#返回结果:1000000继承(Inheritance):面向对象三要素之一一.基本概念(1)人类和猫类都继承动物类。(2)个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性(3)在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法。这样可以减少代码,多复用。子类可以定义自己的属性和方法。1.不用继承的例子
classAnimal:defshout(self):print('叫')#打印:叫a=Animal()a.shout()classCat:defshout(self):print('喵喵叫')#打印:喵喵叫c=Cat()c.shout()总结:上面的2个类虽然有关系,但是定义时并没有建立这种关系,而是各自完成定义。动物类和猫类都有叫,但是它们的叫有区别,所以分别定义。2.继承的例子:
classAnimal:def__init__(self,name):self._name=namedefshout(self):#一个通用的叫方法,可以被子类继承print('叫')#打印:叫#父类里定义了一个属性(方法),也可以被继承@propertydefname(self):returnself._name#子类1classCat(Animal):passcat=Cat('暹罗猫')#继承父类方法cat.shout()#打印:叫#打印猫名字print(cat.name)#打印:暹罗猫#子类2classDog(Animal):passdog=Dog('沙皮狗')#继承父类方法dog.shout()#打印:叫#打印狗名字print(dog.name)#打印:沙皮狗总结:上例子可以看出,通过继承,猫类,狗类不用写代码,继承可以让子类从父类获取特征(属性和方法)3.继承:classCat(Animal)类的继承跟现实生活中的父亲儿子孙子重孙子继承关系一样父亲类又称为基类,括号中写上继承的类的列表。4.父类:Animal是Cat的父类,也称基类,超类5.子类:Cat就是Animal的子类,也称派生类6.什么时候用继承(1)当类之间有显著的不通,并且较小的类是较大的类所需要的组件时,用组合比较好(2)当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好二.继承1.格式如下:
class子类名(基类1[,基类2,...])语句块如果类定义时,没有基类列表,等同于继承自object。在python3中,object类是所有对象的根基类。2.单继承(1)子类单继承父类(子类继承了父类的所有属性)
classFather:'这个是爸爸类'money='100万'#给类定义了一个数据属性:money='100万'def__init__(self,name):#初始化函数定义一个属性为nameprint('爸爸')self.name=nameclassSon(Father):#定义Son单继承father类money='1万'#子类定义的属性跟父类重名,优先调用自己的属性print(Son.money)#返回结果:1万#父类调用自己的数据属性print(Father.money)#返回结果:100万3.继承和派生(1)继承描述了子类属性从祖先类继承这一种方式(2)派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义(3)继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系
classA:#定义一个基类Adeftest(self):print('A')classB(A):#B类继承A类passclassC(A):#C类继承A类passclassD(B):#D类继承B类passclassE(C):#E类继承C类passclassF(D,E):#F类继承D类和E类passf1=F()#经典类:F类当中没有会找D类->D类当中没有会找B类->B类当中没有会找基类A类(如果A类当中没有在返回来找E类再找C类,C类当中没有会报错了)f1.test()#返回结果:A(4)广度优先:A类继承了B类,B类又继承了C类和D类,如果在A类中去找一个属性,A类的查找顺序是先找B类,如果都没有的话再找C类,C类没有的话再找D类(当父类继承了object类就是新式类,当类是新式类时,多继承情况下,会按照广度优先方式查找)举例:python3新式类广度优先方法:定义一个基类A,B类继承A类,C类继承A类,D类继承B类,E类继承C类,F类继承D类和E类
classA:#定义一个基类Adeftest(self):print('A')classB(A):#B类继承A类passclassC(A):#C类继承A类deftest(self):print('C')classD(B):#D类继承B类passclassE(C):#E类继承C类passclassF(D,E):#F类继承D类和E类passf1=F()#新式类:F类当中没有会找D类->D类当中没有会找B类->B类当中没有会找E类->E类当中没有会找C类返回C(如果C类当中没有才会找最后的基类A,最后基类A类在没有会报错)f1.test()#返回结果:C#查看MRO列表print(F.__mro__)#返回结果:(
classdongdong:defserver(self):#第六步:dongdong类里有server方法self.shishi()#第七步:回到obj=beibei(),因为self(obj=shishi)是beibei的对象defshishi(self):self.yaoyao()defprocess_request(self):print('xixi')classnannan(dongdong):#第五步:xixi里没有server在到beibei的另一个父类nannan里找没有server,在到nannan父类dongdong里找#第十步:nanan里找defshishi(self):#第十一步:找到找beibei对象shishi(self)print('shishi')#第十二步:打印:shishidefyaoyao(self):self.process_request()classxixi:#第四步:到xixi这里找server没有#第九步:xixi里找没有self(obj=shishi)defprocess_request(self):print('xixi')classbeibei(xixi,nannan):#第三步:在beibei这里找server没有到他的父类xixi里找passobj=beibei()#第一步:实例化beibei赋值obj#第八步:找beibei对象self(obj=shishi)obj.server()#第二步:通过obj实例化server举例2:
文档Document类是其他所有文档类的抽象基类;Word,Pdf类是Document的子类。实现类的功能需求:为Document子类提供打印能力方式一:面向对象最经典的,子类继承父类的方法+覆盖(1)基类提供的方法不应该具体实现,因为它未必适合子类的打印,子类中需要覆盖重写。
classDocment:def__init__(self,content):#self.content=content#内容#打印方法:defprint(self):print(self.content)#Docment的子类WordclassWord(Docment):pass#假定第三方库,不允许修改#Word的子类PrintableWord实现打印功能classPrintableWord(Word):defprint(self):print('Wordprint{}'.format(self.content))#Docment的子PdfclassPdf(Docment):pass#先看PrintableWord,没有看Word,没有看Docment,在没有看object,如果都没有抛异常print(PrintableWord.mro())#打印结果:[
classC:def__init__(self):self.name='xixi'②xi目录下test2.py
#调用lib目录下有一个test1模块,test1模块里有一个C类fromxi.test1importC#实例化c1=C()#可以调用到print(c1.name)#返回结果:xixi#查看c1来自那个模块print(c1.__module__)#返回结果:xi.test13.__class__:对象或类所属的类①xi目录下test1.py
#调用lib目录下有一个test1模块,test1模块里有一个C类fromxi.test1importC#实例化c1=C()#可以调用到print(c1.name)#返回结果:xixi#查看c1来自那个类产生的print(c1.__class__)#返回结果:
classFoo:passclassBar(Foo):pass#该属性无法继承给子类print(Bar.__doc__)#返回结果:None#该属性无法继承给子类print(Bar.__doc__)#返回结果:None6.__mro__:类的mro,class.mro()返回的结果的保存在__mro__中7.__dict__:类或实例的属性,可写的字典二.魔术方法分类1.hash:内建函数hash()调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash
classA:X=123def__init__(self):self.y=5passdef__hash__(self):return1#返回结果必须是整型#hash调用实例A里面的__hash__方法print(hash(A()))#打印结果:1#打印两个A实例化的keyprint({A(),A()})#返回结果:{<__main__.Aobjectat0x00000000006DA828>,<__main__.Aobjectat0x0000000000675D30>}#通过set把两个A实例化key去重s=set({A(),A()})print(s)#返回结果并没有去重:{<__main__.Aobjectat0x00000000006DA828>,<__main__.Aobjectat0x0000000000675D30>}(1).上例子中set为什么不能去重相同的key①__eq__:对应==操作符,判断2个对象是否相等,返回bool值②__hash__方法只能返回一个hash值作为set的key,但是去重,还需要__eq__来判断2个对象是否相等。③hash值相等,只是hash冲突,不能说明两个对象是相等的。因此一般来说提供__hash__方法是为了作为set或者dict的key的,所以去重重要同时提供__eq__方法。
classA:X=123def__init__(self):self.y=5passdef__hash__(self):return1#判断2个对象是否相等def__eq__(self,other):returnself.y==other.y#hash调用实例A里面的__hash__方法print(hash(A()))#打印结果:1#打印两个A实例化的结果#print({A(),A()})#返回结果:{<__main__.Aobjectat0x00000000006DA828>,<__main__.Aobjectat0x0000000000675D30>}#通过set把两个A实例化去重s=set({A(),A()})print(s)#返回结果并没有去重:{<__main__.Aobjectat0x00000000006DA828>,<__main__.Aobjectat0x0000000000675D30>}(2)可hash对象必须提供__hash__方法,没有提供的话,isinstance(p1,collections.Hashable)一定为False。1)isinstance(obj,cls)检查一个对象obj是否是一个类cls的实例2)issubclass(sub,super)检查sub类是否是super类的派生类(子类)
fromcollectionsimportHashableclassA:X=123def__init__(self):self.y=5passdef__hash__(self):return1print(isinstance(A,Hashable))#打印:Trueprint(issubclass(A,Hashable))#打印:True2.内建函数bool():或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义__bool__(),就找__len__()返回长度,非0位真。如果__len__()也没有定义,那么所有实例都返回真~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~3.可视化(__str__,__repr__,__bytes__)(1)__str__:str()函数,内建函数format,print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用__repr__方法返回字符串表达式,如果__repr__也没有定义,就直接返回对象的内存地址信息
classFoo:#初始化def__init__(self,name,age):self.name=nameself.age=agedef__str__(self):#__str__调用的是f1.__str__()方法,必须要return个值#可以自己控制str的打印的信息return'名字是%s年龄是%s'%(self.name,self.age)#实例化传俩个参数f1=Foo('xixi',18)#方式一:实际触发的是系统提供的str(f1)方法的运行,str(f1)执行的是f1.__str__()的方法print(f1)#返回结果:名字是xixi年龄是18#方式二:print(str(f1))#返回结果:名字是xixi年龄是18#方式三:print(f1.__str__())#返回结果:名字是xixi年龄是18(2)__repr__:内建函数repr()对一个对象获取字符串表达。如果一个类定义了__repr__()但没有定义__str__,那么在请求该类的实例的“非正式”的字符串表示时也将调用__repr__()。如果__repr__也没有定义,就直接返回object的定义就是显示内存地址信息。
classFoo:def__init__(self,name,age):#init方法传name,ageself.name=nameself.age=age#def__str__(self):#return'这是str'def__repr__(self):#在解释器里触发return'名字是%s年龄是%s'%(self.name,self.age)#实例化传俩个参数f1=Foo('xixi',18)#如果有str先找str(f1)下的f1.__str__(),如果找不到str直接找f1.__repr__()作为一个替代品print(f1)#返回结果:名字是xixi年龄是18(3)__bytes__:bytes的时候,返回一个对象的bytes表达,即返回bytes对象。
classA:#类属性X=123#使用slots添加p1名称和p2名称,如果不加p3的话,p3属性是用不了__slots__=('p1','p2')def__init__(self):#实例属性1self.p1=1#实例属性2self.p2=2#实例属性3#self.p3=3#报错结果:AttributeError:'A'objecthasnoattribute'p3'defshowme(self):print(self.p1,self.p2)#A类字典print(A.__dict__)#打印结果:{'__slots__':('p1','p2'),.....}#定义了slots后A实例字典就消失了#print(A().__dict__)#报错结果:AttributeError:'A'objecthasnoattribute'__dict__'#只有slots自己了p1和p2print(A().__slots__)#打印结果:('p1','p2')#实例化出一个aa=A()#因为没有在slots定义过,所以a实例不能动态改类属性#a.X=300#报错结果:AttributeError:'A'objectattribute'X'isread-only总结:①__slots__告诉解释器,实例的属性都叫什么,一般来说,既要节约内存,最好还是使用元祖比较好②一旦提供了__slots__,就阻止实例产生__dict__来保护实例属性③__slots__不影响子类实例,不会继承下去,除非子类里面自己定义了__slots__举例2:
classFoo:#定义字符串类似属性字典的形式{'name':None,'age':None},只是定义了key,由这个类产生的实例不再具有__dict__属性字典__slots__=['name','age']#由Foo生成的实例只能有俩个定义的属性['name','age']f1=Foo()#f1实例传名字xixif1.name='xixi'print(f1.name)#返回结果:xixi#f1实例传年龄18f1.age=18print(f1.age)#返回结果:18#显示f1可以定义的属性['name','age'],就是print(Foo.__slots__)类的属性print(f1.__slots__)#返回结果:['name','age']##给f1加一个没有的属性gender会报错#f1.gender='male'(3)应用场景:使用需要构建在百万以上对象,且内存容量较为紧张的场景5.运算符重载:operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作
运算符特殊方法含义<,<=,==,>,>=,!=__lt__,__le__,__eq__,__gt__,__ge__,__ne__比较运算符+,-,*,/,%,//,**,divmod__add__,__sub__,__mul__,__truediv__,__mod__,__floordiv__,__pow__,__divmod__算数运算符,移位,位运算也有对应的方法+=,-=,*=,/=,%=,//=,**=__iadd__,__isub__,__imul__,__itruediv__,__imod__,_ifloordiv__,__ipow__(1)运算符重载举例
#创建一个Foo类classFoo:#创造构造函数可以传值ndef__init__(self,n):self.n=n#__iter__生成迭代器def__iter__(self):#加上__iter__方法变成把一个对象变成一个可迭代对象returnself#返回self新的迭代对象#__next__迭代def__next__(self):#必须有一个next方法#当n=9ifself.n==9:#StopIteration终止循环raiseStopIteration('终止')#每次n这个值自增self.n+=1#返回自增1这个值returnself.n#实例化传入参数得到f1这个对象f1=Foo(5)#for循环f1这个对象实际上先把这个对象变成f1.__iter__(),每次得到的结果是可迭代对象objforiinf1:#每次循环就是在调用obj下面的.__next_()方法print(i)####返回结果:6789举例:迭代器协议实现斐波那契数列
classFib:#创建一个类Fibdef__init__(self):#创造构造函数self.a=1#起始值aself.b=1#起始值bdef__iter__(self):#定义__iter__方法returnself#return自己def__next__(self):#定义__next__方法ifself.a>50:#当self.a大于50raiseStopIteration('终止了')#退出循环self.a,self.b=self.b,self.a+self.b#self.a等于self.b,self.b等于self.a+self.b俩个相加的结果returnself.a#返回self.af1=Fib()#得到f1这个对象print(next(f1))print(next(f1))print('==================================')foriinf1:#for循环从3以后开始print(i)####返回结果:12==================================35813213455(3)__contains__in成员运算符,没有实现,就调用__iter__方法遍历(4)__getitem__实现self[key]访问。序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable。key不存在引发KeyError异常(5)__setitem__和__getitem__的访问类似,是设置值的方法(6)__missing__字典使用__getitem__()调用时,key不存在执行该方法7.可调用对象(__call__方法)(1)python中一切皆对象,函数也不例外。
deffoo(x):print(x)#函数是个可调用对象,所以函数就有__call__方法print(dir(foo))##打印结果:['__annotations__','__call__','__class__','__closure__','__code__','__defaults__','__delattr__','__dict__','__dir__','__doc__','__eq__','__format__','__ge__','__get__','__getattribute__','__globals__','__gt__','__hash__','__init__','__kwdefaults__','__le__','__lt__','__module__','__name__','__ne__','__new__','__qualname__','__reduce__','__reduce_ex__','__repr__','__setattr__','__sizeof__','__str__','__subclasshook__']#函数对象functionobjectat0x0000000000917F28的__call__方法被method-wrapper包装print(foo.__call__)#打印:
classA:def__init__(self,x,y):self.x=xself.y=ydef__call__(self,*args,**kwargs):return"A({},{})".format(self.x,self.y)#实例化调用__init__拿到一个实例aa=A(4,5)#打印这个实例print(a)#返回:__main__.Aobjectat0x0000000000545D30>#在实例a后面加上括号调用相当于调用__call__方法print(a())#返回:A(4,5)#等价于#a=A(4,5)#a()#A()先调__init__返回实例本身,实例本身是一个可调用对象,在加一个()就调用__call__这个方法print(A(4,5)())#返回:A(4,5)8.上下文管理(__enter__,__exit__)(1)文件IO操作可以对文件对象使用上下文管理,使用with...as语法。
classOpen:def__init__(self,name):self.name=namedef__enter__(self):#2.with执行的时候触发__enter__print('执行enter')returnself#3.拿到一个结果是self,self会赋值给f,会执行with里的代码块def__exit__(self,exc_type,exc_val,exc_tb):#5.with里面的代码块执行完毕的时候会触发__exit__的执行print('执行exit')withOpen('xixi.txt')asf:#1.withOpen触发的是__enter__#print(f)#f就是Open产生的对象:<__main__.Openobjectat0x006C4870>print(f.name)#4.有了f这个对象就可以调用到f下面的属性##打印结果:执行enterxixi.txt执行exit总结:实例化对象的时候,并不会调用enter,进入with语句块调用__enter__方法,然后执行语句体,最后离开with语句块的时候,调用__exiit__方法with可以开启一个上下文运行环境,在执行前做一些准备工作,执行后做一些收尾工作。(4)上下文管理的安全性看看异常对上下文管理的影响。
classPoint:def__init__(self):print('初始化')def__enter__(self):#进去了帮我做了事情print('enter')def__exit__(self,exc_type,exc_val,exc_tb):print('exit')#离开的时候帮忙做了事情p=Point()withpasf:raiseException('error')####返回结果:Traceback(mostrecentcalllast):File"E:/wangshiyao/aaa/test/test2/aaa.py",line14,in
classOpen:def__init__(self,name):self.name=namedef__enter__(self):print('执行enter')#返回结果:执行enterreturnselfdef__exit__(self,exc_type,exc_val,exc_tb):print('执行exit')#返回结果:执行exit#显示错误信息:异常的类,没有返回Noneprint(exc_type)#显示错误信息:异常的值,没有返回Noneprint(exc_val)#显示错误信息:异常的追踪信息,没有返回Noneprint(exc_tb)returnTrue#代表把异常吞了可以执行withwithOpen('xixi.txt')asf:#打印Open实例赋值给fprint(f)#打印结果:<__main__.Openobjectat0x0000000000D2ADA0>#打印不存在的,with代码块在触发异常的时候会执行__exit__方法print(aaaaaaaaaaaaaa)#异常后不会执行到这步骤print(f.name)#程序还继续往下走打印这行print('~~~~~~~~~~~~~~~~~~~~~~~~~~')####返回结果:执行enter<__main__.Openobjectat0x0000000000D3A630>执行exit
classPoint:def__init__(self):passdef__enter__(self):#进去了帮我做了事情passreturnself#enter将自己的返回值赋给f,必须加returndef__exit__(self,exc_type,exc_val,exc_tb):pass#离开的时候帮忙做了事情p=Point()withpasf:#打印Point实例print(p)#打印结果:<__main__.Pointobjectat0x00000000009FA5F8>#打印Point实例赋值给fprint(f)#打印结果:<__main__.Pointobjectat0x00000000009FA5F8>#打印f和p是否相等print(f==p)#打印结果:True(7)上下文的应用场景:1)增强功能:在代码执行的前后增加代码,以增加其功能。类似装饰器的功能。2)资源管理:打开了资源需要关闭,例如文件对象,网络连接,数据库连接等。3)权限验证:在执行代码前,做权限的验证,在__enter__中处理9.__format__自定制格式化方法
#定义一个类salesclerk售货员classSalesclerk:#数据属性特征beautifulfeature='beautiful'#初始化函数def__init__(self,name,addr):self.name=name#店名self.addr=addr#地址#定义方法1:卖水果deffruit_sale(self):print('【%s】正在卖水果'%self.name)#定义方法2:卖蔬菜defsell_vegetables(self):print('【%s】正在卖蔬菜'%self.name)#实例化一个销售员b1=Salesclerk('美廉美超市','西城')#hasattr检测b1能不能调用的到name这个属性调用的到print(hasattr(b1,'name'))#返回结果:True#hasattr检测b1能不能调用的到fruit_sale这个属性调用的到print(hasattr(b1,'fruit_sale'))#返回结果:True(2)getattr(object,name[,default]):通过name返回object的属性值。当属性不存在,将使用default返回,如果没有default,则抛出AttributeError。name必须为字符串(3)setattr(object,name,value):object的属性存在,则覆盖,不存在,新增
classBase:n=5classA(Base):m=6def__init__(self,x):self.x=x#翻完所有字典后找这里def__getattr__(self,item):print('__getattr__',item)#打印x属性print(A(10).x)#打印结果:10#打印m属性print(A(10).m)#打印结果:6#打印n属性(可以找到父类的)print(A(10).n)#打印结果:5#如果找不到属性执行__getattr__A(10).y#打印结果:__getattr__y总结:一个类的属性会按照继承关系找,如果找不到,就会执行__getattr__()方法,如果没有这个方法,就会抛出AttributeError异常表示找不到属性查找顺序为:instance.dict->instance.class.dict->继承的祖先类(直到object)的dict->调用getattr()(2)__setattr__():实例通过.点设置属性,如同self.x=x,就会调用__setattr__(),属性要加到实例的__dict__中,就需要自己完成
classBase:n=5classA(Base):m=6def__init__(self,x):self.x=xdef__setattr__(self,key,value):print('__setattr__',key,value)self.__dict__[key]=value#塞到实例的字典中#实例化时调用self.x=x把10赋值过去触发__setattr__a=A(10)#打印结果:__setattr__x10#当前实例字典:print(a.__dict__)#打印结果:{'x':10}#触发修改实例属性x为100a.x=100#打印结果:__setattr__x100#触发设置实例属性y为200a.y=200#打印结果:__setattr__y200#打印修改后实例字典:print(a.__dict__)#打印结果:{'x':100,'y':200}总结:__setattr__()方法,可以拦截对实例属性的增加,修改操作,入股要设置生效,需要自己操作实例的__dict__(3)__delattr__():当通过实例来删除属性时调用此方法
classBase:n=5classA(Base):m=6def__init__(self,x):self.x=xdef__delattr__(self,item):print('delattr')#实例化时调用self.x=x把10赋值a=A(10)#触发删除实例属性xdela.x#打印结果:delattr#触发删除实例父类属性ndela.n#打印结果:delattr总结:可以阻止通过实例删除属性的操作。但是通过类依然可以删除属性(4)__getattribute__:实例所有的属性调用都从这个方法开始实例的所有的属性访问,第一个都会调用__getattribute__方法,它阻止了属性的查找,该方法应该返回(计算后的)值或者抛出一个AttributeError异常。它的retrun值将作为属性查找的结果。如果抛出AttributeError异常,则会直接调用__getattr__方法,因为表示属性没有找到举例:retrun返回值
classBase:n=5classA(Base):m=6def__init__(self,x):self.x=x#在去字典找属性之前,先拦住def__getattribute__(self,item):print('__getattribute__',item)returnitem#实例化时调用self.x=x把10赋值过a=A(10)#触发__getattribute__方法print(a.x)###返回结果__getattribute__xx