1.知识点回顾
a.简述面向过程和面向对象的区别
面向过程: 思路:问题是怎样一步一步解决的,然后亲力亲为的去解决问题【执行者】 代码:代码从上往下依次执行 面向对象: 思路:将复杂的问题简单化,找到一个具有特殊功能的个体,然后委托这个个体帮忙完成某件事情【指挥者】 代码:类和对象【核心】
b.简述类和对象之间的区别和联系
类:将多个具有相同特征和行为的对象进行抽取,抽取其中相同的特征和行为,形成一个类,类是一个抽象的存在 对象:将类具体化【在一个类中,具体的存在】 联系:不严谨的理解,包含关系【在代码中,先定义类,然后再通过类创建对象】
c.定义一个类,在类中书写构造函数和成员函数,在类外面创建对象,调用成员函数
class Person(): #构造函数 def __init__(self,name,age): #实例属性 self.name = name self.age = age #成员函数 def show(self): print("show") p = Person("abc",18) p.show()
2.作业讲解
#1.定义一“圆”(Circle)类,圆心为“点”Point类,构造一圆,求圆的周长和面积,并判断某点与圆的关系 """ 圆类: 特征:圆心,半径 行为:周长,面积 点类: 特征:x坐标,y坐标 """ #1.导入模块 from homework.circle import Cirlce from homework.point import Point import math #2.创建圆心【点】的对象 circlePoint = Point(10,20) print(circlePoint.x,circlePoint.y) #3.创建圆的对象 ci = Cirlce(circlePoint,15) print(ci.radius,ci.circlePoint.x,ci.circlePoint.y) #4.让圆执行自己的行为 print(ci.cirlceLength()) print(ci.circleArea()) #5.创建某点的对象 x = int(input("请输入点的x坐标:")) y = int(input("请输入点的y坐标:")) myPoint = Point(x,y) #6.计算某点到圆心之间的距离 #(x1,y1) (x2,y2) 距离:sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) #(myPoint.x,myPoint.y) (ci.circlePoint.x,ci.circlePoint.y) result = (myPoint.x - ci.circlePoint.x) ** 2 + (myPoint.y - ci.circlePoint.y) ** 2 disance = math.sqrt(result) #7.判断半径和距离之间的大小关系 if disance > ci.radius: print("圆外") elif disance < ci.radius: print("圆内") else: print("圆上")
""" 圆类: 特征:圆心,半径 行为:周长,面积 """ class Cirlce(): #构造函数 #circlePoint:圆心,传参的时候必须传 的一个点的对象 #radius:半径 def __init__(self,circlePoint,radius): self.circlePoint = circlePoint self.radius = radius #成员函数 #周长 def cirlceLength(self): return 3.14 * self.radius * 2 #面积 def circleArea(self): return 3.14 * self.radius ** 2
""" 点类: 特征:x坐标,y坐标 """ #文件名的命名:小驼峰,尽量不要使用中文 class Point(): #构造函数 def __init__(self,x,y): self.x = x self.y = y
二、析构函数
构造函数:创建对象的时候第一个被自动调用的函数,__init__ 作用:在其中定义实例属性,给实例属性赋值 析构函数:和构造函数相反,当对象被销毁的时候自动调用的函数,__del__ 作用:当程序执行结束的时候需要做的一些清理工作,比如:关闭文件,关闭数据库等 问题:对象什么时候会被销毁?
import time class Animal(): #构造函数 def __init__(self,name,age): self.name = name self.age = age print("构造函数被执行了") #成员函数 def show(self): print(self.name,self.age) #析构函数 #析构函数执行的时机:对象的生命周期结束【全局:程序执行结束,局部:函数执行完毕】 #生命周期的结束:正常的生老病死,通过del中途夭折 def __del__(self): print("析构函数被执行了~~~") #a是一个对象,同时他也是一个全局变量,生命周期:从代码开始执行到执行结束 #情况一 # a = Animal("大黄",18) # a.show() # print("over") # time.sleep(3) #情况二 # def func(): # a = Animal("大黄", 18) # print("func函数被执行了") # # func() # print("over") #情况三 a = Animal("大黄", 18) #删除变量 del a print("over") #总结:常用构造函数,析构函数不常用,但是,不管怎样,构造函数和析构函数都会被调用
三、封装
面向对象语言三大特征:封装,继承,多态
学习思路:
是什么
如何使用
有什么作用
1.概念【掌握】
广义的封装:函数和类的使用,就是封装的体现
函数:将某段特殊功能提取出来,定义 一个函数
类:将多个具有相同特征和行为的对象提取出来,定义一个类
狭义的封装【面向对象中的封装】:一个类的某些属性,在使用的过程中,如果不希望外界可以直接访问它,就可以将该属性作为私有的【private:私有的,public:公开的】,然后暴露给外界一个访问的方法即可【对属性进行间接访问】
私有的:只有当前类持有或者只能被当前类直接访问
封装的本质:就是属性私有化的过程
举例:插排【不需要关心属性在类的内部做了什么样的操作,只需要关心将值传进去,也可以将值获取出来,也就是说:只需要关心暴露给外界的接口能不能用】
封装的好处:提高了数据的安全性,提高了代码的复用性
封装的使用流程:
a.将属性私有化
b.提供给外界一个访问的函数
c.外界需要通过函数间接的访问属性
2.属性私有化
如果一个属性不想要被外界直接访问,则可以在属性名称的前面添加两个下划线,则该属性被称为私有化的属性【私有成员变量】
私有属性的特点:只能在当前类的内部被直接访问
#1.属性未被私有化 class Person1(): def __init__(self,name,age): self.name = name self.age = age def myprint(self): print("姓名:%s 年龄:%d" % (self.name,self.age)) p1 = Person1("zhangsan",19) p1.myprint() #直接访问:对象.属性名 p1.name = "lisi" p1.myprint() print(p1.name,p1.age) print("**********************") #2.属性被私有化 class Person2(): def __init__(self,name,age): self.__name = name self.age = age def myprint(self): print("姓名:%s 年龄:%d" % (self.__name,self.age)) p2 = Person2("abc",18) p2.myprint() #注意1:下面两种写法,并不是访问定义的实例属性__name,而都属于动态绑定 #p2.name = "yyyy" #p2.__name = "ppppp" #print(p2.age) #注意2:在实例属性名称的前面添加__,则该属性在外界不能被直接访问 #print(p2.name) #AttributeError: 'Person2' object has no attribute 'name' #print(p2.__name) #AttributeError: 'Person2' object has no attribute '__name' #注意3:私有变量在内存中的存储的形式 #属性一旦被私有化之后,在内存中的存在:Python解释器将__name解释成了_Person2__name #存在的问题:虽然可以通过_Person2__name访问该私有属性,但是不建议这么做,因为Python是一门跨平台的语言, # 在不同的平台上,可能存在不同的Python解释器 #print(p2._Person2__name)
3.get函数和set函数
get函数和set函数在Java中是一个特有的语法,但是在Python,只是一个过渡
get函数和set函数并不是系统内置的函数,而是通过封装的概念自定义的,为了和封装的概念相吻合,命名为:getXxx() setXxx()
get函数:获取值【p1.name】
set函数:设置值【p1.name = “xxx”】
说明:getXxx() 和setXxx()中的Xxx其实就是被私有化的属性的名称
举例:在一个类中,私有的属性__name,getName() setName()
语法:
get函数:设置返回值
def getXxx(self):
return 被私有化的属性
set函数:设置参数
def setXxx(self,xxx):
私有化属性 = xxx
#3.get函数和set函数 class Person3(): def __init__(self,name,age): self.__name = name self.__age = age def myprint(self): print("姓名:%s 年龄:%d" % (self.__name,self.__age)) #get函数和set函数 #get函数:获取私有化属性的值 #命名方式:getXxx #特点:设置返回值,但是没有参数 def getName(self): return self.__name #set函数:给私有化属性赋值 #命名方式:setXxx #特点:设置参数,但是没有返回值 def setName(self,name): self.__name = name def getAge(self): return self.__age def setAge(self,age): #数据的过滤【在类的内部对属性做操作:逻辑处理】 if age < 0: age = abs(age) self.__age = age p3 = Person3("abc",18) p3.myprint() print(p3.getName()) #print(p3.name) p3.setName("nnbv") print(p3.getName()) print(p3.getAge()) #18 p3.setAge(-10) print(p3.getAge()) #get函数和set函数------》Python的装饰器【常用】
4.@property装饰器【掌握】
property:属性
装饰器的作用:可以给 函数动态的添加功能,对于类的成员函数而言,装饰器仍然起作用
@property装饰器的作用:
a.作用相当于get函数,获取私有属性的值
b.实际的作用:可以将可以函数转换为属性使用【p1.getAge()----->p1.getAge】
c.使用@property之后,会自动生成一个新的装饰器,命名@私有属性名.setter,相当于set函数,给私有属性进行赋值
注意:@property装饰器的出现简化了get函数和set函数的使用
#4.@property的使用 class Person4(): def __init__(self,name,age): self.__name = name self.__age = age def myprint(self): print("姓名:%s 年龄:%d" % (self.__name,self.__age)) #注意:函数的命名方式:被私有化属性的名称 ,例如:self.__age------>age #相当于get函数:设置返回值,但是没有参数 #说明:函数名的命名并不是固定的,只要是一个合法的标识符即可,但是,为了区分不同的私有属性, # 一套流程中一般是相似或者相同的命名 @property def age(self): return self.__age #注意:装饰器的命名:@【和property修饰的函数名相同】.setter #相当于set函数:设置参数,但是没有返回值 @age.setter def age(self,age): if age < 0: age = abs(age) self.__age = age @property def name(self): return self.__name @name.setter def name(self,name): self.__name = name #补充:@propery除了进行操作私有化属性之外,还可以修改普通的成员函数,将普通的成员函数当做属性使用 #调用函数的语法:对象.函数名 @property def show(self): print("show") return "show" p4 = Person4("abc",18) #调用函数 #print(p4.age()) #添加了@property之后,就可以将一个函数当做属性使用 #注意:下面出现的age并不是私有的属性名,而是定义的函数名 print(p4.age) #相当于get函数,将私有化属性的值返回 p4.age = 100 #相当于set函数,给私有化属性赋值 print(p4.age) print(p4.name) p4.name = "fjakdg" print(p4.name) #print(p4.show()) print(p4.show)
5.属性的不同形式
#属性的不同形式 class Person(): def __init__(self,pid,name,age,height,score): #1.普通变量:小驼峰或者Python官方【字母全部小写,不同的单词使用_分隔】 self.pid = pid self.score_num = score #2.私有变量 self.__name = name #3.受保护的变量,虽然能够在外界直接访问,但是不建议使用,是一种约定 self._age = age #4.__xxx__:一般是系统的变量或者函数名,不建议使用,是一种约定 self.__height__ = height @property def name(self): return self.__name @name.setter def name(self,name): self.__name = name p = Person("1001","zhangsan",18,178.0,87) #1 print(p.pid,p.score_num) #2 print(p.name) p.name = "lisi" #3 print(p._age) #4 print(p.__height__) """ 【面试题:解释下面变量的作用】 xxx:普通变量,可以任意的访问 _xxx:受保护的变量,在实际项目开发中,尽量不要使用 __xxx:私有变量,只能在当前类中被直接访问 __xxx__:一般一用于系统的变量和函数的命名,尽量不要使用,比如__name__,__init__,__del__等 """
6.私有成员方法
函数名相当于是一个变量名,变量可以被私有化,函数也可以被私有化
写法:在函数名的前面添加两个下划线
特点:私有函数只能在当前类中被调用
class Site(): def __init__(self,name): self.name = name #成员函数 #公开函数 def func(self): print("普通函数") #在类的内部,可以在其他普通函数的内部调用私有函数 #类的内部的函数相互之间进行调用,都必须通过self调用 self.__check() self.show() #私有函数 def __check(self): print("check") def show(self): print("show") s = Site("千锋") #直接调用 s.func() #对象不能直接调用私有函数 #s.check() #s.__check()
四、继承
1.概念【掌握】
如果两个或者两个以上的类具有相同的属性或者方法,我们就可以重新抽取一个类出来,在抽取出来的类中声明其他类公共的部分,把这个实现的过程被称为继承
被抽取出来的类:父类【parent】,超类【super】,基类【base】
两个或者两个以上的类:子类,派生类
他们之间的关系:父类 派生了 子类
子类 继承了 父类
2.单继承
extends;继承
概念:一个子类只有一个父类,
注意:默认情况下,所有类的父类都是object,如果在自定义类的时候,没有指明的父类,则继承自object
语法:
父类
class 父类类名(object):
类体【公共的部分】
子类
class 子类类名(父类类名):
类体【子类特有的内容】
from extends01.person import Person from extends01.worker import Worker from extends01.doctor import Doctor from extends01.student import Student #s = Student("jack",19,"语文") #1.创建子类的对象 w = Worker("tom",17,"铁路工人") #注意1:子类对象如果要访问父类中的变量,想办法调用父类的构造函数 print(w.name,w.age,w.kind) print(w.kind) #注意2:子类的对象不能直接访问父类中私有化属性,但是当父类中提供了@property的函数,则可以访问 #3.子类的对象可以调用父类中未被私有化的成员函数 w.eat() #4.如果在子类中出现了和父类重名的成员函数,则子类对象优先调用子类中的函数【就近原则】 #重写:override w.func() #5.父类的对象无法访问子类中特有的变量和成员函数 #在父类中书写各个类公共的部分,在子类中书写的是该类特有的数据 p = Person("abc",17) p.work()
class Person(object): def __init__(self, name, age): self.name = name self.age = age def eat(self): print("eating") def func(self): print("父类中的func")
from extends01.person import Person class Worker(Person): def __init__(self,name,age,kind): # self.name = name # self.age = age #2.调用父类的构造函数 #注意:调用父类中的构造函数,只能在子类的构造函数中调用,原因:创建子类对象的时候就需要将父类中的实例属性继承过来 #在子类的构造函数中调用父类的构造函数 """ self:自己,表示当前对象的引用 super:表示父类的对象的引用 """ #方式一:super(当前类名,self).__init__(属性列表) #super(Worker,self).__init__(name,age) #方式二:父类名.__init__(self,属性列表) #Person.__init__(self,name,age) #方式三:super().__init__(属性列表) super().__init__(name,age) self.kind = kind # def eat(self): # print("eating") def work(self): print("working") def func(self): print("子类中的func~~~~")
特殊情况:
from extends02.student import Student from extends02.graduateStudent import GraduateStudent #创建父类的对象 s1 = Student() s1.name = "jakc" s1.age = 17 print(s1.name,s1.age) # s1.score = 89 # print(s1.score) #创建子类的对象 gs1 = GraduateStudent() gs1.score = 18 print(gs1.score) #结论:如果在父类中作了属性的限制绑定,则只对当前类起效,子类不会起效
class Student(object): __slots__ = ("name","age")
from extends02.student import Student class GraduateStudent(Student): pass
总结:
继承的特点:
a.子类对象可以直接访问父类中未被私有化的属性
b.子类对象可以直接调用父类中未被私有化的成员函数
c.父类对象不能访问子类中的属性和调用子类的中成员函数
优缺点:
优点:
a.可以简化代码,减少冗余
b.提高代码的维护性
c.提高了代码的安全性
d.继承是多态的前提
缺点:
耦合和内聚被用来描述类与类之间的关系,耦合性越低,内聚性越高,说明代码质量比较好
但是,在继承关系中,耦合性相对比较高的【如果修改了父类,则子类也会随着发生改变】
3.多继承
概念:一个子类可以有多个父类,一个子类可以同时获得多个父类中的功能
语法:
class 子类类名(父类1,父类2.。。):
类体
from extends03.child import Child c = Child(8,10000,"打游戏") print(c.faceValue,c.money,c.hobby) c.eat() c.play() c.study() #注意:如果多个父类中出现了重名的函数,则子类对象调用函数的时候, # 查找规则:在父类列表中从前往后依次查找,如果找到第一个符合条件的,则直接执行该函数,停止查找 c.func()
class Mother(object): def __init__(self,faceValue): self.faceValue = faceValue def eat(self): print("eating") def cook(self): print("cooking") def func(self): print("Monther~~~func")
class Father(object): def __init__(self, money): self.money = money def play(self): print("playing") def func(self): print("Father~~~func")
from extends03.father import Father from extends03.mother import Mother class Child(Mother,Father): def __init__(self,faceValue,money,hobby): # self.faceValue= faceValue # self.money = money #调用父类中的构造函数 Father.__init__(self,money) Mother.__init__(self,faceValue) self.hobby = hobby def study(self): print("studying") def func(self): #调用父类中的普通函数 super().func() print("子类")
扩展:继承树
#继承树和方法解析 # 【如果类与类之间存在复杂的继承关系:单继承,多继承,一个子类也可以是另外一个类的父类,则将不再遵循单层解析的原则】 #父类 class BaseClass(object): def show(self): print("BaseClass") #子类 class SubClassA(BaseClass): def show(self): print("enter subclass~~~~A") super().show() print("exit subclass~~~~A") class SubClassB(BaseClass): def show(self): print("enter subclass~~~~B") super().show() print("exit subclass~~~~B") class SubClassC(SubClassA): def show(self): print("enter subclass~~~~C") super().show() print("exit subclass~~~~C") class SubClassD(SubClassB,SubClassC): def show(self): print("enter subclass~~~~D") super().show() print("exit subclass~~~~D") s = SubClassD() s.show() """ enter subclass~~~~D enter subclass~~~~B enter subclass~~~~C enter subclass~~~~A BaseClass exit subclass~~~~A exit subclass~~~~C exit subclass~~~~B exit subclass~~~~D """ """ 多继承树,如果在中间层某类有向上一层解析的迹象,则会把本层平级的其他类方法解析完,返回再接着解析,被称为广度优先解析 D---->B,B有向上解析的迹象,所以解析C———》A---->BaseClass """