面向对象三大特征:封装、继承、多态
封装:将细节封装起来,只对外暴露调用方法,增加整体的安全性。通过前面所学的私有属性和私有方法即可实现。
继承:使得子类可以继承父类的方法和属性,提高了代码的复用性
多态:同一个方法被调用时会因为对象不同而产生不一样的结果。
一.封装
可以使用私有属性和私有方法对数据进行隐藏封装,使得只有调用方法会对外暴露。
二.继承
继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。 如果一个新类继承自一个设计好的类,就直接具备了已有类的特征,就大大降低了工作 难度。已有的类,我们称为“父类或者基类”,新的类,我们称为“子类或者派生类”。
1.语法格式
Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
class 子类类名(父类 1[,父类 2,...]):
类体
如果在类定义中没有指定父类,则默认父类是 object 类。也就是说,object 是所有类的父 类,里面定义了一些所有类共有的默认实现,比如:__new__()。
定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下: 父类名.__init__(self, 参数列表)
class Person():
def __init__(self,name,age):
self.name=name
self.age=age
def print(self):
print(self.name,"-",self.age,"-",self.attribute)
class Lhx(Person):
def __init__(self,name,age,attribute): #此处要将和父类重复但是需要使用的属性重新书写
self.attribute=attribute
Person.__init__(self,name,age)
lhx=Lhx("林海馨","18","可爱")
lhx.print()
print(dir(lhx))
2.类成员的继承和重写
1. 成员继承:子类继承了父类除构造方法之外的所有成员。
2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”。直接在类对象中写出即可。
class Person():
def __init__(self,name,age):
self.name=name
self.age=age
def say_name(self):
print("我的名字是:",self.name)
def say_age(self):
print(self.name,'-',self.age)
class Lhx(Person):
def __init__(self,score,name,age):
self.score=score
Person.__init__(self,name,age)
def say_score(self):
print(self.score)
def say_name(self):
print("老师好!我叫",self.name)
lhx=Lhx(90,"林海馨",18)
lhx.say_name()
lhx.say_score()
lhx.say_age()
3.查看类的继承层次结构
通过类的方法 mro()或者类的属性__mro__可以输出这个类的继承层次结构。
class A():
pass
class B(A):
pass
class C(B):
pass
print(C.mro())
object 类是所有类的父类,因此所有的类都有 object 类的属性和方法。
class Person():
def __init__(self,name,age):
self.name=name
self.age=age
def say_name(self):
print(self.name)
obj=object()
print(dir(obj))
p1=Person("林海馨",18)
print(dir(p1))
从上面我们可以发现这样几个要点:
1. Person 对象增加了六个属性: __dict__ __module__ __weakref__ age name say_age
2. object 的所有属性,Person 类作为 object 的子类,显然包含了所有的属性。
3. 我们打印 age、name、say_age,发现 say_age 虽然是方法,实际上也是属性。只不过, 这个属性的类型是“method”而已。
4.重写__str__()方法
class Person():
def __init__(self,name,age):
self.name=name
self.age=age
def __str__(self):
return "名字是{0},年龄是{1}".format(self.name,self.age)
p1=Person("林海馨",18)
print(p1)
5.多重继承
Python 支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父 类”的特点。但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。
MRO()方法:MRO(Method Resolution Order):方法解析顺序。 我们可以通过 mro()方法获得 “类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
class A():
def aa(self):
print("aa")
def print(self):
print("1230")
class B():
def bb(self):
print("bb")
def print(self):
print("4567")
class C(A,B):
#class C(B,A):
def cc(self):
print("cc")
c=C()
c.aa()
c.bb()
c.cc()
c.print()
print(C.mro())
7.super()获得父类定义
class A():
def print(self):
print("aa")
class B(A):
def print(self):
print("bb")
super().print()
b=B()
b.print()
在子类中,如果想要获得父类的方法时,我们可以通过 super()来做。 super()代表父类的定义,不是父类对象。
三.多态
关于多态要注意以下 2 点:
1. 多态是方法的多态,属性没有多态。
2. 多态的存在有 2 个必要条件:继承、方法重写。
class Animal():
def shout(self):
print("动物叫了一声!")
class Dog(Animal):
def shout(self):
print("汪汪汪")
class Cat(Animal):
def shout(self):
print("喵~")
class Mice(Animal):
def shout(self):
print("吱吱吱")
def Animalshout(a):
if isinstance(a,Animal): #判断a是不是Animal的子类,isinstance用于判断是否有继承关系
a.shout()
Animalshout(Dog())
Animalshout(Cat())
Animalshout(Mice())
1.特殊方法和运算符重载
class Person():
def __init__(self,name):
self.name=name
def __add__(self, other):
if(isinstance(other,Person)):
print("{0}-{1}".format(self.name,other.name))
else:
print("两个对象格式不同!")
def __mul__(self, other):
if(isinstance(other,int)):
print(self.name*other)
else:
print("不可相乘!")
p1=Person("林海馨")
p2=Person("任国亮")
p2+p1 #此处调用_add_函数
p2*3
Python 对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。这 里我们列出常见的特殊属性:
class A():
pass
class B():
pass
class C(A,B):
def print(self):
print("cc")
c=C()
print(dir(c)) #对象的全部属性
print(c.__dict__) #对象属性的字典展示
print(c.__class__) #对象所属的类
print(C.__base__) #类的基类
print(C.mro()) #打印所有父类 此处必须使用类来调用函数,对象调用会出错
print(A.__subclasses__())#打印所有子类
2.特殊属性
class A():
pass
class B():
pass
class C(A,B):
def print(self):
print("cc")
c=C()
print(dir(c)) #对象的全部属性
print(c.__dict__) #对象属性的字典展示
print(c.__class__) #对象所属的类
print(C.__base__) #类的基类
print(C.mro()) #打印所有父类 此处必须使用类来调用函数,对象调用会出错
print(A.__subclasses__())#打印所有子类
4.对象的浅拷贝和深拷贝
- 变量的赋值操作:只是形成两个变量,实际还是指向同一个对象。
- 浅拷贝 :Python 拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象。
- 深拷贝 : 使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象。源对象和拷贝对象所有的子对象也不同。
import copy
class MobilePhone():
def __init__(self,cpu,screen):
self.cpu=cpu
self.screen=screen
class Cpu():
def calculate(self):
print("计算。。。")
print("CPU:",self)
class Screen():
def show(self):
print("展示。。。")
print("Screen:",self)
c1=Cpu()
s1=Screen()
m1=MobilePhone(c1,s1)
n=m1 #此处应该被赋值的是变量n,n应该写在=前面
print(n)
print(m1)
#测试浅复制
print("测试浅复制","#"*20)
m2=copy.copy(m1)
print(m2)
print(m1)
m2.cpu.calculate()
m1.cpu.calculate() #不同地址对象中的子对象地址相同
#测试深复制
print("测试深复制","#"*20)
m3=copy.deepcopy(m1)
print(m3)
print(m1)
m1.cpu.calculate()
m3.cpu.calculate() #不同地址的对象中的子对象地址不同
5.组合
“is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。“is-a” 关系指的是类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。” has-a”关系指的是这样的关系:手机拥有 CPU。 MobilePhone has a CPU。
import copy
class MobilePhone():
def __init__(self,cpu,screen):
self.cpu=cpu
self.screen=screen
class Cpu():
def calculate(self):
print("计算。。。")
print("CPU:",self) #展现方法的地址
class Screen():
def show(self):
print("展示。。。")
print("Screen:",self)
c1=Cpu()
s1=Screen()
m1=MobilePhone(c1,s1)
m1.cpu.calculate() #此处通过组合就可以调用cpu对象,相当于手机对象简介拥有了CPU类的方法
四.设计模式_工厂模式实现、设计模式_单例模式实现
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进 行统一的管理和控制。
1.设计模式_工厂模式实现
class CarFactory():
def car(self,brand):
if(brand=="奔驰"):
return Benz()
if(brand=="宝马"):
return BMV()
if(brand=="比亚迪"):
return BYD()
else:
print("版本无效,无车!")
class Benz():
pass
class BMV():
pass
class BYD():
pass
factory=CarFactory()
print(factory.car("奔驰"))
print(factory.car("比亚迪"))
2.设计模式_单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一 个访问该实例的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较 多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久 驻留内存中,从而极大的降低开销。
这里我们推荐重写_init_方法
class Mysingleton(): #单例模式一般通过重写_new()_方法来实现
__obj=None
__singleflag=True #定义类属性
def __new__(cls, *args, **kwargs): #定义构造方法
if cls.__obj==None: #cls对象进行调用__obj属性
cls.__obj=object.__new__(cls) #通过object对象调用构造方法创建cls对象,此处__object属性不为空
return cls.__obj
def __init__(self,name):
if Mysingleton.__singleflag:
print("init...")
self.name=name
Mysingleton.__singleflag=False #使得不会再输出print语句,此处对__singleflag重新复制,不需要写双等号,双等号只需要再循环语句中写出
a=Mysingleton("aa")
print(a)
b=Mysingleton("bb")
print(b)
注意:==只用在循环的判断条件中,在别处不可使用==进行赋值
设计模式称之为“模式”,就是一些固定的套路。我们很容易用到其他场景上,比如前面讲 的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现:
class CarFactory():
__obj = None
__singleflag = True # 定义类属性
def car(self,brand):
if(brand=="奔驰"):
return Benz()
if(brand=="宝马"):
return BMV()
if(brand=="比亚迪"):
return BYD()
else:
print("版本无效,无车!")
def __new__(cls, *args, **kwargs):
if cls.__obj == None: # cls对象进行调用__obj属性
cls.__obj = object.__new__(cls) # 通过object对象调用构造方法创建cls对象,此处__object属性不为空
return cls.__obj
def __init__(self):
if CarFactory.__singleflag:
print("init...")
CarFactory.__singleflag = False # 使得不会再输出print语句,此处对__singleflag重新复制,不需要写双等号,双等号只需要再循环语句中写出
class Benz():
pass
class BMV():
pass
class BYD():
pass
factory=CarFactory()
factory1=CarFactory()
print(factory)
print(factory1)
#要注意全篇的缩进,否则不能识别为函数