python基础(面向对象(对象魔法三大特征)、多态)

1.对象魔法
在面向对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法。使用对象而非全局变量和函数的原因有多个,下面列出了使用对象的最重要的好处。

  • 多态:多态指的是能够同样地对待不同类型和类的对象,即无需知道对象属于哪个类就可调用其方法。
  • 封装:对象可能隐藏(封装)其内部状态。在有些语言中,这意味着对象的状态(属性)只能通过其方法来访问。在Python中,所有的属性都是公有的,但直接访问对象的状态时程序员应谨慎行事,因为这可能在不经意间导致状态不一致。
  • 继承:一个类可以是一个或多个类的子类,在这种情况下,子类将继承超类的所有方法。你可指定多个超类,通过这样做可组合正交(独立且不相关)的功能。为此,一种常见的做法是使用一个核心超类以及一个或多个混合超类。

多态

术语多态(polymorphism)源自希腊语,意思是“有多种形态”。这大致意味着即便你不知道变量指向的是哪种对象,也能够对其执行操作,且操作的行为将随对象所属的类型(类)而异。

类的继承有两层意义:

1.改变 2.扩展

多态就是类的这两层意义的一个具体的实现机制。

多态,多种状态!不同的子类对象,根据不同的情况,调用相同的父类方法,相同的方法产生不同的执行结果。即调用不同的类实例化对象下的相同的方法,实现的过程不一样。

 

例如,假设你要为一个销售食品的电子商务网站创建在线支付系统,程序将接收来自系统另一部分(或之后设计的类似系统)的购物车。因此你只需计算总价并从信用卡扣除费用即可。
你首先想到的可能是,指定程序收到商品时必须如何表示。例如,你可能要求用元组表示收到的商品,如下所示:
('SPAM', 2.50) 
如果你只需要描述性标签和价格,这样的表示很好,但不太灵活。假设该网站新增了拍卖服务,即不断降低商品的价格,直到有人购买为止。在这种情况下,如果能够允许用户像下面这样做就好了:将商品放入购物车并进入结算页面(你所开发系统的一部分),等到价格合适时再单击“支付”按钮。
然而,使用简单的元组表示商品无法做到这一点。要做到这一点,表示商品的对象必须在你编写的代码询问价格时通过网络检查其当前价格,也就是说不能像在元组中那样固定价格。要解决这个问题,可创建一个函数。
# 不要像下面这样做:
def get_price(object): 
    if isinstance(object, tuple): 
        return object[1] 
    else: 
        return magic_network_method(object)

这里使用isinstance来执行类型/类检查旨在说明:使用类型检查通常是馊主意,应尽可能避免。

前面的代码使用函数isinstance来检查object是否是元组。如果是,就返回其第二个元素,否则调用一个神奇的网络方法。
如果网络方法已就绪,问题就暂时解决了。但这种解决方案还是不太灵活。如果有位程序员很聪明,决定用十六进制的字符串表示价格,并将其存储在字典的'price'键下呢?没问题,你只需更新相应的函数。
# 不要像下面这样做:
def get_price(object): 
    if isinstance(object, tuple): 
        return object[1] 
    elif isinstance(object, dict): 
        return int(object['price']) 
    else: 
        return magic_network_method(object)

你确定现在考虑到了所有的可能性吗?假设有人决定添加一种新字典,并在其中将价格存储在另一个键下,你该如何办呢?当然,可再次更新get_price,但这种应对之策能在多长时间内有效呢?每当有人以不同的方式实现对象时,你都需要重新实现你的模块。如果你将该模块卖给了别人,转而从事其他项目的开发,客户该如何办呢?显然,这种实现不同行为的方式既不灵活也不切实际。
那么该如何做呢?让对象自己去处理这种操作。这好像没什么大不了,但仔细想想将发现,这样事情将简单得多:每种新对象都能够获取或计算其价格并返回结果,而你只需向它们询问价格即可。这正是多态(从某种程度上说还有封装)的用武之地。


2.多态和方法
你收到一个对象,却根本不知道它是如何实现的——它可能是众多“形态”中的任何一种。你只知道可以询问其价格,但这就够了。至于询问价格的方式,你应该很熟悉。
>>> object.get_price() 
2.5 
像这样与对象属性相关联的函数称为方法。
>>> 'abc'.count('a') 

>>> [1, 2, 'a'].count('a') 

如果有一个变量x,你无需知道它是字符串还是列表就能调用方法count:只要你向这个方法提供一个字符作为参数,它就能正常运行。
下面来做个实验。标准库模块random包含一个名为choice的函数,它从序列中随机选择一个元素。下面使用这个函数给变量提供一个值。
>>> from random import choice 
>>> x = choice(['Hello, world!', [1, 2, 'e', 'e', 4]]) 
执行这些代码后,x可能包含字符串'Hello, world!',也可能包含列表[1, 2, 'e', 'e', 4]。具体是哪一个,你不知道也不关心。你只关心x包含多少个'e',而不管x是字符串还是列表你都能找到答案。为找到答案,可像前面那样调用count。
>>> x.count('e') 

从上述结果看,x包含的应该是列表。但关键在于你无需执行相关的检查,只要x有一个名为count的方法,它将单个字符作为参数并返回一个整数就行。如果有人创建了包含这个方法的对象,你也可以像使用字符串和列表一样使用这种对象。

多态形式多样
每当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用。这不仅仅适用于方法,我们还通过内置运算符和函数大量使用了多态。请看下面的代码:
>>> 1 + 2 

>>> 'Fish' + 'license' 
'Fishlicense' 
上述代码表明,加法运算符(+)既可用于数(这里是整数),也可用于字符串(以及其他类型的序列)。为证明这一点,假设你要创建一个将两个对象相加的add函数,可像下面这样定义它(这与模块operator中的函数add等价,但效率更低):
def add(x, y): 
   return x + y 
可使用众多不同类型的参数来调用这个函数。
>>> add(1, 2) 

>>> add('Fish', 'license') 
'Fishlicense' 
这也许有点傻,但重点在于参数可以是任何支持加法的对象。请注意,这些对象必须支持它们之间的加法,因此调用add(1, 'license')不可行。如果要编写一个函数,通过打印一条消息来指出对象的长度,可以像下面这样做(它对参数的唯一要求是有长度,可对其执行函数len)。
def length_message(x): 
    print("The length of", repr(x), "is", len(x)) 
如你所见,这个函数还使用了repr。repr是多态的集大成者之一,可用于任何对象,下面就来看看:
>>> length_message('Fnord') 
The length of 'Fnord' is 5 
>>> length_message([1, 2, 3]) 
The length of [1, 2, 3] is 3 
很多函数和运算符都是多态的,你编写的大多数函数也可能如此,即便你不是有意为之。每当你使用多态的函数和运算符时,多态都将发挥作用。事实上,要破坏多态,唯一的办法是使用诸如type、issubclass等函数显式地执行类型检查,但你应尽可能避免以这种方式破坏多态。重要的是,对象按你希望的那样行事,而非它是否是正确的类型(类)。

 

多态案例

class MiniOS(object):
    """MiniOS 操作系统类 """
    def __init__(self, name):
        self.name = name
        self.apps = []  # 安装的应用程序名称列表

    def __str__(self):
        return "%s 安装的软件列表为 %s" % (self.name, str(self.apps))

    def install_app(self, app):
        # 判断是否已经安装了软件
        if app.name in self.apps:
            print("已经安装了 %s,无需再次安装" % app.name)
        else:
            app.install()
            self.apps.append(app.name)


class App(object):
    def __init__(self, name, version, desc):
        self.name = name
        self.version = version
        self.desc = desc

    def __str__(self):
        return "%s 的当前版本是 %s - %s" % (self.name, self.version, self.desc)

    def install(self):
        print("将 %s [%s] 的执行程序复制到程序目录..." % (self.name, self.version))


class PyCharm(App):
    pass


class Chrome(App):
    def install(self):
        print("正在解压缩安装程序...")
        super().install()


linux = MiniOS("Linux")
print(linux)

pycharm = PyCharm("PyCharm", "1.0", "python 开发的 IDE 环境")
chrome = Chrome("Chrome", "2.0", "谷歌浏览器")

linux.install_app(pycharm)
linux.install_app(chrome)
linux.install_app(chrome)

print(linux)

运行结果

Linux 安装的软件列表为 []
将 PyCharm [1.0] 的执行程序复制到程序目录...
正在解压缩安装程序...
将 Chrome [2.0] 的执行程序复制到程序目录...
已经安装了 Chrome,无需再次安装
Linux 安装的软件列表为 ['PyCharm', 'Chrome']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值