一、面向对象编程介绍(与面向过程编程对比)
# 面向过程编程:
# 核心是过程二字,过程是解决问题的步骤,即先干什么再干什么后干什么....
# 基于该思想编写程序就好比在设计一条条流水线,是一种机械式的思维方式
#
# 优点:复杂的问题流程化进而简单化
# 缺点:可扩展性差
#
# 面向对象编程:
# 核心是对象二字,对象是特征与技能的结合体
# 基于该思想编写程序就好比在创造一个世界,世界都是由一个个的对象组成,你就是这个世界的上帝
#
# 优点:可扩展性强
# 缺点:编程的复杂度高于面向过程
二、类与对象
1 #二、类与对象介绍 2 3 # 类介绍: 4 # 对象是特征与技能的结合体,类则是一系列对象相似的特征与技能的结合体 5 # ps:总结类与对象必须站在特定的角度去区分 6 # 7 # 先有类or先有对象 8 # 在现实世界中:先有的对象,再有类 9 # 在程序中:务必保证先定义类,后调用类来产对象 10 11 # 为什么要有类???《类就是为了节省内存空间而产生的,将每个对象相同的属性,存成一份放到类中》 12 # 为什么要有对象??《》 13 14 15 ''' 16 站在培训机构选课系统的角度 17 在现实世界中: 18 对象1: 19 特征: 20 学校='BeiDa' 21 名字='王三炮' 22 性别='male' 23 年龄=38 24 技能: 25 选课 26 27 对象2: 28 特征: 29 学校='BeiDa' 30 名字='王二炮' 31 性别='male' 32 年龄=18 33 技能: 34 选课 35 36 对象3: 37 特征: 38 学校='BeiDa' 39 名字='王大炮' 40 性别='female' 41 年龄=48 42 技能: 43 选课 44 45 对象4: 46 特征: 47 学校='BeiDa' 48 名字='Egon' 49 性别='male' 50 年龄=18 51 等级=10 52 技能: 53 打分 54 55 站在对象的角度来看: 56 总结出现实世界中培训机构的学生类: 57 相似的特征: 58 学校='BeiDa' 59 相似的技能 60 选课 61 ''' 62 63 # 第一步:先定义类 64 # #####注意:类体代码在类定义阶段就会立即运行,会产生类的名称空间,用来将类体代码运行过程中产生的名字都存放起来 65 # #####对比:import 模块名字《在执行import 语句时,会立马将模块中的代码运行过程中产生的名字存放起来》 66 # #####区别于函数:函数体代码在定义阶段只会检查语法而不执行代码体,而在调用函数时,函数体代码才会执行,得到返回值。 67 class BeiDaStudent: #产生了BeiDaStudent的名称空间 68 # 相似的特征 69 school = 'BeiDa' 70 71 #相似的技能 72 def choose_course(self): 73 print('choose course...') 74 # print('==run==>') 75 76 # print(BeiDaStudent.school) #得到类中 变量school的值 77 # print(BeiDaStudent.choose_course) #得到类中 choose_course函数的内存地址 78 # print(BeiDaStudent.choose_course(1)) #得到类中 choose_course函数执行后的值 79 # print(BeiDaStudent) #得到BeiDaStudent这个类的名称空间 80 # print(BeiDaStudent()) 81 82 83 # 类的用途一:类的本质就是一个名称空间,所以第一种用途就是'增删改查'其内部的名字,类名.名字,这些我类中所有的都称之为类的属性 84 85 # BeiDaStudent.school = 'beida' #修改类的属性 86 # del BeiDaStudent.school #删除类的属性 87 # BeiDaStudent.xxx = 111 #新增类的属性 88 # print(BeiDaStudent.__dict__) #查看类的所有属性 89 # print(BeiDaStudent.__dict__['school']) #通过内置函数去调用类的属性,不建议使用 90 # print(BeiDaStudent.__dict__) 91 92 93 stu1 = BeiDaStudent() #调用类,创建一个对象1 94 stu2 = BeiDaStudent() #调用类,创建一个对象2 95 stu3 = BeiDaStudent() #调用类,创建一个对象3 96 97 # print(stu1) #0x106afe208 98 # print(stu2) #0x106b1d668 99 # print(stu3) #0x106fe9780 100 101 102 # 类就是个名称空间 103 # 对象的本质也是一个名称空间,把该对象独有的属性存放到该对象的名称空间 104 # 如:函数也是一种对象,每一个函数都拥有自己独有的名称空间。 105 106 # print(stu1.__dict__) #创建好一个对象后,该对象的名称空间就会形成一个空字典,用来存放该对象中的独有的属性 107 # stu1.name='王三炮' 108 # stu1.sex='male' 109 # stu1.age=38 110 # print(stu1.__dict__) 111 112 # stu2.name='王二炮' 113 # stu2.sex='male' 114 # stu2.age=18 115 116 # stu3.name='王大炮' 117 # stu3.sex='female' 118 # stu3.age=48 119 120 # print(stu1.__dict__) 121 # print(stu2.__dict__) 122 # print(stu3.__dict__) 123 # print(stu1.name) # stu1.__dict__['name'] 124 125 126 # 总结: 127 # 类就是个名称空间 128 # 对象的本质也是一个名称空间,把该对象独有的属性存放到该对象的名称空间 129 # 如:函数也是一种对象,每一个函数都拥有自己独有的名称空间。 130 131 132 # 属性查找 133 # print(stu1.name) 134 # print(stu1.choose_course)
三、对象的初始化与__init__方法
1 # 原始代码:(臃肿,繁琐) 2 # class BeiDaStudent: #产生了BeiDaStudent的名称空间 3 # # 相似的特征 4 # school = 'BeiDa' 5 # 6 # #相似的技能 7 # def choose_course(self): 8 # print('choose course...') 9 # # print('==run==>') 10 # 11 # stu1 = BeiDaStudent() 12 # stu2 = BeiDaStudent() 13 # stu3 = BeiDaStudent() 14 15 16 17 # print(stu1.__dict__) 18 # stu1.name='王三炮' 19 # stu1.sex='male' 20 # stu1.age=38 21 22 # stu2.name='王二炮' 23 # stu2.sex='male' 24 # stu2.age=18 25 26 # stu3.name='王大炮' 27 # stu3.sex='female' 28 # stu3.age=48 29 30 # 改进1: #定义了一个函数,减少重复代码部分 31 # def init(obj,name,sex,age): 32 # obj.name = name 33 # obj.sex = sex 34 # obj.age = age 35 # 36 # init(stu1,'王三炮','male','38') 37 # init(stu2,'王二炮','male','18') 38 # init(stu3,'王大炮','male','48') 39 40 # print(stu1.__dict__) 41 42 43 #改进2: # 44 class BeiDaStudent: #产生了BeiDaStudent的名称空间 45 # 相似的特征 46 school = 'BeiDa' 47 count = 0 48 def __init__(obj,name,sex,age): #对象初始化的标准格式,obj是一个位置参数,可以是任意一个参数名 49 BeiDaStudent.count += 1 50 obj.name = name 51 obj.sex = sex 52 obj.age = age 53 54 #相似的技能 55 def choose_course(self): 56 print('%s is choosing course...'% self.name) 57 # print('==run==>') 58 # 59 # 类调用阶段发生的事: 60 # 1. 会产生一个空对象, 61 # 2. 会触发类的函数__init__的运行,将空对象连同调用用时括号内指定的参数一同传入, 62 # 如:空对象.__init__('王三炮','male',18)===> BeiDaStudent.__init__(空对象,'王三炮','male',18) 63 # 3.__init__()方法不能有return值,只能返回None 64 65 stu1 = BeiDaStudent('王三炮','male','38') 66 #第一步:类的调用时,会产生一个空对象 67 #第二步:会出发类的函数__init__()的运行,并且将(空对象+stu1括号内的参数)一同传入__init__()函数中 68 #第三步:将__init__()函数执行的结果返回给stu1对象 69 stu2 = BeiDaStudent('王二炮','male','18') 70 stu3 = BeiDaStudent('王大炮','female','48') 71 72 # print(stu1.__dict__) 73 # print(stu1.count) 74 # print(stu2.count) 75 # print(stu3.count) 76 77 78 # 类中属性总结 79 # 1.类中定义的变量是类的数据属性,类可以访问,但其实是共享给对象使用的,所有对象直接指向同一个内存地址 80 81 82 # print(BeiDaStudent.choose_course) #类拿到自己内部定义的函数,就叫做函数 83 # print(stu1.choose_course) #对象拿到类中所定义的函数,叫做绑定方法 84 # print(stu2.choose_course) #对象拿到类中所定义的函数,叫做绑定方法 85 # print(stu3.choose_course) #对象拿到类中所定义的函数,叫做绑定方法 86 87 88 # 2. 类中定义的函数是类的函数属性,类可以用,但必须按照函数的参数规则来用(该传几个参数就传几个), 89 # 但其实类中定义的函数是给对象用的,而且是绑定给对象使用的,称之为绑定方法 90 # 绑定方法的特殊之处:绑定给谁就应该由谁来调用,谁来调用就会将谁当做第一个参数自动传入 91 92 # print(stu1.choose_course,stu1) 93 # print(stu2.choose_course,stu2) 94 # print(stu3.choose_course,stu3) 95 # 96 # print(id(stu1.choose_course)) 97 # print(id(stu2.choose_course)) 98 # print(id(stu3.choose_course)) 99 100 # stu1.choose_course() 101 # stu2.choose_course() 102 # stu3.choose_course() 103 104 105 #python3中统一了类和类型的感念 106 print(BeiDaStudent) #class 107 print(list) #class 108 print(dict) #class 109 110 li1 = [1,2,3,] #实质上就是 li1 = list([1,2,3,]) 111 print(li1[0]) 112 print(li1[2]) 113 print(li1[1]) 114 115 li1.append(4) #和list.append(li1,4)效果是一样一样的 116 print(li1.append) #append本质就是list这个类中的函数
四、继承介绍
''' 1、 什么是继承 继承是一种新建类的方式,新建的类称之为 子类或者叫派生类,被继承的类称之为父类/基类/超类 继承描述的是一种遗传的关系,父类的属性可以被子类访问到 2、 为何要继承 为了解决类与类之间代码冗余的问题 3、 如何用继承 在python中继承的特点: 1:在python中一个子类同时可以继承多个父类 2:在python3中,如果一个类没有指明继承的类,则默认继承object类(而python2中则不继承任何类) 新式类:但凡继承了object类的子类,以及该子类的子子类都是新式类 经典类:没有继承了object类的子类,以及该子类的子子类都是经典类 在python3中全都是新式类 在python2中才区分经典类和新式类 派生:如果子类中也有和父类一样的属性,则子类查找属性时,以子类自己的属性为准,并且不会影响父类这个相同的属性。 ''' class Parent1: pass class Parent2: pass class Sub1(Parent1): pass class Sub2(Parent1,Parent2): pass print(Sub1.__bases__) #查看该类继承的类 print(Sub2.__bases__) #查看该类继承的类
五、单继承与多继承的属性查找
# 在单继承背景下,新式类、经典类的属性查找顺序都一样 # 对象本身 -> 对象的类 -> 父类 -> 父父类 -> ... class Foo: def f1(self): print('Foo.f1',self) def f2(self): print('Foo.f2',self) self.f1() class Bar(Foo): def f1(self): print('Bar.f1',self) obj = Bar() obj.f2() print(obj) #obj是一个对象 # 'F00.f2' obj # 'Bar.f1' obj # ==========分割线============== #! /usr/bin/env python # -*- coding: utf-8 -*- # 在多继承背景下,如果一个子类继承了多个分支的父类,多个分支最终没有汇聚一个非Object类上 # 新式类和经典类的属性查找方式都一样: A->B->E->C->F->D->Object # 例1: # class E: # def test(self): # print('E') # pass # # class F: # def test(self): # print('F') # pass # # class D: # def test(self): # print('D') # pass # # class B(E): # def test(self): # print('B') # pass # # class C(F): # def test(self): # print('C') # pass # # class A(B,C,D): # # def test(self): # # print('A') # pass # # obj = A() # obj.test() # 在多继承背景下,如果一个子类继承了多个分支的父类,多个分支最终汇聚一个Object类上,称之为菱形继承 # 在菱形继承的背景下:新式类与经典类的属性查找有所不同 # (c3算法) # 经典类:深度优先查找 A->B->E->G->C->F->D (无法查询c3算法) # 新式类:广度优先查找 A->B->E->C->F->D->G->Object(可以查询) class G: def test(self): print('G') pass class E(G): def test(self): print('E') pass class F(G): def test(self): print('F') pass class D(G): def test(self): print('D') pass class B(E): def test(self): print('B') pass class C(F): def test(self): print('C') pass class A(B,C,D): def test(self): print('A') pass obj = A() obj.test() print(A.mro()) #c3算法中放着 属性查找得一个优先级顺序列表 print(B.mro()) #以哪个类作为起始查找属性的,就会以该类的mro列表作为基准来进行属性查找
六、在子类派生的新方法中重用父类的功能
方式一:指名道姓的访问莫一个类的函数,完全不依赖继承
#方式一:(例1) # class BeiDaPeople: # school = 'BeiDa' # # def __init__(self,name,age,sex): #BeiDaStudent和# BeiDaTeacher共用__init__() # self.name = name # self.age = age # self.sex = sex # # # class BeiDaStudent(BeiDaPeople): # # def choose_course(self): # print('%s is choosing course'%self.name) # # # class BeiDaTeacher(BeiDaPeople): # # def __init__(self,name,age,sex,level): # BeiDaPeople.__init__(self,name,age,sex) # # self.level = level # # def score(self,stu,num): # print('老师[%s]为学生[%s]打了分[%s]'%(self.name,stu.name,num)) # # # stu1 = BeiDaStudent('王大炮','18','male') # print(stu1.__dict__) # # tea1 = BeiDaTeacher('egon','18','male',10) # print(tea1.__dict__)
方式二:super(自己的类名,self) 得到一个特殊的对象,该对象专门用来引用父类的属性,严格依赖继承,完成参照mro列表
#方式二:(例1) # class BeiDaPeople: # school = 'BeiDa' # # def __init__(self,name,age,sex): #BeiDaStudent和BeiDaTeacher共用__init__() # self.name = name # self.age = age # self.sex = sex # # # class BeiDaStudent(BeiDaPeople): # # def choose_course(self): # print('%s is choosing course'%self.name) # # # class BeiDaTeacher(BeiDaPeople): # # def __init__(self,name,age,sex,level): # # BeiDaPeople.__init__(self,name,age,sex) # super(BeiDaTeacher, self).__init__(name,age,sex) #super()得到对象的类的父类,然后再.__init__调用函数,严格按照mro表 # # super().__init__(name,age,sex) #python3中支持此类写法,效果同上面那行代码一致 # self.level = level # # def score(self,stu,num): # print('老师[%s]为学生[%s]打了分[%s]'%(self.name,stu.name,num)) # # # stu1 = BeiDaStudent('王大炮','18','male') # print(stu1.__dict__) # # tea1 = BeiDaTeacher('egon','18','male',10) # print(tea1.__dict__) #super的小练习 #A没有继承B,但是A内super会基于C.mro()继续往后找 class A: def test(self): super().test() #照着 类C的mro列表进行查找,所以他的父类为B class B: def test(self): print('from B') class C(A,B): pass c=C() c.test() #from B #本次属性查找,是由C引发的,所以本次查找中牵涉到的父类,均以C的mro列表为基准 # print(C.mro()) #[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
七、组合
#组合:某一个类的对象具备一个属性,该属性的值是另外一个对象 class BeiDaPeople: school = 'BeiDa' def __init__(self,name,age,sex): #BeiDaStudent和BeiDaTeacher共用__init__() self.name = name self.age = age self.sex = sex class Course: def __init__(self,course,price,period): self.course = course self.price = price self.period = period def tell_info(self): print('<name:%s price:%s period:%s>'%(self.course,self.price,self.period) ) class BeiDaStudent(BeiDaPeople): def choose_course(self): print('%s is choosing course'%self.name) class BeiDaTeacher(BeiDaPeople): def __init__(self,name,age,sex,level): super().__init__(name,age,sex) #python3中支持此类写法,效果同上面那行代码一致 self.level = level def score(self,stu,num): print('老师[%s]为学生[%s]打了分[%s]'%(self.name,stu.name,num)) py_course_obj = Course('python自动化开发','10000元','三个月') linux_course_obj = Course('linux运维','9000','三个月') stu1 = BeiDaStudent('王大炮','18','male') tea1 = BeiDaTeacher('egon','12','male','10') stu1.course = py_course_obj #组合的精髓。。。 stu1.courses = [] stu1.courses.append(py_course_obj) stu1.courses.append(linux_course_obj) print(stu1.__dict__) print(stu1.course.course) print(stu1.course.period) print(stu1.school)
八、多态与鸭子类型
''' 1、什么是多态 同一种事物的多种的形态 2、为何要用多态 多态性:可以在不用考虑一个对象具体的类的前提下而直接调用对象下的方法(参照父类) 只要父类有的,子类一定有 3、如何用多态 使用同一个规范来定义(解耦合) ''' class Animal: def speak(self): pass class People(Animal): def speak(self): pass class Dog(Animal): def speak(self): pass class Cat(Animal): def speak(self): pass class SPEAK(Animal): ''' 统一接口 ''' def cao(self): pass obj1 = People() obj2 = Dog() obj3 = Cat() obj1.speak() obj2.speak() obj3.speak() # print(Animal.__dict__) # def test(animal): # animal # SPEAK(obj1) # SPEAK(obj2) # SPEAK(obj3) ##python崇尚鸭子类型,只要特征一致,那归为同一类 class Disk: def read(self): print('disk read') def write(self): print('disk write') class Menory: def read(self): print('mem read') def write(self): print('mem write') class proocess: def read(self): print('proc read') def write(self): print('proc write')
九、封装
''' 1、什么是封装 装:将属性撞到一个容器里,该容器可以是类,也可以是对象 封:指的是将撞到容器里的对象给隐藏起来,该隐藏是对外,不对内的 2、为何要封装 待补充... 3、如何封装 在属性前 加 __前缀 注意: 1、__开头属性会在类定义阶段检测语法时发生变形,变形规则:_类名__属性名 2、类定义阶段之后新增的__属性并不会发生变形 3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 ''' # class Foo: # __x = 1 #变形成 _Foo__x = 1 # # def __init__(self,name,age): # self.__name = name #self._Foo__name = name # self.__age = age #self._Foo__age = age # # def tell_me(self): # print('hello %s %s'%(self.__name)) #self._Foo__name # # # print(Foo.x) # # obj = Foo('egon','19') # # print(obj.name,obj.age) # # obj.tell_me() # print(Foo.__dict__) #例:类内函数属性 没有私有使用 # class Foo: # def f1(self): # print('Foo.f1') # # def f2(self): # self.f1() # # class Bar(Foo): # def f1(self): # print('Bar.f1') # # # obj = Bar() # obj.f2() #例:类内函数属性 私有使用 # class Foo: # def __f1(self): #_Foo__f1() # print('Foo.f1') # # def f2(self): #_Foo__f2() # self.__f1() # # class Bar(Foo): # def __f1(self): #_Bar__f1() # print('Bar.f1') # # # obj = Bar() # obj.f2()
十、为何要封装
#为何要封装 #1、封装数据属性:将数据属性隐藏起来,从而类的使用者无法直接操作该数据属性 #需要类的设计者在类内部开辟接口,然后让类的使用者通过接口来间接的操作属性 #类的设计者可以在接口之上附加任意逻辑,从而严格控制类的使用者对属性的操作 #2、封装函数属性:隔离复杂度 class People: def __init__(self,name,age): self.__name = name self.__age = age def tell_info(self): print('<%s %s>'%(self.__name,self.__age)) def set_info(self,name,age): if type(name) is not str or type(age) is not int: print('必须输入正确的数据类型') raise TypeError() else: self.__name = name self.__age = age obj = People('lich',19) print(obj.__dict__) obj.set_info('dadaf',12) print(obj.__dict__)