简介
面向对象三大特征介绍
Python 是面向对象的语言,也支持面向对象编程的三大特性:继承、封装(隐藏)、多态。
1、封装(隐藏)
隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外
暴露“相关调用方法”。
通过前面学习的“私有属性、私有方法”的方式,实现“封装”。Python 追求简洁的 语法,
没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。
2、继承
继承可以让子类具有父类的特性,提高了代码的重用性。
从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。
3、多态
多态是指同一个方法调用,由于对象不同会产生不同的行为。生活中这样的例子比比
皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏。
一、继承
继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。 如果一个新类继承自
一个设计好的类,就直接具备了已有类的特征,就大大降低了工作 难度。已有的类,我们称为
“父类或者基类”,新的类,我们称为“子类或者派生类”。
1、语法格式
Python 支持多重继承,一个子类可以继承多个父类。继承的语法格式如下:
class 子类类名(父类 1[,父类 2,...]):
类体
如果在类定义中没有指定父类,则默认父类是 object 类。也就是说,object 是所有类的父 类,
里面定义了一些所有类共有的默认实现,比如:__new__()。
注意:
定义子类时,必须在其构造函数中调用父类的构造函数。调用格式如下:
父类名.__init__(self, 参数列表)
示例代码:
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def get_info(self):
print('学生的信息:{0}的年龄是{1}岁'.format(self.name, self.__age))
class Student(Person):
def __init__(self, name, age, score):
self.score = score
# 构造函数中包含调用父类构造函数。(根据需要,不是必须)
# 子类并不会自动调用父类的__init__(),我们必须显式的调用它。
Person.__init__(self, name, age)
s1 = Student('Jack', 14, 88)
s1.get_info()
print('s1对象内容:'dir(s1))
===================运行结果=========================
学生的信息:Jack的年龄是14岁
s1对象内容:['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'get_info', 'name', 'score']
===================运行结果=========================
# 我们可以看到子类‘Student’中拥有父类‘Person’的实例属性、方法等。
2、类成员的继承和重写
1. 成员继承:子类继承了父类除构造方法之外的所有成员。
2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写”
示例代码:
class Person:
def __init__(self, name, age, address):
self.name = name
self.age = age
self.address = address
def get_info(self):
print('获得信息1:{0}的年龄是{1}岁!'.format(self.name, self.age))
def country_info(self):
print('国籍信息1:{0}来自{1}'.format(self.name, self.address))
class Student(Person):
def __init__(self, name, age, address, score):
self.score = score
Person.__init__(self, name, age, address) # 构造函数中包含调用父类构造函数
def class_info(self):
print('{0}是大一XX班的学生'.format(self.name))
def get_info(self): # 重写父类的方法
print('获得信息2:{0}学生的成绩是{1}分!'.format(self.name, self.score))
def country_info(self): # 重写父类的方法
print('国籍信息2:{0}是个留学生,来自{1}'.format(self.name, self.address))
s1 = Student('Jack', 20, '英国', 77)
s1.class_info()
s1.get_info()
s1.country_info()
# 查看类的继承层次结构:通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构。
print('Student类的继承层次结构:', Student.mro())
print('s1对象属性:', dir(s1)) # dir()查看对象属性
===================运行结果=========================
Jack是大一XX班的学生
获得信息2:Jack学生的成绩是77分!
国籍信息2:Jack是个留学生,来自英国
Student类的继承层次结构: [<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]
s1对象属性:['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'address', 'age', 'class_info', 'country_info',
'get_info', 'name', 'score']
===================运行结果=========================
# 注:object类是所有类的父类,因此所有的类都有object类的属性和方法。
3、重写__str__()方法
object 有一个__str__()方法,用于返回一个对于“对象的描述”,对应于内置函数 str() 经常用于
print()方法,帮助我们查看对象的信息。__str__()可以重写。
示例代码:
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
# 将对象转化成一个字符串,一般用于 print 方法
return '{0}学生的年龄是{1}岁!'.format(self.name, self.age)
s1 = Student('Leo', 14)
print(s1)
===================运行结果=========================
Leo学生的年龄是14岁!
===================运行结果=========================
二、多重继承
Python 支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父 类”的特点。
但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用。
示例代码:
# 多重继承
class Animal:
def __init__(self, name):
self.name = name
def run(self):
print('{0}跑的快!'.format(self.name))
def eat(self):
print('{0}吃东西特别快!'.format(self.name))
class People:
def __init__(self, name):
self.name = name
def learn(self):
print('{0}学习算术!'.format(self.name))
def eat(self):
print('{0}吃东西比较慢!'.format(self.name))
class Dog1(Animal, People):
def __init__(self, name):
Animal.__init__(self,name)
People.__init__(self,name)
# def eat(self):
# print('{0}喜欢抓兔子吃!'.format(self.name))
class Dog2(People, Animal):
def __init__(self, name):
Animal.__init__(self,name)
People.__init__(self,name)
# def eat(self):
# print('{0}喜欢抓兔子吃!'.format(self.name))
dog1 = Dog1('Dog1')
dog1.run()
dog1.learn()
dog1.eat()
print('--------------华丽的分割线-----------------')
dog2 = Dog2('Dog2')
dog2.run()
dog2.learn()
dog2.eat()
# MRO()
# Python 支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将
# “从左向右”按顺序搜索。 MRO(Method Resolution Order):方法解析顺序。 我们可以
# 通过 mro()方法获得 “类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。
# 如果子类中也有重名的方法,首先会执行子类中的方法(此时的方法是方法的重写)。
print('Dog1 MRO:', Dog1.mro())
print('Dog2 MRO:', Dog2.mro())
===================运行结果=========================
Dog1跑的快!
Dog1学习算术!
Dog1吃东西特别快!
--------------华丽的分割线-----------------
Dog2跑的快!
Dog2学习算术!
Dog2吃东西比较慢!
Dog1 MRO: [<class '__main__.Dog1'>, <class '__main__.Animal'>, <class '__main__.People'>, <class 'object'>]
Dog2 MRO: [<class '__main__.Dog2'>, <class '__main__.People'>, <class '__main__.Animal'>, <class 'object'>]
===================运行结果=========================
1、super()获得父类定义
super()代表父类的定义,不是父类对象。
示例代码:
# super 测试
class Animal:
def __init__(self, name):
self.name = name
def run(self):
print('{0}跑的快!'.format(self.name))
class Dog(Animal):
def __init__(self, name):
Animal.__init__(self, name)
def run(self):
super().run() # 通过super调用父类的方法run()
dog = Dog('二哈')
dog.run()
===================运行结果=========================
二哈跑的快!
===================运行结果=========================
三、多态
多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。
关于多态要注意以下 2 点:
1. 多态是方法的多态,属性没有多态。
2. 多态的存在有 2 个必要条件:继承、方法重写。
示例代码:
# 多态测试
class Animal:
def shout(self):
print('动物在黑夜中一直叫!')
class Dog(Animal):
def shout(self):
print('小狗汪汪叫!')
class Cat(Animal):
def shout(self):
print('小猫喵喵叫!')
def animal_shout(animal):
if isinstance(animal, Animal): # 传入的对象不同,shout方法对应的实际行为也不同。
animal.shout()
dog = Dog()
cat = Cat()
animal_shout(dog)
animal_shout(cat)
===================运行结果=========================
小狗汪汪叫!
小猫喵喵叫!
===================运行结果=========================
四、特殊方法和运算符重载
常见的特殊方法统计如下:
每个运算符实际上都对应了相应的方法,统计如下:
五、特殊属性
Python 对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。
这里我们列出常见的特殊属性:
六、对象的浅拷贝和深拷贝
1、变量的赋值操作
只是形成两个变量,实际还是指向同一个对象。
2、浅拷贝
Python拷贝一般都是浅拷贝。拷贝时,对象包含的子对象内容不拷贝。因此,源对象和拷贝对象会引用同一个子对象。
3、深拷贝
使用 copy 模块的deepcopy函数,递归拷贝对象中包含的子对象。源对象和拷贝对象 所有的子对象也不同。
七、组合
“is-a”关系,我们可以使用“继承”。从而实现子类拥有的父类的方法和属性。“is-a” 关系指的是
类似这样的关系:狗是动物,dog is animal。狗类就应该继承动物类。
“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。” has-a”关
系指的是这样的关系:手机拥有 CPU。 MobilePhone has a CPU。
示例代码:
# 测试组合
class MobilePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
class CPU:
def calculate(self):
print('CPU具有运算功能。')
class Screen:
def show(self):
print('Screen可以显示一个好看的画面。')
cpu = CPU()
screen = Screen()
phone = MobilePhone(cpu, screen)
phone.cpu.calculate()
phone.screen.show()
===================运行结果=========================
CPU具有运算功能。
Screen可以显示一个好看的画面。
===================运行结果=========================
八、设计模式_工厂模式实现
设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,设计模式有很多种,
比较流行的是:GOF(Goup Of Four)23 种设计模式。
对于初学者,我们学习两个最常用的模式:工厂模式和单例模式。
工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理
和控制。
示例代码:
# 工厂模式
class Carfactory:
def create_car(self, brand):
if brand == 'BMW':
return BMW()
elif brand == 'BYD':
return BYD()
elif brand == 'BenC':
return BenC()
else:
print('未知品牌的汽车!!!')
class BMW:
def run(self):
print('BMW车跑的快!')
class BYD:
def run(self):
print('BYD车跑的慢!')
class BenC:
def run(self):
print('BenC车跑的比较快!')
factory = Carfactory()
car1 = factory.create_car('BMW')
car1.run()
car2 = factory.create_car('BYD')
car2.run()
car3 = factory.create_car('QQ')
print('car1(BMW) obj:{0}'.format(car1))
print('car2(BYD) obj:{0}'.format(car2))
===================运行结果=========================
BMW车跑的快!
BYD车跑的慢!
未知品牌的汽车!!!
car1(BMW) obj:<__main__.BMW object at 0x0000022F7CA2FB38>
car2(BYD) obj:<__main__.BYD object at 0x0000022F7CA2FC88>
===================运行结果=========================
九、设计模式_单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一 个访问该实例
的全局访问点。
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,
如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大
的降低开销。
单例模式有多种实现的方式,我们这里推荐重写__new__()的方法。
示例代码:
# 测试单例模式
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self, name):
if MySingleton.__init_flag:
print('init......')
self.name = name
MySingleton.__init_flag = False
a = MySingleton('aa')
print('a:', a)
b = MySingleton('bb')
print('b:', b)
===================运行结果=========================
init......
a: <__main__.MySingleton object at 0x000001D20F662C50>
b: <__main__.MySingleton object at 0x000001D20F662C50>
===================运行结果=========================
# 此时我们会发现a、b同属于一个对象,而且初始化只执行了一次。
上述联系的汽车工厂类(CarFactory)实现单利模式的示例代码:
# 工厂类(CarFactory)实现单利模式
class CarFactory:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if CarFactory.__init_flag:
print('CarFactory init ......')
CarFactory.__init_flag = False
def creat_car(self, brand):
if brand == 'BMW':
return BMW()
elif brand == 'BYD':
return BYD()
elif brand == 'BenC':
return BenC()
else:
print('未知品牌的汽车!!!')
class BMW:
def run(self):
print('BMW车跑的快!')
class BYD:
def run(self):
print('BYD车跑的慢!')
class BenC:
def run(self):
print('BenC车跑的比较快!')
factory1 = CarFactory()
car1 = factory1.creat_car('BMW')
car1.run()
car2 = factory1.creat_car('BYD')
car2.run()
factory2 = CarFactory()
car3 = factory2.creat_car('BenC')
car3.run()
car4 = factory2.creat_car('QQ')
===================运行结果=========================
CarFactory init ......
BMW车跑的快!
BYD车跑的慢!
BenC车跑的比较快!
未知品牌的汽车!!!
===================运行结果=========================
对以前的知识回顾,加深基础知识!
学习来自:北京尚学堂高琪老师 Python 400集