Grundlagen-14. Objektorientiert Ⅲ
一、多态
- 多态是面向对象的三大特性之一。简而言之就是多种形态。
- 一个对象可以以不同形态去呈现。
- 面向对象三大特性:
- 封装:确保对象中的数据的安全。(_name)
- 继承:保证了对象的扩展性。
- 多态:保证了程序的灵活性。
- pytho中多态的特点
- 1、只关心对象的实例方法是否同名,不关心对象所属的类型。
- 2、对象所属的类之间,继承关系可有可无。
- 3、多态的好处可以增加代码外部调用灵活度,让代码更加通用,兼容性比较强。
- 4、多态是调用方法的技巧,不会影响到类的内部设计。
代码演示实例:
class Duck(object):
def fly(self):
print('鸭子飞起来了!')
class Swan(object):
def fly(self):
print('天鹅在空中飞翔!')
class Plane(object):
def fly(self):
print('飞机在空中滑翔!')
# 创建一个函数来调用方法对象
def fly(obj):
obj.fly()
# 通过函数传递参数的方式将对象传入函数
duck = Duck()
fly(duck)
swan = Swan()
fly(swan)
plane = Plane()
fly(plane)
打印输出结果:
/Volumes/苹果微软公共盘/PycharmProjects/venv/bin/python /Volumes/苹果微软公共盘/PycharmProjects/基础班第11期/day-14/上课代码练习/01-多态.py
鸭子飞起来了!
天鹅在空中飞翔!
飞机在空中滑翔!
Process finished with exit code 0
注:一个对象fly的多种表现形式。
二、方法和属性
1、属性
- 类属性:直接在类中定义的属性是类属性。
- 类属性可以通过类或类的实例访问到。但是类属性只能通过类对象来修改,无法通过实例对象来修改。
class A(object):
# 类属性 直接定义在类中的属性(变量)
# 类属性可以通过实例对象和类对象来访问
# 类属性只能通过类对象来修改,不能通过实例对象来修改
count = 0
def __init__(self):
self.name = '刘亦菲'
# 实例方法
# 在类中定义 以self 为第一个参数的方法,都是实例方法
def run(self):
print('小偷快跑')
# 类方法
@classmethod
def test(cls):
print('测试方法')
# 静态方法
# 不需要指定任何的默认参数,静态方法可以被类对象和实例对象调用(静态方法与类本身无关,就是一个功能函数)
@staticmethod
def static():
print('这是静态方法')
a = A()
# 实例属性 只能由实例对象来访问
a.count = 10 # 相当于重新在实例对象中添加一个新的属性
print(a.count)
print(A.count)
打印输出结果:
E:\python\python.exe D:/PycharmProjects/基础班第11期/day-14/上课代码/属性和方法.py
10
0
Process finished with exit code 0
总结:类属性可以通过类对象来修改(添加),但是不会对类属性本身造成更改。如果通过实例对象来修改,就会报错。报错原因是实例属性并没有该属性名。
2、实例属性
- 实例属性:通过实例对象添加的属性属于实例属性。
- 实例属性只能通过实例对象来修改访问,类对象无法访问修改。
3、方法
- 在类中定义,以self为第一个参数的方法都是实例方法
- 实例方法在调用时,Python会将调用对象以self传入
- 实例方法可以通过实例和类去调用
- 当通过实例调用时,会自动将当前调用对象作为self传入
- 当通过类调用时,不会自动传递self,我们必须手动传递self
- 类方法:在类的内容以@classmethod 来修饰的方法是类方法
- 类方法第一个参数是cls 也会自动被传递。cls就是当前的类对象
- 类方法和实例方法的区别,实例方法的第一个参数是self,类方法的第一个参数是cls
- 类方法可以通过类去调用,也可以通过实例调用
4、静态方法
- 在类中用@staticmethod来修饰的方法属于静态方法
- 静态方法不需要指定任何的默认参数,静态方法可以通过类和实例调用
- 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数
- 静态方法一般都是些工具方法,和当前类无关
class A(object):
# 类属性 直接定义在类中的属性(变量)
# 类属性可以通过实例对象和类对象来访问
# 类属性只能通过类对象来修改,不能通过实例对象来修改
count = 0
def __init__(self):
self.name = '刘亦菲'
# 实例方法
# 在类中定义 以self 为第一个参数的方法,都是实例方法
def run(self):
print('小偷快跑')
# 类方法 (classmethod来修饰类方法)
@classmethod
def test(cls):
print('测试方法')
# 静态方法
# 不需要指定任何的默认参数,静态方法可以被类对象和实例对象调用(静态方法与类本身无关,就是一个功能函数)
@staticmethod
def static():
print('这是静态方法')
a = A()
# 实例属性 只能由实例对象来访问
a.count = 10 # 相当于重新在实例对象中添加一个新的属性
print(a.count)
print(A.count)
# print(A.count)
# print(a.name)
# print(A.name)
# 实例方法的调用
a.run()
A.run(a)
# 实例可以访问类属性和类方法
# 实例属性只能由实例来访问,实例方法实例对象可以直接访问,类对象访问需要添加实例对象作为参数
A.test()
a.test()
A.static()
a.static()
打印输出结果:
E:\python\python.exe D:/PycharmProjects/基础班第11期/day-14/上课代码/属性和方法.py
10
0
小偷快跑
小偷快跑
测试方法
测试方法
这是静态方法
这是静态方法
Process finished with exit code 0
自我总结:
-
1、实例方法
- 定义:第一个参数必须是实例对象,该参数名一般约定为“self”,通过它来传递实例的属性和方法(也可以传类的属性和方法)。
- 调用:只能由实例对象调用。
-
2、类方法
- 定义:使用装饰器@classmethod。第一个参数必须是当前类对象,该参数名一般约定为“cls”,通过它来传递类的属性和方法(不能传实例的属性和方法)。
- 调用:实例对象和类对象都可以调用。
- 总结:类方法是将类本身作为对象进行操作的方法。假设有个方法,且这个方法在逻辑上采用类本身作为对象来调用更合理,那么这个方法就可以定义为类方法。另外,如果需要继承,也可以定义为类方法。
- 类方法的演示:
'''
如下场景:
假设我有一个学生类和一个班级类,想要实现的功能为:
执行班级人数增加的操作、获得班级的总人数;
学生类继承自班级类,每实例化一个学生,班级人数都能增加;
最后,我想定义一些学生,获得班级中的总人数。
'''
# 推导:这个问题用类方法做比较合适,为什么?因为我实例化的是学生,但是如果我从学生这一个实例中获得班级总人数,在逻辑上显然是不合理的。同时,如果想要获得班级总人数,如果生成一个班级的实例也是没有必要的。
class ClassTest(object):
__num = 0
@classmethod
def addNum(cls):
cls.__num += 1
@classmethod
def getNum(cls):
return cls.__num
# 这里我用到魔术函数__new__,主要是为了在创建实例的时候调用人数累加的函数。
def __new__(self):
ClassTest.addNum()
return super(ClassTest, self).__new__(self)
class Student(ClassTest):
def __init__(self):
self.name = ''
a = Student()
b = Student()
print(ClassTest.getNum())
- 3、静态方法
- 定义:使用装饰器@staticmethod。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法。
- 调用:实例对象和类对象都可以调用。
- 总结:静态方法是类中的函数,不需要实例。静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但是和类本身没有关系,也就是说在静态方法中,不会涉及到类中的属性和方法的操作。可以理解为,静态方法是个独立的、单纯的函数,它仅仅托管于某个类的名称空间中,便于使用和维护。
- 静态方法的演示:
# 譬如,我想定义一个关于时间操作的类,其中有一个获取当前时间的函数。
import time
class TimeTest(object):
def __init__(self, hour, minute, second):
self.hour = hour
self.minute = minute
self.second = second
@staticmethod
def showTime():
return time.strftime("%H:%M:%S", time.localtime())
print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)
理解:如上,使用了静态方法(函数),然而方法体中并没使用(也不能使用)类或实例的属性(或方法)。若要获得当前时间的字符串时,并不一定需要实例化对象,此时对于静态方法而言,所在类更像是一种名称空间。
其实,我们也可以在类外面写一个同样的函数来做这些事,但是这样做就打乱了逻辑关系,也会导致以后代码维护困难。
三、单例模式
1、__new__魔法方法
* __new__魔法方法用于创建于返回一个对象。在类准备将自身实例化时调用。
注:在__new__魔法方法于__init__魔法方法共同在一个类中使用时,优先输出的是__new__魔法方法,后输出__init__魔法方法。
演示实例:
class Duck(object):
def __init__(self):
print('这是init魔法方法!')
def __new__(cls, *args, **kwargs):
print('这是snew魔法方法!')
duck = Duck()
打印输出结果:
E:\python\python.exe D:/PycharmProjects/基础班第11期/day-14/上课代码练习/联系一.py
这是new魔法方法!
Process finished with exit code 0
- 注意:
- new()方法用于创建对象。
- init()方法在对象创建的时候,自动调用。
- 但是此处重写了父类的__new__()方法,覆盖了父类__new__()创建对象的功能,所以对象并没有创建成功。所以仅执行__new__()方法内部代码。
- 对象创建执行顺序
- 1、通过
__new__()
方法创建对象 - 2、并将对象返回,传给
__init__
方法
- 1、通过
- 练习:
'''
在自定义类中实现创建对象
'''
# 思路:
# 1、重写父类(object)的__new__()方法
# 2、并且在该方法内部,调用父类的__new__()方法
class Duck(object):
# init方法是对属性的初始化
def __init__(self):
print('这是init方法!')
# new方法是创建实例对象使用的
def __new__(cls, *args, **kwargs):
print('这是new方法!')
return super().__new__(cls)
duck = Duck()
打印输出结果:
E:\python\python.exe D:/PycharmProjects/基础班第11期/day-14/上课代码练习/联系一.py
这是new方法!
这是init方法!
Process finished with exit code 0
- 注意:
- 在创建对象时,一定要将对象返回,才会触发
__init__()
方法。 __init__()
方法当中的self,实际上就是__new__
返回的实例,也就是对象。
- 在创建对象时,一定要将对象返回,才会触发
__init__()
与__new__()
区别__init__()
是实例方法,__new__()
是静态方法__init__
在对象创建后自动调用,__new__
是创建对象的方法。__init__
方法与__new__
方法是配合使用的。
2、单例模式
3、单例模式的介绍
- 单例模式是一种常用的软件设计模式。也就是说该类只包含一个实例。
- 通过单例模式可以保证系统中一个类只有一个实例而且该实例易与外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只存在一个,单例模式是最好的解决方式。
- 通常应用在一些资源管理器中,比如日志记录等。
4、单例模式的实现
- 思路
- 当对象不存在时,创建对象。
- 当对象存在时,永远返回当前已经创建对象。
class Single(object):
obj = None
def __new__(cls, *args, **kwargs):
if cls.obj is None:
# 对创建的obj进行修改
cls.obj = super().__new__(cls)
return cls.obj
else:
return cls.obj
s = Single()
s1 = Single()
print(id(s))
print(id(s1))
打印输出结果:
/Volumes/苹果微软公共盘/PycharmProjects/venv/bin/python /Volumes/苹果微软公共盘/PycharmProjects/基础班第11期/day-14/上课代码/单例模式.py
140712910705088
140712910705088
Process finished with exit code 0
单例模式思维导图:
- 总结:
- 执行第一个single调用时,对obj进行了修改。
- 执行第二个sihgle1调用时,obj不是none。执行else语句。
四、模块的简介和创建
1、模块
- 模块化指一个完整的程序分解成一个个的小模块
- 通过将模块组合,来搭建出一个完整的程序
- 模块化的优点:
- 方便开发
- 方便维护
- 模块可以复用
2、模块的创建
- 在Python当中一个.py文件就是一个模块。
- 在一个模块中引入外部模块import+模块名(模块名就是py文件)。
- 可以引入同一个模块多次,但是模块的实例只会创建一次。
- import + 模块名 as 模块别名
- 在一个模块内部都有一个__name__.通过它我们可以获取模块的名字。
- 如果py文件直接运行时,那么__name__默认等于字符串“main”。__name__属性值为__main__的模块是主模块。一个程序只有一个主模块。
代码演示实例:
主模块:
from test import *
test()
a = A()
print(a.name)
需调用模块:
print('这是我的第一个模块!')
# a = 1
# b = 10
# print(__name__)
# print(locals())
def test():
print('test')
class A(object):
def __init__(self):
self.name = '刘亦菲'
# a = A()
# print(a.name)
# if __name__ == '__main__': ---函数是程序入口,只有当是本文件运行才能将程序运行成功。
if __name__ == '__main__':
a = A()
print(a.name)
打印主模块输出效果:
/Volumes/苹果微软公共盘/PycharmProjects/venv/bin/python /Volumes/苹果微软公共盘/PycharmProjects/基础班第11期/day-14/上课代码练习/模块/主模块.py
这是我的第一个模块!
test
刘亦菲
Process finished with exit code 0
注:此电脑macOS Big Sur。
注:as是将调用模块中的变量更换名字的词。