面向对象编程
类和对象
1.什么叫类:类是一种数据结构,就好比一个模型,该模型用来表述一类事物(事物即数据和动作的结合体),用它来生产真实的物体(实例)。
2.什么叫对象:睁开眼,你看到的一切的事物都是一个个的对象,你可以把对象理解为一个具体的事物(事物即数据和动作的结合体)
(铅笔是对象,人是对象,房子是对象,狗是对象,alex是对象,配齐是对象,元昊是对象)
3.类与对象的关系:对象都是由类产生的,上帝造人,上帝首先有一个造人的模板,这个模板即人的类,然后上帝根据类的定义来生产一个个的人
4.什么叫实例化:由类生产对象的过程叫实例化,类实例化的结果就是一个对象,或者叫做一个实例(实例=对象)
三大编程范式
编程范式即编程的方法论,标识一种编程风格
三大编程范式:
1.面向过程编程:捂裆派
2.函数式编程:峨美眉妹妹派
3.面向对象编程:少林蛋黄派
面向对象设计与面向对象编程
面向对象设计(Object oriented design):将一类具体事物的数据和动作整合到一起,即面向对象设计
1 #用基于结构化的语言来实现面向对象设计 2 def wang(name,genle,type): 3 def jiao(dog): 4 print("%s正在叫"%dog["name"]) 5 def chi(dog): 6 print("%s正在吃"%dog["name"]) 7 def init(name,genle,type): 8 dog1 ={ 9 "name":name, 10 "genle":genle, 11 "type":type, 12 "jiaoa":jiao, 13 "chi":chi 14 } 15 return dog1 16 return init(name,genle,type) 17 18 d1 = wang("yuanhao","gong","tianyuanquan") 19 print(d1) 20 d1["jiaoa"](d1)
面向对象编程(object-oriented programming):用定义类+实例/对象的方式去实现面向对象的设计
1 class Chinese: 2 name = "abc" 3 def chifan(self,x): 4 print("姓名:%s喜欢吃%s"%(self.mingzi,x)) 5 def shuijiao(self): 6 print("nianling:%s"%self.nianling) 7 def __init__(self,name,age): 8 self.mingzi = name 9 self.nianling = age 10 f1 = Chinese("alex",18) 11 f1.chifan("eat") 12 f1.shuijiao()
类的声明
1 大前提: 2 1.只有在python2中才分新式类和经典类,python3中统一都是新式类 3 2.新式类和经典类声明的最大不同在于,所有新式类必须继承至少一个父类 4 3.所有类甭管是否显式声明父类,都有一个默认继承object父类(讲继承时会讲,先记住) 5 在python2中的区分 6 经典类: 7 class 类名: 8 pass 9 10 经典类: 11 class 类名(父类): 12 pass 13 14 在python3中,上述两种定义方式全都是新式类
python3中声明类:
1 1 ''' 2 2 class 类名: 3 3 '类的文档字符串' 4 4 类体 5 5 ''' 6 6 7 7 #我们创建一个类 8 8 class Data: 9 9 pass 10 10 11 11 #用类Data实例化出一个对象d1 12 12 d1=Data()
类的属性
类有数据属性跟函数属性(又称方法属性)
1 class Chinese: 2 name = "abc" 3 def chifan(self,x): 4 print("姓名:%s喜欢吃%s"%(self.mingzi,x)) 5 def shuijiao(self): 6 print("nianling:%s"%self.nianling) 7 def __init__(self,name,age):#为实例定制数据属性,可以使用类的一个内置方法__init__()该方法,在类()实例化是会自动执行 8 self.mingzi = name 9 self.nianling = age 10 f1 = Chinese("alex",18) #类的实例化,相当于运行__init__函数,将参数传给__init函数体中对应的位置,生成一个字典,其中self即代表f1自身 11 f1.chifan("eat")#通过.调用类的属性,首先会在__init__作用域内查找,若没找到会到类的属性中寻找,实例化生成的对象只具备数据属性,调用的方法为类的属性并非生成的对象所具备的属性 12 f1.shuijiao()
有两种方法查看类的属性
dir(类名):查出的是一个名字列表
类名.__dict__:查出的是一个字典,key为属性名,value为属性值
1 class Chinese: 2 name = "abc" 3 def chifan(self): 4 print(123) 5 def shuijiao(self): 6 print("456") 7 print(Chinese.name)#打印的是name属性对应的内容 8 print(Chinese.__dict__)#显示结果是一个字典,包含类的所有属性:属性值 9 print(dir(Chinese))#显示结果是一个列表,包含类(包含内建属性在内的)所有的属性名
特殊的类属性
__name__,类的名字
__doc__,查看类的文档字符串
__module__,类定义所在的模块__class__,实例C对应的类(仅新式类中)
类的方法属性的增删改查
1 class math: 2 country = "China" 3 def chi(self,food): 4 print("%s正在吃%s"%(self.mingzi,food)) 5 def __init__(self,name): 6 self.mingzi = name 7 f1 = math("alex") 8 print(math.country) #查看 9 # print(dir(math)) 10 # print(math.__dict__) 11 def yundong(self,hobby):#增加 12 print("%s正在%s"%(self.mingzi,hobby)) 13 math.aihao = yundong 14 #print(math.__dict__) 15 f1.aihao("打篮球") 16 #del math.chi 17 #print(math.__dict__) 18 def aichi(self,food):#修改 19 print("%s很开心的吃%s"%(self.mingzi,food)) 20 math.chi =aichi 21 f1.chi("面")
类的数据属性的增删改查
1 class math: 2 country = "China" 3 def chi(self,food): 4 print("%s正在吃%s"%(self.mingzi,food)) 5 def __init__(self,name,age): 6 self.mingzi = name 7 self.nianling = age 8 9 f1 = math("alex",18) 10 print(f1.__dict__) #查看 11 f1.mingzi = "eric" #修改 12 print(f1.__dict__) 13 f1.xingbie = "男" # 增加 14 del f1.mingzi #删除 15 print(f1.__dict__)
类属性与对象(实例)属性
1.实例化会自动触发init函数的运行,最后返回一个值即实例,我们要找的实例属性就存放在init函数的局部作用域里
2.类有类的属性字典,就是类的作用域,实例有实例的属性字典,即实例的作用域
3.综上,一个点代表一层作用域,obj.x先从自己的作用域找,自己找不到去外层的类的字典中找,都找不到,就会报错
4.在类中没有使用点的调用,代表调用全局变量
1 country = "China" 2 class Chinese: 3 country = "japan" 4 def __init__(self,name): 5 self.mingzi = name 6 print(country)#打印的是普通变量country,非类结构中的变量,要调用类中的变量要用"."调用 7 f1 = Chinese("alex")
静态属性、类方法、静态方法
1 class Room: 2 arg = 123 3 def __init__(self,name,weith,lenth,height): 4 self.mingzi = name 5 self.kuandu = weith 6 self.changdu = lenth 7 self.gaodu = height 8 @property#静态属性,将实例化对象变成类的数据属性,将该方法封装起来,作用可隐藏逻辑代码,直接用实例+"."调用该方法 9 def tiji(self): 10 return self.kuandu*self.changdu*self.gaodu 11 12 @classmethod#类方法,专门供类使用,与实例无关,类方法只能访问类相关的属性,不能访问实例属性,与实例无关 13 def tell_info(cls,x):#默认参数cls不可变,为类名,后边可接参数 14 print(cls) 15 print("------>",Room.arg,x) 16 17 @staticmethod #静态方法 ,类的静态方法,没有默认参数,不能使用类变量和实例变量 18 def op(x,y,z): 19 print(x,y,z) 20 f1 = Room("alex",10,50,20) 21 print(f1.tiji)#由于用了property方法封装该方法(已变成数据属性),故可直接调用该数据属性 22 Room.tell_info("abc") 23 Room.op(1,2,3) #类传参数可以调用 24 f1.op(1,2,3) #实例传参数可以调用
组合
作用:做关联
1 class School: 2 def __init__(self,name,difang): 3 self.name = name 4 self.difang = difang 5 class Teacher: 6 def __init__(self,name,sex,school): 7 self.name = name 8 self.sex = sex 9 self.school = school 10 class Lesson: 11 def __init__(self,name,price,period,techer): 12 self.name = name 13 self.price = price 14 self.period = period 15 self.techer = techer 16 17 p1 = School("old boy","北京") 18 t1 = Teacher("alex","male",p1)#将p1对象直接添加到t1对象属性中 19 L1 = Lesson("python班",10000,"4month",t1)
面向对象编程三大特性
(1)类的继承:类的继承跟现实生活中的父、子、孙子、重孙子、继承关系一样,父类又称为基类。
python中类的继承分为:单继承和多继承
1 class ParentClass1: 2 pass 3 4 class ParentClass2: 5 pass 6 7 class SubClass(ParentClass1): #单继承 8 pass 9 10 class SubClass(ParentClass1,ParentClass2): #多继承 11 pass
子类继承了基类的所有属性
1 class Dad: 2 money = 10 3 def __init__(self,name): 4 self.name = name 5 def hit_son(self): 6 print("打人") 7 class Son(Dad):#继承参数“类”的所有属性 8 money = 5000#当子级类属性跟父级类属性重名时,调用当级类方法会先从当级寻找,找不到会去父级找 9 pass 10 p1 = Son("eric") 11 print(p1.name) 12 p1.hit_son() 13 print(p1.money)
当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好
当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好
继承同时具有两种含义:
含义一.继承基类的方法,并且做出自己的改变或者扩展(代码重用)
含义二.声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法
1 接口继承 2 import abc #调用接口继承模块 3 4 class All_file(metaclass=abc.ABCMeta):#声明某个子类兼容于某基类,定义一个接口类,子类继承接口类,并且实现接口中定义的方法 5 @abc.abstractclassmethod #装饰器使方法具备接口继承特性 6 def read(self): 7 pass 8 @abc.abstractclassmethod 9 def write(self): 10 pass 11 12 class Disk(All_file):#子类继承基类时,必须对基类的方法进行派生才可实例化 13 def read(self): 14 print("disk-read") 15 def write(self): 16 print("disk-write") 17 class Cd(All_file): 18 def read(self): 19 print("cd-read") 20 def write(self): 21 print("cd-write") 22 class Mem(All_file): 23 def read(self): 24 print("mem-read") 25 def write(self): 26 print("mem-write") 27 p1 = Disk() 28 p1.read()
继承顺序:python如果继承了多个类,遵循深度优先跟广度优先顺序
当类时经典类时,多继承情况下,会按照深度优先方式查找
当类是新式类时,多继承情况下,会按照广度优先方式查找
经典类与新式类的差别:新式类在定义时便自动继承了object类,而经典类没有
新式类继承顺序:对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表
为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
而这个MRO列表的构造是通过一个C3线性化算法来实现的。我们不去深究这个算法的数学原理,它实际上就是合并所有父类的MRO列表并遵循如下三条准则:
1.子类会先于父类被检查
2.多个父类会根据它们在列表中的顺序被检查
3.如果对下一个类存在两个合法的选择,选择第一个父类
1 继承顺序 2 3 4 class A: 5 def test(self): 6 print("testA") 7 class B(A): 8 def test(self): 9 print("testB") 10 class C(A): 11 def test(self): 12 print("testC") 13 class D(B): 14 def test(self): 15 print("testD") 16 class E(C): 17 def test(self): 18 print("testE") 19 class F(D,E): 20 def test(self): 21 print("testF") 22 f1 = F() 23 print(F.__mro__)#(<class '__main__.F'>, <class '__main__.D'>, <class '__main__.B'>, <class '__main__.E'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>) 24 f1.test()#实例调用方法时,遵循mro顺序,会优先从子级寻找,找不到往上,先广度优先,若还找不到会找最深一级,若还没有会报错
子类中调用父类方法
1 class jiaotong: 2 def __init__(self,name,speed): 3 self.name = name 4 self.speed = speed 5 def run(self): 6 print("%srunning!"%self.name) 7 class bicycle(jiaotong): 8 def __init__(self,name,speed,type): 9 #jiaotong.__init__(self,name,speed)#第一种方法:类调用,子级继承父级,可以在调用父级方法的基础上进行派生,减少重复代码 10 super().__init__(name,speed)#第二种方法:用内置super().调用父级方法 11 self.type = type 12 def run(self): 13 jiaotong.run(self) 14 print("%s开动了"%self.name) 15 16 17 f1 = bicycle("danche",10,"meilida") 18 print(f1.name,f1.speed,f1.type) 19 f1.run()
多态、封装、
类的继承有两层意义:继承和扩展
多态实际上就是继承的过程体现
1 class Dt: 2 def __init__(self,name,temputer): 3 self.name = name 4 self.temputer = temputer 5 def fu(self): 6 if self.temputer < 0: 7 print("【%s】温度太低变成冰了!"%self.name) 8 elif self.temputer > 0 and self.temputer < 100: 9 print("【%s】变成水了!"%self.name) 10 elif self.temputer > 100: 11 print("【%s】温度太高变成蒸汽了!"%self.name) 12 w1 = Dt("water",-2) 13 ice = Dt("ice",5) 14 steam = Dt("steam",150) 15 w1.fu() 16 ice.fu() 17 steam.fu()
封装的概念就是隐藏
第一个层面的封装:类就是麻袋,这本身就是一种封装
例:略
第二个层面的封装:类中定义私有的,只在类的内部使用,外部无法访问,类中定义属性名时在其前面加上_或__可实现该属性的隐藏,但这种隐藏只是一种语言上的约定,python并不会从底层禁止你访问
1 class People: 2 _population = "60亿" 3 __star = "earth" 4 def __init__(self,name,salary): 5 self.name = name 6 self.slary = salary 7 p1 = People("alex",1000) 8 print(p1._population)#对于第一种用_封装的属性,虽然是私有的,但仍然可以访问 9 10 print(p1._People__star)#对于第二种用__封装的属性,在其前面加上_类名仍然可以访问
python并不会真的阻止你访问私有的属性,模块也遵循这种约定,如果模块名以单下划线开头,那么from module import *时不能被导入,但是你from module import _private_module依然是可以导入的
其实很多时候你去调用一个模块的功能时会遇到单下划线开头的(socket._socket,sys._home,sys._clear_type_cache),这些都是私有的,原则上是供内部调用的,但通过特殊方法仍然可以调用
注意:双下滑线开头的属性在继承给子类时,子类是无法覆盖的(原理也是基于python自动做了双下滑线开头的名字的重命名工作)