一、面向对象之多态
1.1、多态
- 定义:多态是面向对象的三大特征之一(封装、继承和多态),从字母意思理解就是多种形态。
- 一个对象可以以不同形态呈现;
- 面向对象的三大特征:
a. 封装: 确保对象中的数据不被安全不被改变;
b. 继承:保证了对象中的扩展性。
c. 多态:保证了程序的灵活性
代码分析:
class A(object):
def __init__(self, name):
self.name = name
def speak(self):
print("大家好,我是%s" % self.name)
class B(object):
def __init__(self, name):
self.name = name
def speak(self):
print("大家好,我是%s" % self.name)
a = A('张敏')
b = B('朱茵')
def fun(obj):
obj.speak()
fun(a)
fun(b)
--------------------输出------------------------
大家好,我是张敏
大家好,我是朱茵
- 上面例子就是多态的演示,最后一个 fun(obj)函数,函数对象obj 通过调用不同的类方法,来展示多种不同的形式,这样增强了代码的灵活性。
多态的特点:
- 只关心对象的实例方法是否同名,不关心对象所属的类型;
- 对象所属的类之间,继承关系可有可无;
- 多态的好处可以增加代码外部调用的灵活性,让代码更加通用;
- 多态是调用方法的技巧,不会影响到类的设计。
2、鸭子类型 (Ducking Type)
1)什么是鸭子类型?
鸭子类型: 当一只鸟走起路来像鸭子,游起泳来像鸭子,叫起来像鸭子,那么这只鸟就可以被叫做鸭子。
我们并不关系对象是什么类型,到底是不是鸭子,只关系其行为是不是像鸭子。
2)举例分析:
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()
swan = Swan()
plane = Plane()
fly(duck)
fly(swan)
fly(plane)
--------------------输出------------------------
鸭子沿着水面滑行
天鹅在空中翱翔
飞机在云层之中飞行
上面3个类也没有继承关系,但是实现了多态的灵活性~
3)有继承关系的多态举例:<重点掌握>
class Grandpa(object):
def money(self):
print("这是爷爷的钱")
class Father(Grandpa):
def money(self):
super().money() # 继承父类方法,同时具有2种方法,可以使用,也可以不使用,满足多种需求。
print('这是爸爸的钱')
class Mother(Grandpa):
def money(self):
super().money() # 继承父类方法,同时有2种方法,可以去使用,也可以不使用,满足多种需求
print("这是妈妈的钱")
g = Grandpa()
f = Father()
m = Mother()
def fun(obj):
obj.money()
fun(g)
fun(f)
fun(m)
-----------------输出----------------
这是爷爷的钱
这是爷爷的钱 # 继承学习到新方法,功能更多,满足更多需求。
这是爸爸的钱
这是爷爷的钱 #继承学习到的新方法,功能更多,满足更多需求
这是妈妈的钱
这里通过 super().money 继承学习到父类更多的方法,功能更加丰富,致我们的代码在灵活运用时,满足更多的需求。
实际工作中,我们使用包含继承关系的多态方法更加常用。
3、属性和方法
3.1 类属性和实例属性
(1) 类属性: 之间定义在类中的属性
- 类属性可以通过类和实例对象来调用;
- 类属性只能通过类对象修改,不能通过实例对象来修改。
(2) 实例属性: 通过实例对象添加的属性就是实例属性,只能通过实例对象调用。,不能使用类对象调用;
class A(object):
num =0 # 定义在类里面,叫做 类属性
a = A()
a.num = 10 # 实例属性
print(A.num) # 使用类对象来调用类属性,因类里面已经定义了类属性,所有可以调用。
print(a.num) # 使用实例对象来调用实例属性
情景二:假如上面类属性没有定义呢 ?
实例属性,只能通过实例对象来调用,不能通过类属性来调用。
class A(object):
def __init__(self, name):
self.name = name # 这个 name 也是实例属性, self = a ,self.name = a.name (所以这个那么也是实例属性)
print(self)
a = A('刘亦菲') #
print(a) # slef = a
a.num = 10 # 实例属性,只能通过实例对象来调用,不能通过类属性来调用。
print(a.num)
print(a.name) # slef = a , slef.name = a.name, 所以a.name 也是实例方法。
# print(A.name) # 因为name 是实例属性。所以实例属性是不能被类对象所调用的。
情景三:
class A(object):
def __init__(self, name):
self.name = name
print(self)
def speak(self): # 实例方法
print("大家好,我是%s" % self.name)
a = A('张敏')
print(a)
print(a.speak())
print(A.speak()) # TypeError: speak() missing 1 required positional argument: 'self'
----------------输出----------------
<__main__.A object at 0x0000020377A524A8>
<__main__.A object at 0x0000020377A524A8>
大家好,我是张敏
None
print(A.speak())
TypeError: speak() missing 1 required positional argument: 'self'
- 类对象调用speak()方法失败了, 因此可以推断 speak() 也是一个实例方法。
- 类对象其实也可以调用实例方法的,但是必须传递类是使用的是哪一个实例来调用这个实例方法。比如:print(A.speak(a))
3.2 类方法 与 实例方法
1)类方法
定义:以 @classmethod 装饰器来装饰的方法叫类方法。
a. 实例对象和类对象同时都可以调用。
2) 实例方法
实例方法: 在类中定义,以self 为第一个参数的方法都是实例方法。
a. 实例方法在调用时,python 会将调用对象以 self 传入;
b. 类对象和实例对象都可以调用,但是类对象必须要传递一个实例对象参数。
class B(object):
def __init__(self, name):
self.name = name
print(self)
def speak(self): # 实例方法
print('大家好,我是%s' % self.name)
@classmethod # @classmethod 定义类方法
def man(cls):
print("大家好,我是星爷")
b = B('朱茵')
print(b)
# 实例方法
print(b.speak())
print(B.speak(b))
# 类方法
b.man()
B.man()
----------------输出----------------
<__main__.B object at 0x0000026553B62550>
<__main__.B object at 0x0000026553B62550>
大家好,我是朱茵
None
大家好,我是朱茵
None
大家好,我是星爷
大家好,我是星爷
3.3 静态方法
- 定义:在类中用 @staticmethod 来修饰的方法属于静态方法。
静态方法是一个功能方法,本质上就是一个函数~!
a. 静态方法不需要指定任何默认参数,静态方法可以通过类和实例来调用。
b. 静态方法,基本上是一个和当前类无关的方法,它只是一个保存到当前类中的函数。
c. 静态方法一般都是一些工具类方法,和当前类无关。
class B(object):
def __init__(self, name):
self.name = name
def speak(self): # 实例方法
print("大家好,我是%s" % self.name)
@classmethod # 类方法
def man(cls):
print("大家好,我是星爷")
@staticmethod # 定义静态方法,工具方法,存储一些功能。
def static():
print("我是静态方法")
b = B("欧布奥特曼")
# 实例方法
print(b.speak())
print(B.speak(b))
# 类方法
print(b.man())
print(B.man())
# 静态方法
print(b.static())
print(B.static())
------------------输出----------------
大家好,我是欧布奥特曼
None
大家好,我是欧布奥特曼
None
大家好,我是星爷
None
大家好,我是星爷
None
我是静态方法
None
我是静态方法
None
4、单例模式
4.1 new()方法
- new() 方法 :主要用于创建和返回一个对象,在类准备将自己实例化时调用。
代码介绍:
class Demo(object):
def __init__(self): # 实例方法
print("__init__")
def __new__(cls, *args, **kwargs): # 静态方法
super().__new__(cls)
print("__new__")
d = Demo()
上面的方法并不属于 new() 方法,因为没有返回一个对象。
怎么优化代码才是 new() 方法呢?
class Demo(object):
def __init__(self): # 实例方法
print("__init__")
def __new__(cls, *args, **kwargs): # 静态方法
print("__new__")
return super().__new__(cls)
d = Demo()
d1 = Demo()
print(d)
print(d1)
---------------输出-------------------
__new__
__new__
__init__
__new__
__init__
<__main__.Demo object at 0x00000208A25F2710>
<__main__.Demo object at 0x00000208A25F26D8>
总结:
- 在创建对象时,一定要将对象返回,才会自动触发init方法。
- _init() 方法中的self, 实际上就是 new() 方法返回的实例,也就时该对象。
init() 与 new()的区别
- init()是实例方法, new() 是静态方法;
- init()在对象创建后自动调用,new() 是创建对象的方法。
4.2 单例模式
上面 class Demo创建2个实例, d 和 d1 对应的实例都不同。那么怎么解决这个问题,只创建一个内存地址呢 ?
class Single(object):
obj = None # 最先加载
def __new__(cls, *args, **kwargs):
if cls.obj is None:
cls.obj = super().__new__(cls) # 继承
return cls.obj
else:
return cls.obj
s3 = Single()
s4 = Single()
print(s3)
print(s4)
<__main__.Single object at 0x000001F381DF0EB8>
总结:
做一个判断:
- 如果实例存在,我们就不再调用super这个方法,而是之间返回之前的那个实例对象
- 如果不存在,就调用super 方法创建实例并且返回。
二、 模块
2.1 模块的简介
- 模块化指的是将一个完整的程序分解为一个个的小模块
- 通过将模块组合,来搭建一个完整的程序
- 模块化的特点:
a. 方便开发
b. 方便维护
c. 模块可以复用。
2.2 创建并使用模块
导入模块方法:
首先,我们再当前目录创建一个自定义的模块 test1.py
然后,我们导入刚才的模块,并使用
import test1
print(test1)
--------------输出-------------
这是我的第一个模块
<module 'test1' from 'D:\\Python学习\\Python基础\\DAY14\\test1.py'>
2.3 导入模块的几种方式:
- import test1 : 直接导入模块
- from test1 import fun1 : 导入模块的部分功能。
- from test1 import * : 导入所有功能。
2.4 main函数对入口模块程序模块名name进行判断
(1)自己定义test1.py 模块:
print('这是我的第一个模块')
class A():
def __init__(self, name):
self.name = name
print(self.name)
if __name__ == "__main__":
a = A('迪迦奥特曼')
- name 等于刚才模块的 名称,这里是 test1。
- 我们可以通过locals() 来获取 当前的 main函数。
(2)对入口函数模块名 name做判断:
from test1 import *
'''
__main__ 函数
程序的入口函数,不是再当前模块中运行的程序,那么这个程序就不会运行。
'''
print(__name__)
a = A('欧布奥特曼')
--------------------输出--------------------
这是我的第一个模块
__main__
欧布奥特曼
- 因此,我们可以通过 main函数,程序的入口函数,程序入口函数不再当前的模块中运行的程序,那么这个程序就不会运行。