面向对象
类和对象
- 类和对象是面向对象编程中重要的概念
- 类就是一个模板,模板里可以包含多个函数,函数里实现一些功能
- 对象则是根据模板创建的实例,通过实例对象可以执行类中的函数
类的组成部分
- 类的名称:类名
- 类的属性:一组数据
- 类的方法:允许对进行操作的方法(行为)
类和对象
- 类是具有一组相同或者相似特征(属性)的行为(方法)的一系列对象的集合
- 对象是实实在在的一个东西,类的实例化、具象化
- 类是对象的抽象化,对象是类的一个实例
创建类:
class 类名:
属性
方法
创建对象:
对象名=类名()
实例方法
- 在类的内部,使用def关键字可以定义一个实例方法,与一般函数定义不同,类方法必须包含参数self(名字表示可以是其他的名字,但这个位置必须被占用)且为第一个参数
- 实例方法是归于类的实例所有
- 实例方法可由实例对象调用,如果要使用类对象调用的话,必须要为self参数显式的传递对象名,语法为:类对象名.实例方法名(实例对象名)
属性
- 在类的内部定义的变量称为类属性
- 在方法内部定义,通过类似于self.变量名定义的变量称为实例属性
- 通过对象添加的属性也是实例属性
- 实例属性和类属性从结果上没有什么区别,在cpu分配内存空间时有区别
__init__方法
- python内置函数,具有特殊含义
- 是一个初始化方法,是用来定义实例属性和初始化数据的,在创建对象的时候调用,不用手动去调用
- 利用传参的极至可以让我们定义功能更加强调和方便
self
- self和对象指向同一个内存地址,可以认为self就是对象的引用
- self只有在类中定义实例方法的时候才有意义,在调用的时候不必传入相应的参数,而是由解释器自动去指向
- 不一定叫self,可以定义成其他名字,只是约定熟成 的定义成self
魔术方法
名字名时"xxx",在特定的操作时会自动被调用,这些方法称为魔术方法。
- __init__方法:初始化一个类,在创建实例对象为其赋值时使用
- __str__方法:在将对象转换成字符串str(对象)测试的时候,打印对象的信息
- __new__方法:创建并返回一个实例对象,调用了一次就会得到一个对象
- __class__方法:获得一致对象的类(对象.class)
- __del__方法:对象在程序运行结束后进行对象的销毁的时候调用这个方法来释放资源。
__new__和__init__函数的区别
- new :类的实例化方法,必须要返回该实例,否则对象就创建不成功,至少有一个参数是cls,代表要实例化的类,此参数在实例化时由python解释器自动提供;
- init :用来做数据属性的初始化工作,也可以认为是实例的构造方法,接收类的实例,使用self对其进行构造
- __new__函数的执行要早于__init__函数
案例:决战紫禁之巅
import time
#第一步 需要先去定义一个类【角色类】
class Role:
def __init__(self,name,hp):
'''构造初始化函数'''
self.name=name
self.hp=hp
def tong(self,enemy):
'param enemy:敌人'
enemy.hp-=10
print('【%s】捅了【%s】一刀'%(self.name,enemy.name))
pass
def kanren(self,enemy):
enemy.hp-=15
print('【%s】砍了【%s】一刀' % (self.name, enemy.name))
pass
def chiyao(self):
self.hp+=10
print('【%s】吃了一颗补血药,增加10滴血' % (self.name))
pass
def __str__(self):
return '%s还剩%s的血量'%(self.name,self.hp)
#创建两个实例化对象【西门吹雪和叶孤城】
xmcx=Role('西门吹雪',100)
ygc=Role('叶孤城',100)
while True:
if xmcx.hp<=0 or ygc.hp<=0:
break
xmcx.tong(ygc)
print(xmcx)
print(ygc)
time.sleep(1)#休眠一秒钟
print("对战结束")
析构函数
1.析构方法
当一个对象被删除或者被销毁时,python解释器会默认调用一个方法,这个方法为__del__()方法,也称为析构方法
- 当整个程序脚本执行完毕后会自动调用__del__方法
- 当对象被手动销毁(即 del 对象)时也会自动调用__del__方法
- 析构函数一般用于资源回收,利用__del__方法销毁对象回收内存等资源
2.单继承
在python中展现面向对象的三大特征:
封装、继承、多态
- 封装:指的是把内容封装到某个地方,便于后面的使用,对于封装来说,其实就是使用初始化构造方法__init__方法将内容封装到对象中,然后通过对象直接或者self来获取被封装的内容
- 继承:子可以继承父的内容【属性和行为】(爸爸有的儿子都有,儿子有的爸爸不一定有)
单继承
将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法,这样可以极大的提高效率,减少代码的重复编写,精简代码的层即结构,便于拓展
语法:
class 子类名(父类名):
class Animal:#父类
def eat(self):
print("吃饭啦")
pass
def drink(self):
pass
class Dog(Animal): #子类,继承Animal父类
def wwj(self):
print('小狗汪汪叫')
pass
class Cat(Animal):
def mmj(self):
print("小猫喵喵叫")
pass
dog=Dog()
dog.eat()#具备了吃的行为,是继承了父类的行为
3.多继承
一个子类继承多个父类
语法:
class 子类名(父类1,父类2,……):
class Shenxian:
def fly(self):
print("神仙都会飞")
class Monkey:
def chitao(self):
print("猴子喜欢吃桃")
class Sunwukong(Shenxian,Monkey):
pass
swk=Sunwukong()
swk.fly()
swk.chitao()
问题:当多个父类当中存在相同方法的时候,应该去调用哪一个呢?
class D(object):
def eat(self):
print('D.eat')
class C(D):
def eat(self):
print('C.eat')
class B(D):
pass
class A(B,C):
pass
a=A()
a.eat()
print(A.__mro__)#显示类的依次继承关系
#输出C.eat
#查找顺序,平级查找A->B->C->D->没有找到则报错
4.重写父类方法
在子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中的同名的方法。
5.多态
定义时的类型和运行时的类型不一样,顾名思义就是多种状态、形态,就是同一种行为对于不同的子类对象有不同的行为表现
要想实现多态,必须有两个前提:
- 继承:多态必须发生在父类和子类之间
- 重写:子类需要重写父类的方法
多态有什么用?
- 可以增加程序的灵活性
- 增加程序的扩展性
6.类属性和实例属性
- 类属性:类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象都可以访问
- 实例属性:实例对象所拥有的属性,只能通过实例对象访问
class Student:
name='李明'#类属性
def __init__(self,age):
self.age=age#实例属性
xm=Student(18)
print(xm.name,xm.age)#通过实例对象xm进行访问
print(Student.name)#通过类对象Student进行访问
print(Student.age)#报错,不可以!
7.类方法和静态方法
- 类方法:类对象所拥有的方法,需要用装饰器@classmethod来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数,类方法可以通过类对象、实例对象调用。
- 静态方法:类对象所拥有的方法,需要用装饰器@staticmethod来标识静态方法,静态方法不需要任何参数,可以带参数!
类方法
class People:
country='china'
@classmethod#装饰器,此方法为类方法
def get_country(cls):
return cls.country #访问类属性
pass
@classmethod
def change_country(cls,data):#修改类属性
cls.country=data
print(People.get_country())#通过类对象直接引用
xm=People()
print(xm.get_country())#通过实例对象引用
People.change_country('英国')
print(People.get_country())
静态方法
一般情况下,不会通过实例对象去访问静态方法
为什么要使用静态方法?
由于静态方法主要用来存放逻辑性的代码,本身和类以及实例对象美欧交互,也就是说,在静态方法中不会涉及到类中方法和属性的操作。所以一般不需要和实例对象进行交互,使数据资源能够得到有效的充分的利用
import time
class TimeTest:
def __init__(self,hour,min,second):
self.hour=hour
self.min=min
self.second=second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S",time.localtime())
pass
print(TimeTest.showTime())#直接通过类对象调用
总结:
从方法定义形式可以看来:
- 类方法的第一个参数是类对象cls,进而去引用类对象的属性和方法,必须用装饰器@classmethod方法来修饰
- 实例方法的第一个参数必须是self,通过self可以应用类属性或者实例属性,若存在相同名称的实例属性和类属性的话,实例属性的优先级最高
- 静态方法不需要定义额外的参数,若是要引用属性,可以通过类对象或者实例对象去应用即可。必须用装饰器@staticmethod方法来修饰。
8.私有化属性和方法
为了更好的保存属性安全,即不能随意修改,将属性定义为私有属性,添加一个可调用的方法去访问。
语法:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。
访问私有属性的语法:
对象名.__类名_私有属性名
python中不存在严格意义上的私有成员
使用私有属性的场景:
- 不想让类的外部进行直接调用
- 不想让属性的值随意的改变
- 不想让子类去继承这个属性
私有化属性小结:
- 私有化的属性不能在外部直接访问,可以在类的内部随意的使用
- 子类不能继承父类的私有化属性,只能继承父类公共的属性和行为
私有化方法
和私有化属性一样以‘__'开头
9.property属性
实现property属性的两种方式:
①
class Person:
def __init__(self):
self.__age=18
def get_age(self):#得到私有属性
return self.__age
def set_age(self,age):#修改私有属性
if age<0:
print("年龄不能小于0")
else:
self.__age=age
#定义一个类属性,实现通过直接访问属性的形式去访问私有属性
age=property(get_age,set_age)
xm=Person()
print(xm.age)
xm.age=25#修改
print(xm.age)
②装饰器
class Person:
def __init__(self):
self.__age=18
@property #用装饰器修饰 添加属性标志,提供一个getter方法
def age(self):
return self.__age
@age.setter#提供一个setter方法
def age(self,params):
if params<0:
print("年龄不能小于0")
else:
self.__age=params
xm=Person()
xm.age=10
print(xm.age)
10.__new__方法
创建并返回一个实例对象,如果__new__只调用了一次,就会得到一个对象。继承自object的新式类才有new这一魔术方法
注意事项:
- __new__是在一个对象实例化的时候所调用的第一个方法
- __new__至少必须有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供,其他的参数是用来直接传递给__init__方法
- __new__决定是否要使用该__init__方法,因为__new__可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果__new__没有返回实例对象,则__init__不会被调用
- 在__new__方法中,不能调用自己的__new__方法,即:return cls.new(cls),否则会报错(RecursionError:maximum recursion depth exceeded:超过最大递归深度)
使用方式
class A:
def __init__(self):
print("__init__执行了")
def __new__(cls, *args, **kwargs):
print("__new__执行了")
return object.__new__(cls)
a=A()
结果:
__new__执行了
__init__执行了
实例化的过程会自动调用__new__方法去创建实例,在新式类中__new__方法才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法__init__进行丰满操作。
比喻建房子,__new__方法负责开发地皮、打地基、并将原料存放在工地,而__init__方法负责从工地取材料建造出地皮开发图纸规定的大楼
__new__方法实现单例模式
单例模式是常见设计模式的一种,就是说整个系统中只有一个实例,不管创建多少次对象,类返回的对象都是最初创建的,不会再新建其他对象。
class DataBaseClass:
def __new__(cls, *args, **kwargs):
if not hasattr(cls,'_instance'):#如果不存在就开始创建
cls._instance=object.__new__(cls)
return cls._instance
class DB(DataBaseClass):
pass
db1=DB()
print(id(db1))
db2=DB()
print(id(db2))
11.错误与异常处理
有时候代码写错了,执行程序的时候,执行到错误代码额时候,程序直接终止报错,这是因为python检测到一个错误时,解释器就无法继续执行了,出现了错误的提示,这就是“异常”。
语法格式:
try:
可能出现错误的代码块
except:
出现之后执行的代码块
else:
没有出错的代码块
finally:
不管有没有出错都执行的代码块
try:
print(b)#捕获逻辑的代码
except NameError as msg:
print(msg)#捕获到错误,这里才会执行
print("初次接触异常")
try:
li = [1, 2, 3]
print(li[10])
except IndexError as msg:
print(msg)
print("初次接触异常")
总结:
- except在捕获异常的时候,要根据具体的错误类型来捕获
- 用一个块(try)可以捕获多个不同类型的异常
异常 ------------------------------>错误
Exception可以捕获所有异常,当对出现的问题或者错误不确定的情况下使用。
try……except……else……
try:
print('aa')
except Exception as msg:
print(msg)
else:
print("try中没有错误才输出")
结果:
aa
try中没有错误才输出
自定义异常类
都要直接或间接的继承Error或者Exception类
class ToolongMyException(Exception):
def __init__(self,leng):
self.leng=leng
def __str__(self):
return '您输入的数据长度是'+str(self.leng)+'超过长度'
def name_Test():
name=input("请输入")
try:
if len(name)>5:
raise ToolongMyException(len(name))
else:
print(name)
except ToolongMyException as msg:
print(msg)
finally:
print("执行完毕")
name_Test()
12.动态的添加属性和方法
动态语言:运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化,如php、javaScript、python都是动态语言,C,C#,java是静态语言。
动态添加属性:
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '%s的年龄是%s'%(self.name,self.age)
xm=Student('小明',18)
xm.sex='男'#给实例对象动态添加属性
print(xm.sex)
print(xm)
Student.school='北京大学'#给类对象动态添加属性
print(xm.school)
动态绑定实例方法
语法:实例对象.自定义方法名=types.MethodType(绑定的方法名,实例对象)
import types #添加方法的库
def dymisMethod(self):
print('%s的性别是%s,毕业于%s'%(self.name,self.sex,Student.school))
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return '%s的年龄是%s'%(self.name,self.age)
xm=Student('小明',18)
xm.sex='男'#给实例对象动态添加实例属性
xm.printInfo=types.MethodType(dymisMethod,xm)#给实例对象xm动态绑定实例方法
Student.school='北京大学'#给类对象动态添加类属性
print("-------动态添加实例方法---------")
xm.printInfo()
动态绑定类方法
语法:类名.自定义方法名=绑定的方法名
@classmethod
def classTest(cls):
print("这是一个类方法")
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
Student.testMethod=classTest
Student.testMethod()
动态绑定静态方法
和绑定类方法一样!
@staticmethod
def staticTest():
print("这是一个静态方法")
class Student:
def __init__(self,name,age):
self.name=name
self.age=age
Student.staticMethod=staticTest
Student.staticMethod()
13.__slots__属性
在定义class的时候定义__slots__变量,用于限制该class实例能添加的属性。
只有在__slots__变量中的属性才能被添加,可以防止其他人在调用类的时候胡乱添加属性或方法,__slots__属性只在当前类中有效,不会被子类继承,可以节约内存空间。
class Student:
__slots__ = ('name','age')#限制只能添加这两个属性
def __str__(self):
return '%s,%s'%(self.name,self.age)
pass
xw=Student()
xw.name='小王'
xw.age=20
# xw.score=96#添加失败!
#print(xw.__dict__)#所有可以用的属性都在这里存储,不足的地方就是占用的内存空间大
#定义了__slots__属性,实例中就不存在__dict__这个属性,可以节约内存资源
print(xw)