一 析构函数
构造函数:创建对象的时候第一个被自动调用的函数 __init__ 和构造函数相反,当对象被销毁的时候自动调用的函数,被称为析构函数 __del__ 对象什么时候被销毁:del 或者 程序执行完毕 使用场景:对象即将被销毁的时候需要做的一些清理工作,比如:关闭文件,关闭数据库
代码演示:
import time class Animal(): #构造函数 def __init__(self,name,age): self.name = name self.age = age print("构造函数被执行了") #析构函数 def __del__(self): print("析构函数被执行了") a = Animal("大黄",10) #注意:对象被销毁之后在后面的代码中将无法再访问 del a #print(a.name) #NameError: name 'a' is not defined time.sleep(3) #释放对象的时机:a.当程序执行完毕【程序的最后一行代码执行完毕】 b.del 对象 print("over") def func(): a1 = Animal("",10) func() #作用域 #print(a1) #结论:但凡有对象的创建,则肯定会去调用构造函数和析构函数
二、封装
面向对象语言的三大特征:封装,继承,多态
是什么
如何使用
有什么作用
1.概念
广义的封装:函数和类的使用,本身就是封装的体现
狭义的封装【面向对象中的封装】:一个类的某些属性,在使用的过程中,如果不希望被外界直接访问,就可以将该属性作为私有的【private,私有的:只有当前类持有或者只能被当前类直接访问】,然后暴露给外界一个访问的方法即可【对属性的间接访问】
封装的本质:就是属性私有化的过程
举例;插排【不需要关心属性在类的内部进行了什么样的操作,只需要关心将值传进去,也可以将值获取出来】
封装的好处:提高了数据的安全性,提高了数据的复用性
封装的使用流程:
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("jack",10) #属性:对象.属性 print(p1.name,p1.age) #成员函数:对象.函数名() p1.myPrint() #通过对象直接赋值 p1.name = "tom" print(p1.name) 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("jack",10) #注意1:属性被私有化之后,在类的内部可以直接访问 p2.myPrint() #print(p2.name,p2.__age) #AttributeError: 'Person2' object has no attribute '__age' #属性的动态绑定,age和__age属于两个不同的变量 #p2.age = 20 #print(p2.age) #注意2;此处的操作属于属性的动态绑定,和类中的__age并不是同一个属性 #p2.__age = 30 #print(p2.__age) #注意3:属性被私有化之后,在内存中的存在方式;Python的解释器将__age变成了_Person2__age #通过_Person2__age访问,但是不建议这么做,不同的解释器可能存在解释不同的情况, #print(p2._Person2__age) #不要这样做 print("********")
3.get函数和set函数
get函数和set函数并不是系统内置的函数,只是通过封装的概念自定义的,为了和封装的概念相吻合,命名为getXxx() setXxx()
get函数:获取值 【p1.name】
set函数:赋值 【p1.name = xx】
说明:getXxx() setXxx()的Xxx其实就是被私有化的属性的名称
举例:name被私有化,getName() setName()
语法:
get函数:设置返回值
def getXxx(self):
return 被私有化的成员变量
set函数:设置参数
def setXxx(self,xxx):
私有化属性 = xxx
举例:
def getName(self):
return self.__name
def setName(self,name):
self.__name = name
代码演示:
#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)) #给__age添加get函数和set函数 #get函数:获取私有化的属性的值 #命名方式;getXxx #特点:需要设置返回值,但是没有参数 def getAge(self): return self.__age #set函数:给私有化属性赋值 #命名方式:setXxx #特点:有参数,但是没有返回值 def setAge(self,age): #数据的过滤【在类的内部对属性做操作:逻辑处理】 if age < 0: age = abs(age) self.__age = age p3 = Person3("zhangsan",37) p3.myPrint() #获取值 print(p3.getAge()) #重新赋值 p3.setAge(25) p3.myPrint() print(p3.getAge()) p3.setAge(-15) p3.myPrint() print(p3.getAge()) print("**********")
4.@property装饰器
装饰器的作用:可以给函数动态的添加功能,对于类的成员函数而言,装饰器仍然起作用
Python内置的装饰器@property的作用:
a.作用相当于get函数,获取私有属性的值
b.实际的作用:将一个函数转换为属性使用【p1.getAge()------>p1.age】
c.使用@property之后,会自动生成一个新的装饰器,命名@属性名.setter,作用相当于set函数
注意:@property的出现其实就是为了简化get函数和set函数【功能】
代码演示:
#4.@property装饰器 #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 #@property也可以应用于普通函数,该函数将不再正常调用,只需要访问该函数的函数名即可,相当于获取函数的结果, # 如果有返回值,则获取返回值,如果没有返回值,则返回None @property def show(self): #return "showing" print("fjahdgj") p4 = Person4("lisi",43) p4.myPrint() print(p4.age) #相当于调用了get函数【@property修饰的函数】,将私有化属性的值返回 p4.age = 56 #相当于调用了set函数【@age.setter修饰的函数】,给私有化属性进行赋值 p4.myPrint() print(p4.age) #print(p4.show()) print(p4.show) #注意:@property只能应用在面向对象中 """ @property def func(): print("func~~~~~") func() print(func) """ """ 需求:富二代王思聪开着豪车玛莎拉蒂,很自豪的向他的新女友炫耀起来 分析: 富二代类 特征;姓名 行为:开车,炫耀 汽车类: 特征:品牌,颜色 行为:行驶 女友类 特征:姓名 """
5.属性的不同形式
代码演示:
class Person(): def __init__(self,name,age,height,score): self.__name = name #私有化属性 self._age = age self.__height__ = height self.score = score p = Person("abc",19,12.3,37) print(p.score) #受保护的变量【实际开发中,在类中定义普通变量的时候尽量不要以下划线开头】 #print(p._age) #一个变量的前后各添加两个下划线,一般为系统属性的命名方式,不建议使用,比如:__name__\__init__\__del__ #print(p.__height__) """ 【面试题:解释下面不同变量的作用】 xxx:普通变量 _xxx:受保护的变量,自定义变量尽量不要使用 __xxx;私有变量,只能在当前类中被直接访问 __xxx__;一般用于系统内置变量的命名,自定义变量尽量不要使用 """
6.私有函数
类中的成员函数也可以被私有化
写法:在函数名的前面添加两个下划线【函数名其实就是一个变量名】,此时,称为私有函数
特点:私有函数只能在当前类中被调用
代码演示:
class Site(): def __init__(self,name): self.name = name #公开函数 def func(self): print("普通函数") #注意:在类的内部,进行函数之间的相互调用,需要使用self self.__func() self.check() def check(self): print("check") #私有函数 def __func(self): print("私有函数") s = Site("深圳") s.func() def show1(): pass def show2(): pass show1() show2()
三、继承
1.概念
如果两个或者两个以上的类具有相同的属性和方法,我们可以抽取一个类出来,在抽取出来的类中声明各个类中的公共的部分
被抽取出来的类:父类、超类,基类
两个或者两个以上的类:子类,派生类
他们之间的关系:父类 派生了 子类 或者 子类 继承了 父类
2.单继承
一个类只有一个父类,被称为单继承
object是所有类的父类,如果没有自定义父类,默认都是object,object也被称为所有的类的根类
语法:
父类:
class 父类类名(object):
公共的部分
子类:
class 子类类名(父类类名):
特有的部分
注意:在使用继承时,尽量一个类处于一个模块中
代码演示:
测试文件
from extends01.person import Person from extends01.worker import Worker from extends01.student import Student #4.创建父类的对象 p1 = Person("lisi",10) print(p1.name) #5.创建子类的对象 w1 = Worker("abc",27,"工人") print(w1.name,w1.kind) #说明:在子类的构造函数中调用父类的构造函数,目的是为了使用父类中的成员变量 #6.子类对象访问父类中私有化属性 # 访问私有化的属性,实质上访问的是被装饰器修饰之后的函数 print(p1.age) print(w1.age) #7.子类对象可以直接访问父类中未被私有化的属性和未被私有化的成员函数 p1.eat() w1.eat() #8.父类对象不能访问子类对象中特有的属性和成员函数 #p1.work() w1.work() #9.如果子类中出现和父类重名的函数,则优先调用子类中的函数【就近原则】 #本质是子类中的函数覆盖了父类中的函数【函数的重写】 w1.eat() s = Student("张三",47,"语文") print(s.name,s.age,s.subject) s.eat() s.study()
父类
#1.定义父类 class Person(object): #成员变量 def __init__(self,name,age): print("person的构造函数被执行") self.name = name self.__age = age #成员方法 def eat(self): print("父类~~~eating") @property def age(self): return self.__age @age.setter def age(self,age): self.__age = age
子类1
from extends01.person import Person #2.定义子类 class Worker(Person): def __init__(self,name,age,kind): print("worker的构造函数被执行") # self.name = name # self.age = age self.kind = kind #3.在子类的构造函数中调用父类的构造函数【让子类拥有和父类相同的成员变量】 """ self:自己,当前类的实例 super:父类,使用了父类中的内容 """ #方式一:super(当前类,self),__init__(属性列表) #super(Worker,self).__init__(name,age) #方式二:父类名.__init__(self,属性列表) #Person.__init__(self,name,age) #方式三:super().__init__(属性列表) super().__init__(name,age) def work(self): print("woring") def eat(self): print("work~~~eating")
子类2
from extends01.person import Person class Student(Person): def __init__(self,name,age,subject): self.subject = subject super().__init__(name,age) def study(self): print("studying")
3.多继承
一个子类可以有多个父类
注意:在继承关系中,不能为了使用某个类中的属性或者方法,而刻意的去继承
语法:
class 子类类名(父类1,父类2,父类3.。。。)
子类特有的内容
代码演示:
测试文件
from extends02.mother import Mother from extends02.father import Father from extends02.child import Child #创建子类的对象 c = Child(100,"别墅",39) #访问父类中的成员变量 print(c.money,c.house,c.num) #调用父类中的成员函数 c.play() c.eat() print("******") #如果多个父类中出现重名的函数,当子类对象调用的时候,从左往右一依次查找,左边的父类拥有更高的优先级 c.func()
父类1
class Father(object): def __init__(self,money): self.money = money def play(self): print("playing") def func(self): print("Father~~func")
父类2
class Mother(object): def __init__(self, house): self.house = house def eat(self): print("eating") def func(self): print("Mother~~func")
子类
from extends02.mother import Mother from extends02.father import Father class Child(Mother,Father): def __init__(self,money,house,num): self.num = num #调用父类中的构造函数 #注意:尽量能够明确到底调用是哪个父类中的构造函数 #super().__init__(money) Father.__init__(self,money) Mother.__init__(self,house) def show(self): self.func() def func(self): #调用父类中的普通函数,super().函数名() #self代表是对象,并不是一个类 super().func() #Father.func() #TypeError: func() missing 1 required positional argument: 'self' print("Child~~~~func")
4.继承的特殊使用
代码演示:
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") d = SubClassD() d.show() #继承树和方法解析 #结论:如果类与类之间存在较为复杂的继承关系【单继承,多继承,一个子类也可以是另外一个类的父类】,将不再遵循单层解析的规律 """ 多继承树中,如果在中间层某类有向上一层解析的迹象,则会先把本层右侧的其他类方法解析完,然后从本层最后一个解析的类方法中直接进入上一层并继续解析,也就是在从子类到超类的反向树中按广度优先解析。 在上面的代码中,先从SubClassD类进入SubClassB类,因为在SubClassB类中有向上一层解析的迹象,所以先解析SubClassC,然后由SubClassC进入上一层的SubClassA,再由SubClassA进入上一层的BaseClass """