程序加license_Python程序开发过程中经常说没对象,创建一个对象,到底什么是对象...

0eb47ef0b2c34bb5baaf7189f83cfffd.png

对象的魔力

在面向对象程序设计中,术语对象(object)基本上可以看做数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。使用对像代替全局变量和函数的原因可能有很多。其中对象最重要的有点包括以下几方面。

☑ 多态(Polymorphism):意味着可以对不同类的对象使用同样的操作,它们会像被“施了魔法一般”工作。

☑ 封装(Encapsulation):对外部世界隐藏对象的工作细节。

☑ 继承(Inheritance):以通用的类为基础建立专门的对象。

在许多关于面向对象程序设计的介绍中,这几个概念的顺序是不同的。封装和继承会首先被介绍,因为它们被用作现实世界中的对象的模型。这种方法不错,但是在我看来,面向对象程序设计最有趣的特性是多态。(以我的经历来看)它也是让大多数人犯晕的特性。所以本章会以多态开始,而且这一个概念就足以让你喜欢面向对象程序设计了。

多态

术语多态来自希腊语,意思是“有多种形式”。多态意味着就算不知道变量所引用的对象类型是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为。例如,假设一个食品销售的商业网站创建了一个在线支付系统。程序会从系统的其他部分(或者以后可能会设计的其他类似的系统)获得一“购物车”中的商品,接下来要做的就是算出总价然后使用信用卡支付。

当你的程序获得商品时,首先想到的可能是如何具体地表示它们。比如需要将它们作为元组接收,像下面这样:

("SPAM", 2.50)

如果需要描述性标签和价格,这样就够了。但是这个程序还是不够灵活。我们假设网站支持拍卖服务,价格在货物卖出之前会逐渐降低。如果用户能够把对象放入购物车,然后处理结账(你的系统部分),等价格到了满意的程度后按下“支付”按钮就好了。

但是这样一来简单的元组就不能满足需要了。为了实现这个功能,代码每次询问价格的时候,对象都需要检查当前的价格(通过网络的某些功能),价格不能固定在元组中。解决起来不难,只要写个函数:

# Don't do itdef getPrice(object):     if isinstance(object, tuple):         return object[1]     else:         return magic_network_method(object)

注:这里用isinstance进行类型/类检查是为了说明一点,类型检查一般来说并不是什么好方法,能不用则不用。函数isinstance在7.2.6节会介绍。

前面的代码中使用isinstance函数查看对象是否为元组。如果是的话,就返回它的第2个元素,否则会调用一些“有魔力的”网络方法。

假设网络功能部分已经存在,那么问题已经解决了,目前为止是这样。但程序还不是很灵活。如果某些聪明的程序员决定用十六进制数的字符串来表示价格,然后存储在字典中的键"price"下面呢?没问题,只要更新函数:

# Don't do itdef getPrice(object): if isinstance(object, tuple): return object[1] elif isinstance(object, dict): return int(objecct["price"]) else: return magic_network_method(object)

现在是不是已经考虑到了所有的可能性?但是如果某些人希望为存储在其他键下面的价格增加新的字典呢?那有怎么办呢?可以再次更新getPrice函数,但是这种工作还要做多长时间?每次有人要实现价格对象的不同功能时,都要再次实现你的模块。但是如果这个模块已经卖出了并且转到了其他更酷的项目中,那要怎么应付客户?显然这是个不灵活且不切实际的实现多种行为的代码编写方式。

那么应该怎么办?可以让对象自己进行操作。听起来很清楚,但是想一下,这样做会轻松很多。每个新的对象类型都可以检索和计算自己的价格并且返回结果,只需向它询问价格即可。这时候多态(在某种程度上还有封装)就要出场了。

1. 多态和方法

程序接收到一个对象,完全不了解该对象的内部实现方式——它可能有多种“形状”。你要做的就是询问价格,这样就够了,实现方法是我们熟悉的:

>>> object.getPrice() 2.5

绑定到对象特性上面的函数成为方法(method)。我们已经见过字符串、列表和字典方法。实际上多态也已经出现过:

>>> "abc".count("a") 1>>> [1, 2, "a"].count("a") 1

对于变量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") 1

本例中,看来是字符串胜出了(Marlowes:原文上随机选择到的是字符串。 =_=)。但是关键点在于不需要检测类型:只需要知道x有个叫做count的方法,带有一个字符作为参数,并且返回整数值就够了。如果其他人创建的对象类也有count方法,那也无所谓,你只需要像用字符串和列表一样使用该对象就行了。

2. 多态的多种形式

任何不知道对象到底是什么类型,但是又要对对象“做点儿什么”的时候,都会用到多态。这不仅限于方法,很多内建运算符和函数都有多态的性质,考虑下面这个例子:

>>> 1 + 23>>> "Fish" + "license"'Fishlicense'

这里的加运算符对于数字(本例中为整数)和字符串(以及其他类型的序列)都能起作用。为说明这一点,假设有个叫做add的函数,它可以将两个对象相加。那么可以直接将其定义成上面的形式(功能等同但比operator模块中的add函数效率低些)。

>>> def add(x, y):...     return x + y # 对于很多类型的参数都可以用:>>> add(1, 2) 3>>> 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、isinstance以及issubclass函数等。如果可能的话,应该尽力避免使用这些毁掉多态的方式。真正重要的是如何让对象按照你所希望的方式工作,不管它是否是正确的类型(或者类)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值