概述:
面向对象 是一种编程方式(OOP Object Oriented Programming).
三大编程范式:
一、面向过程
二、函数式编程
三、面向对象编程
Why:
面向对象的程序的程序设计:
python里要不就用面向过程,要不就用面向对象,两种编程网络
面向对象的程序设计:
可控性差,面向对象只解决了可扩展性的问题,一个软件包括了多个特性如图:
可见,面向对象并不是万能的。
How ( 如何使用面象对象编程)
第一步:面向对象的程序设计:OOD:
------找--------〉 对象归类(归纳对象共同的特征与技能,还有每个对象独有的特征。)
先定义类: 实例化对象
EXAMPLE: 如下例子,
程序设计:例如学校有:# 定义学生类共同的特征:Country=’China’共同的技能:查看成绩独有的特征:ID、名字,性别,省
编程:
用def 做面向对象编程
编程:def Student: country='China' def __init__(self,ID,NAME,SEX,PROVINCE): self.id=ID self.name=NAME self.sex=SEX self.province=PROVINCE def search_score(self): print('tell score') def study(self): print('study')
用class 面向对象编程:
例如, 恐怖份子这一类,基本上都人手一把AK47 , 这是必须的,因为他们想捣乱, . 然后他们可以去破坏五角大楼, 还能引起国家战争, .
#defind class
class terrorist: #恐怖份子 这个类 gun='ak47' #都有AK47 def trouble(): #能去破坏五角大楼 print('destory Amercan the Pentagon') def warfare(self): #能引起国家战争 print('country warfare %s'%self)# print(terrorist.gun)
# terrorist.trouble() # . 类. 点后面就可以调用 这个类的功能, 这个类能干什么.# terrorist.warfare('Iraq,muslin')
WHAT?
类型与类:
Python3 中,将类型和类统称为类
经典类与新式类:
在python3 当中类都是新式类(广度优先)。 而在python2.x当中新式类是class B(objcet) : pass
例如继承 B 的类 class C(B)
python2.x中的是经典类:经典类是深度优先
面向对象编程OOP:
类 是同一类事物特征的集合,对象是技能的结合体。
类的属性:
类的属性:数据属性与函数属性
调用方式通过 .点的方式就可引用类的属性
类属性的增、删、改、查
class Chinese: 'this is chinese class' dang='中华人民' country='China' def __init__(self,name,age,gender): # print('I"m initailize ') self.name=name self.age=age self.gender=gender # self.sui_di_tu_tan='ok tu' # print('I"m over ') def sui_di_tu_tan(self): print('吐一口痰') def cha_dui(self): print('插到了前面') def eat_food(self,food): print('%s 正在吃 %s'%(self.name,food)) def play_basketball(self,ball): print('%s 正在打 %s'%(self.name,ball))# p1=Chinese('tony',19,'male')# print(p1.__dict__) #查看对象的属性字典,这里对象只有数据属性,函数属性都是引用类的函数属性。 对象(实例)是没有自己的函数属性的。# print(p1.gender)# print(p1.name)# print(p1.sui_di_tu_tan)# p1.eat_food('baozi')# p2=Chinese('wu sir ',100,'nianmen')# p2.eat_food('jiucaixianbin')# print(dir(p2))# print(Chinese.country)# Chinese.country='American'# print(Chinese.country)# p1=Chinese('tony','29','male')# print(p1.country)## Chinese.food='fang'# print(Chinese.food)# del Chinese.eat_food# print(Chinese.food)# def dance(self,wu):# print('%s 正在跳%s'%(self.name,wu))# Chinese.dance=dance# # print(Chinese.dan)# p1.dance('tuoyiwu')# print(Chinese.dang)# # print(Chinese.sui_di_tu_tan())# # print(dir(Chinese))# Chinese.__dict__['sui_di_tu_tan']()# Chinese.__dict__['cha_dui']('af')# # print(Chinese.__doc__)# print(Chinese.__class__)p1=Chinese('liang',18,'man')# print(p1.name)# p1.play_basketball('backetball')# print(p1.play_basketball)p1.hobby='women'# print(p1.hobby)def aihao(self): print(‘women’)p1.aihao=aihao# print(p1.__dict__)# p1.aihao('self')#实例不能自己定义函数属性,因为这违反了class自动传self 的功能。#所以实例只能有数据属性,能定义函数属性是因为修改了__dict__
del p1.gender# p1.gender='women'# print(p1.gender)# print(p1.__dict__)# p1.gender='male'# print(p1.gender)class People: contry='China' l=[1,2,3] def __init__(self,name): self.name=namep1=People('liang')print(p1.l)# p1.l=['a','b']p1.l.append('cc')p1.contry='japan'print(People.l)print(p1.__dict__)print(p1.l)
继承:
继承是一种创建新的类:解决代码重用
定义一个电脑的类,电脑有很多的品牌,再多的牌子它们还是电脑
继承父类与子类的关系是 ‘是’ 的关系,如:人是动物,狗也是动物,
都继承动物类。
#!-*- coding:utf-8 -*-class Compute: def __init__(self,name,cpu,disk,mem): self.name=name self.cpu=cpu self.disk=disk self.mem=mem def read_file(self,cpu): print('cpu running read_file') def brand(self,name): print('brand is %s'%name)class Thinkpad(Compute): def __init__(self,name,cpu,disk,mem,small_read): Compute.__init__(self,name,cpu,disk,mem) self.small_read=small_read print(small_read,disk) def price(self,price): print('price is %s'%price)Thinkpad_x220=Thinkpad(Thinkpad,'thinkpad_x220','i7','200GB','yes',)Thinkpad_x220.price(20000)print(Thinkpad_x220)print(Thinkpad.__bases__)
查看继承:
print('我是继承查看',Thinkpad.__bases__)
继承不是遗传
组合:(为了节省代码) 组合是‘有’的关系,电脑可以WIND7 MACOS ,但电脑不是 WIN7 MACOS
#使用组合 实现功能class Thinkpad: def __init__(self,name,years,system): self.name=name self.years=years self.system=systemclass lenvo: def __init__(self,name,years,system): self.name=name self.years=years self.system=systemclass Mac: def __init__(self,name,years,system): self.name=name self.years=years self.system=systemclass System: def __init__(self,name): self.name=name # print('oprate system is %s'%name)oprate_sys=System(['windows 7 ','Mac OS'])oprate_mac=System('Mac OS')compute1=Thinkpad('x220','2010',oprate_sys)compute2=lenvo('lenvo E430','2010',oprate_sys)compute3=Mac('Mac Air','2010',oprate_sys)print(compute1.system.name[0])print(compute2.system.name[0])print(compute3.system.name[1])import abcprint(lenvo.__bases__)print(Mac.__bases__)
接口归一化设计:
把应该有的功能实现一个集合体,然后子类来实现
抽象类: 上述的rais 抛出异常的方式不可取,
本质还是类,与类的区别在于,与变通类的额外的特点是:加了装饰器函数,子类必须实现他们
练习:
定义老师类,把老师的属性:薪资,隐藏起来,然后针对该属性开放访问接口苑昊老师有多种癖好,把这种癖好隐藏起来,然后对外提供访问接口,而且以后还会苑昊老师培养很多其他的癖好,对外开放修改接口,可以新增癖好,并且需要保证新增的癖好都是字符串类型,否则无法增加成功。class Teacher(object): __salary=25000 __hobby=['somke','drink'] def __init__(self,name,age,sex): self.name=name self.age=age self.sex=sex def hobby_modify(self,mod_hobby): if type(mod_hobby) is str: Teacher.__hobby.append(mod_hobby) print('you hobby very special') else: print('Can\'t be number or sign') def salary(self): """隐藏属性 查看接口""" print('salary: %s'%Teacher.__salary) def hobby(self): return(Teacher.__hobby)T1=Teacher('yh','23','male')T4=Teacher('eg','22','male')T2=Teacher.hobby(T1)T2=T2[:]T3=','.join(T2)#teacher eg hobbyprint('__________________________________________________')def func1(n): n.namefunc1(T4)T4.salary()T4.hobby_modify('running')print(T4.name,T4.age,'%s habby %s'%(T4.name,T4.hobby()))print('__________________________________________________')# 访问隐藏属性def func(n): n.salary()func(T1)print('teacher %s have %s'% (T1.name,T3))mod_hobby=Teacher.hobby_modify(T1,'drive car')print(Teacher.hobby(T1))print('----------------------------')
多态与多态性:
一种事物 不同的形态,多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)
##一切皆文件:多态 与多态性练习题 import abc class All_file(metaclass=abc.ABCMeta): @abc.abstractmethod def all(self): pass class disk(All_file): def all(self): print('disk is file') class process(All_file): def all(self): print('process is file') class Txt(All_file): def all(self): print('Txt file is file') d1=Txt() d2=disk() # print(d1.all()) def func(d1): d1.all() func(d1) func(d2)
多态性:
多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
多态性分为静态多态性和动态多态性
多态性是‘一个接口(函数func),多种实现(如f.click())
派生:
继承是子类继承父类,但是子类都有自己类的特征,这个就叫做“派生”。如果子类有自已的特征且与父类的特征有重名,就找自己的特征
但是又要在父类的基础上增加自己的功能,那就是重用父类的特征加上自己的特征。
在子类中调用父类的特征。
封装:
封装:
第一层封装:
第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装
第二层封装:
__名字(双下划线),这种语法,只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果。
在python中用双下划线的方式实现隐藏属性(设置成私有的)
要想不让外部访问需要用到一个函数__getattr__ 详细见 进阶篇
特性:
property 是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
import mathclass Circle: def __init__(self,radius): self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算机面积 @property def perimeter(self): return 2**math.pi*self.radius #计算周长c=Circle(10)print(c.area) #可以像访问 “数据属性” 一样去访问area ,一访问就会触发一个函数的执行,动态计算出一个值print(c.perimeter) #和上面一样----------------------------------------------------------如果不用porperty , 那访问就不能像访问数据属性一样去访问了class Circle: def __init__(self,radius): self.radius=radius def area(self): return math.pi * self.radius**2 def perimeter(self): return 2**math.pi*self.radiusc=Circle(10) print(c.area)print(c.perimeter)
使用了property 以后不能被赋值:
不使用porperty 可以被赋值:
为什么要用property:
将一个类的函数定义成特性以后,对象再去使用的时候 obj.name根本无法察觉自己的name是执行了一个函数,这种特性的使用方式遵循了
统一访问的原则
除此之外:
面向对象的封装有三种方式:
[public]
这种其实就是不封装,是对外公开的
[protected]
这种封装方式对外不公开,但对朋友或者子类公开
[private]
这种封装对谁都不公开
python 没有将以上三个内建到自己的class机制中,在python中通过porperty 实现。
class Foo: def __init__(self,val): self.__name=val #将数据属性都隐藏起来 @property def name(self): #执行查询操作此函数运行 print('我是查看') return self.__name #f.name 访问的是self.__name(这才是真实值的存放位置) @name.setter def name(self,value): #执行设置操作此函数运行(设置就是赋值) print('我是设置') if not isinstance(value,str): raise TypeError('must be str') self.__name=value @name.deleter #执行删除操作此函数运行 def name(self): print('我是删除') raise TypeError('Can not delete ')f=Foo('tony')print(f.name)
从外面访问不到类里的数据属性 print(Foo.__name)
通过对象才能访问到类的数据属性
print(f.name)
这就是封装
查询:
删除:
设置:
练习:
#!/usr/bin/env python#!-*- coding:utf-8 -*-def write_func(): with open('user.db','w') as file_write: file_write.write(str({ 'tony':{ "password":"123",'status':False,'timeout':0},'liang':{ "password":"456",'status':False,'timeout':0},}))def read_func(): with open('user.db','r') as file_read: f=file_read.read() data=eval(f) print(data['tony']['status']) print(data['liang']['status'])# write_func()# read_func()#要求三:分析下述代码的执行流程class User: db_path='user.db' def __init__(self,username): self.username=username @property def db(self): data=open(self.db_path,'r').read() return eval(data) # @setattr() # def db(self): # raise TypeError:("cat't delete "# u=User('tony')# print(u.db['tony']['age'])import timeclass User: db_path='user.db' def __init__(self,name): self.name=name @property def db(self): with open(self.db_path,'r') as locked_check: info=locked_check.read() data=eval(info) return 'locked time %s '%data[self.name]['locked_time'] # @property # def db(self): # with open(self.db_path,'r') as read_file: # info=read_file.read() # return eval(info) @db.setter def db(self,value): with open(self.db_path,'w') as write_file: write_file.write(str(value)) write_file.flush() def login(self): data=self.db if data[self.name]['status']: print('已经登录') return True if data[self.name]['timeout'] < time.time(): count=0 while count < 3: passwd=input('password>>: ') if not passwd:continue if passwd == data[self.name]['password']: data[self.name]['status']=True data[self.name]['timeout']=0 self.db=data break count+=1 else: data[self.name]['timeout']=time.time()+10 if data[self.name]['status']: return else: data[self.name]['locked_time']=time.ctime() print('locked time %s'%data[self.name]['locked_time']) self.db=data else: print('账号已经锁定10秒')u1=User('tony')# u1.login()# u2=User('liang')# print(u1.db)#要求四:根据上述原理,编写退出登录方法(退出前要判断是否是登录状态),自定义property,供用户查看自己账号的锁定时间class User: db_path='user.db' def __init__(self,name): self.name=name @property def db(self): with open(self.db_path,'r') as locked_check: info=locked_check.read() data=eval(info) return 'locked time %s '%data[self.name]['locked_time'] @property def db(self): with open(self.db_path,'r') as read_file: info=read_file.read() return eval(info) @db.setter def db(self,value): with open(self.db_path,'w') as write_file: write_file.write(str(value)) write_file.flush() def login(self): data=self.db if data[self.name]['status']: print('已经登录') return True if data[self.name]['timeout'] < time.time(): count=0 while count < 3: passwd=input('password>>: ') if not passwd:continue if passwd == data[self.name]['password']: data[self.name]['status']=True data[self.name]['timeout']=0 print('welecome') self.db=data break count+=1 else: data[self.name]['timeout']=time.time()+10 if data[self.name]['status']: return else: data[self.name]['locked_time']=time.ctime() print('locked time %s'%data[self.name]['locked_time']) self.db=data else: print('账号已经锁定10秒') # @property def logout(self): with open('user.db','r') as logout_check: f=logout_check.read() data=eval(f) # return 'login status "%s" '%data[self.name]['status'] if data[self.name]['status']: # return ('alread login ,logout? "y" or continue "n"') choice=input('alread login ,logout? "y" or continue "n": ').strip() if choice=='y': data[self.name]['status']=False self.db=data print('logout') # return 'logout' else: return# u1=User('tony')# u1.login()u2=User('liang')u2.logout()# u2.login()
封装与扩展性:
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码,而外部调用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供了一个良好的合作基础---或者说,只要接口这个基础约定不变,则代码改变不足虑。
class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时要求的是面积 return self.__width*self.__lengthr1=Room('badroom','tony',20,20,20)print(r1.tell_area()) #使用者调用接口tell_area
再看:
class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self):#对外提供的接口,隐藏了内部的实现细节,此时要求的是面积,,现在内部逻辑变了,而外部调用者感知不到,仍然使用该方法,但是功能已经变了
return self.__width*self.__length*self.__highr1=Room('badroom','tony',20,20,20)print(r1.tell_area()) #使用者调用接口tell_area ,已经使用新功能
静态方法和类方法:
通常情况下,在类中定义的所有函数(所有 ,包括self,它只不过也是个普通的参数而已)都是对象的绑定方法,对象在调用绑定方法时会将自己做为参数传递给方法的第一个参数。除此之外还有两种常见的方法: 静态方法和类方法,二者为类量身定制,但是实例非要使用,也不会报错,后续将介绍
1、静态方法:
是一个普通函数,位于类定义的 命名空间 中,不会对任何实例类型进行操作,python为我们内置了函数staticmethod来把类中的函数定义成静态方法
class Foo: def spam(x,y,z): #类中的一个函数,self和x 没有什么区别 print(x,y,z) spam=staticmethod(spam) #将spam函数做成静态方法#简单写法class Foo: @staticmethod #等同于spam=staticmethod(spam) def spam(x,y,z): print(x,y,z)#调用print(type(Foo.spam))#类型本质就是函数Foo.spam(1,2,3) #调用函数应该有几个参数就传几个参数
f1=Foo() f1.spam(1,2,4) #实例也可以使用,但通常静态方法都是给类用的,实例在使用的时候丧失了自动传值的功能
#应用场景:编写类时需要采用很多不同的方式来创建实例,而我们只有一个__init__函数,此时静态方法就派上用场了
import timeclass Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now():#用Date.now()形式去产生实例,该实例用的是当前时间 t=time.localtime() #获取结构化的时间格式 return Date(t.tm_year,t.tm_mon,t.tm_mday) #新建实例并返回 @staticmethod def tommorrow(): #用Date.tommorrow()的形式去产生实例,该实例用的是明天的时间 t=time.localtime(time.time()+86400) return Date(t.tm_year,t.tm_mon,t.tm_mday)a=Date('1929',11,23) #自定义时间b=Date.now() #采用当前时间c=Date.tommorrow() #采用明天时间print(a.year,a.month,a.day)print(b.year,b.month,b.day)print(c.year,c.month,c.day)
2、类方法
类方法是给类用的,类在使用时会将类本身当做参数传给类方法的第一个参数,python 为我们内置了classmethod来把类中的函数定义成类方法
class A: x=1 @classmethod def ceshi(cls): print(cls,cls.x)class B(A): x=2B.ceshi()
实际应用:
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @staticmethod def now(): t=time.localtime() return Date(t.tm_year,t.tm_mon,t.tm_mday)class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s'%(self.year,self.month,self.day)e=EuroDate.now()print(e) #这里是想触发EuroDate.__str__,但是结果是<__main__.Date object at 0x000000000113C080>#因为e就是用Date类产生的,所以根本不会触发EuroDate.__str__,解决方法就是用classmethod
classmethod 修改
class Date: def __init__(self,year,month,day): self.year=year self.month=month self.day=day @classmethod def now(cls): t=time.localtime() return cls(t.tm_year,t.tm_mon,t.tm_mday)class EuroDate(Date): def __str__(self): return 'year:%s month:%s day:%s'%(self.year,self.month,self.day)e=EuroDate.now()print(e)
结果: