15、面向对象之多态 & 模块

一、面向对象之多态

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 通过调用不同的类方法,来展示多种不同的形式,这样增强了代码的灵活性。
多态的特点:
  1. 只关心对象的实例方法是否同名,不关心对象所属的类型;
  2. 对象所属的类之间,继承关系可有可无;
  3. 多态的好处可以增加代码外部调用的灵活性,让代码更加通用;
  4. 多态是调用方法的技巧,不会影响到类的设计。

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)    # 使用实例对象来调用实例属性
image.png

情景二:假如上面类属性没有定义呢 ?
实例属性,只能通过实例对象来调用,不能通过类属性来调用。

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>

总结:

  1. 在创建对象时,一定要将对象返回,才会自动触发init方法。
  2. _init() 方法中的self, 实际上就是 new() 方法返回的实例,也就时该对象。

init() 与 new()的区别

  1. init()是实例方法, new() 是静态方法;
  2. 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>

总结:
做一个判断:

  1. 如果实例存在,我们就不再调用super这个方法,而是之间返回之前的那个实例对象
  2. 如果不存在,就调用super 方法创建实例并且返回。

二、 模块

2.1 模块的简介
  • 模块化指的是将一个完整的程序分解为一个个的小模块
  • 通过将模块组合,来搭建一个完整的程序
  • 模块化的特点:
    a. 方便开发
    b. 方便维护
    c. 模块可以复用。
2.2 创建并使用模块

导入模块方法:

首先,我们再当前目录创建一个自定义的模块 test1.py


image.png

然后,我们导入刚才的模块,并使用

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函数,程序的入口函数,程序入口函数不再当前的模块中运行的程序,那么这个程序就不会运行。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容