《从Python开始学编程》第4章 朝思暮想是对象

之前我们讲了Python面向过程的一面,现在我们来说说它面向对象的一面,学完这章你就会发现,写Python原来就是天天搞对象,就连过程都是它的对象!那什么是面向对象的编程呢,我们可以和面向过程对比一下。

举个例子,我想吃奥利奥。从过程角度看,我就要经过扭一扭、舔一舔、泡一泡、吃下去这几个步骤才能完成吃奥利奥这个事情。从对象角度看(太详细写会占篇幅,这里会省略一些),这件事参与者有手、奥利奥、舌头、牛奶、嘴,那么首先我要得知这几者的具体信息:手——有五个指头,可以扭动、奥利奥——一个带馅儿的由两个饼干构成的夹心饼干......然后就是具体的做法了,用两只手的五指将夹心的奥利奥的两片饼干扭动后,将两片饼干分开,馅儿会分别粘在两个饼干上,然后......

从这个例子我们可以发现几个事情。“扭一扭”等这些过程就好比一个个封装好的函数,一个函数实现一个过程。但是可以看到,无论哪个过程都会出现奥利奥这个东西,所以每个过程的关联度很高,也即耦合性很强。从对象的角度,每个角色都在做自己的动作,有自己的特点,比如手做扭的动作,而奥利奥刚好是一个有两个饼干的夹心,他们是完全分开的角色,所以面向对象的耦合性很弱。我们再看看面向对象里具体做法的描述,乍一看这不就是“扭一扭”的过程嘛。不过在看过广告之前,一说“扭一扭”这个词,大家肯定不知道是什么意思,因为这是对某一件事的高度抽象概括而来的几个过程,只有仔细阅读这个抽象的具体内容,也就是看过广告的,才知道“扭一扭”为何。而这个抽象的具体内容,是由一个个对象演绎而成的,所以说通过对象的描述,虽然复杂很多,但能够实在的还原事物原本的过程。

所以综上,面向对象的程序语言是更加贴近人的语言,而面向过程更贴近于机器。在如今计算机性能大幅提高的今天,我们越来越重视语言向人的方向发展,利用面向对象语言,程序员可以更快更好的开发出新的产品,大大提高的应用产能的发展。在对面向对象有一点了解后,我们来学学Python面向对象的语法。

1 给对象归类

对象满地球都是,什么《高等数学》、猫、老师......这么杂,我们首先要归个类吧,生物界还分七等呢,如果不分就没有层次,事物看起来会很乱。比如天上这么多飞来飞去的,我们暂且归为鸟类吧。那鸟都有什么呢?比如怎么运动、吃什么、怎么吃?我们来写写看:

class Bird():
    '''
    这是个鸟类
    '''
    move = 'fly'
    food = 'meat'
    def eat(self, name, thing):
        print(name,' eat a ',thing,' !')

class是“类”的关键字,Bird是我取的类的名字。里面就是鸟类的属性了,我们还是用三个单引号表示帮助文档,像move这类属性称为成员变量。利用def关键字,我们可以定义方法,也就是这个类的一些动作,这里指鸟吃东西的动作。这里的方法其实和函数差不多,括号里面是参数。唯一不同就是作为类的方法,括号内必须有self关键字,是指“自己”,也就是在方法里可以调用自己类的成员变量和方法,具体调用后面讲。

其实Python有许多内置方法,这些方法名字都用双下划线括起,我们这里讲一个常用的方法,名为__init__()。这个方法的作用是,在你利用这个类创建一个对象时,它会立刻执行,比如我们现在改写一下鸟类:

class Bird(object):
    move = 'fly'
    food = 'meat'

    def __init__(self, name):
        self.name = name

    def eat(self, thing):
        print(self.name,' eat a ',thing,' !')

这个类现在新增加了init方法,每创建一次对象,这个方法便会执行。创建方法在后面讲解。现在我们已经创建了一个比较完整的鸟类,但这个类过于宽泛,鸟类名下有很多鸟,每个鸟有自己独特的属性,比如羽毛颜色会不一样,这该怎么办?Python中,类可以继承,继承的类包含父类中所有的属性,也会执行父类的内置方法,而且我们可以重写父类,更改父类的方法。比如我们现在创建天鹅类,天鹅也是鸟的一种呀。

class Swan(Bird):

    feather_color = 'white'  # 新的成员变量

    def eat(self):  # 重写eat方法
        print('I ate a frog !')

天鹅类,它继承了鸟类的属性外,我还添加了羽毛颜色,并且重写了吃的方法。添加的属性会在这一子类生效,而重写的方法会代替父类的方法生效。我们还可以看到,在定义鸟类时,括号写的是object,现在写的是Bird,可知括号内写的是继承的类,object是对象类,是所有类的总和,也即是一个根类,一般第一个定义的就用object继承,而后面我们继承鸟类,所以括号写Bird。

2 我想要一个对象

女娲有了人的模型后捏了一堆的人从而创造了人类。我们现在“捏”了这么多类,也该有自己的对象了。有了鸟类,现在就创建一只鸟吧,我们就叫他spring。

spring = Bird('spring')

看,只需要一行代码,你就有一个对象了。原本只需要写Bird()即可,但我们类里有一个init方法,这个方法里有一个name参数,我们需要在这里写入,执行完该语句后,程序会立刻执行init方法。在方法里,我们使用了self.name.是用于引用类属性的,点前写类的名字,后写要调用的成员或方法即可。这里self是自己,也就是调用成员变量name并进行初始化。也即在执行完这行代码后,我们就有了name成员变量了。现在我们试试调用里面的属性和方法:

print(spring.name, spring.move, spring.food)  # 调用成员变量
# 输出: springflymeat
spring.eat('worm')  # 调用成员方法
# 输出:spring eat a worm !

我们再造一只天鹅试试:

summer = Swam('summer')  # 创建对象
print(summer.name, summer.food, summer.feather_color)  # 调用成员变量
# 输出:summermeatwhit
summer.eat  # 调用重写方法
# 输出:I ate a frog !

我们看到天鹅确实继承了鸟类的属性,同时也按照重写的eat方法输出。所以说,我们想要一个对象,要先有它的类,再通过类创造对象。我们可以调用对象的方法实现许多事情。感兴趣的可以试试把吃奥利奥的事情写写。

3 一切皆对象

虽然说Python集成了面向过程、面向对象等于一身的超级语言,但究其本身,就是面向对象,一切的衍生都是利用对象写出的。先说说之前学到的几种数据结构,我曾提到它有很多的函数可以用,其实这些函数恰巧是处理这些数据的方法。Python把这些数据结构看做对象,通过对象方法进行处理。我们加下来详细说说处理他们的方法。

3.1 列表

list.count(5)       # 查看列表有几个元素5
list.index(3)       # 查询第一个出现3的元素下标
list.append(6)      # 在列表最后一个添加元素6
list.sort()         # 排序
list.reverse()      # 反转次序
list.pop()          # 删除最后一个元素并返回该元素
list. remove(2)     # 删除第一个元素2
list. insert(0, 9)  # 在第0位置插入元素2
list. clear()       # 清空列表

3.2 元组和字符串

tuple.count(5)  # 查看元组有几个元素5
tuple.index(3)  # 查询元组第一个出现3的元素下标
str = 'hello world!'
sub = 'world'
str.count(sub)  # 返回sub在str出现的次数
str. find(sub) # 从左开始,返回找到第一个sub的第一个字符的位置,没有返回-1
str. index(sub) # 从左开始,返回找到的第一个sub的索引,没有则报错
str. rfind(sub) # 从右开始,返回找到第一个sub的第一个字符的位置,没有返回-1
str. rindex(sub) # 从右开始,返回找到的第一个sub的索引,没有则报错
str. isalnum() # 查看是否所有字符都是字母或数字,是则返回True,否则False
str. isalpha() # 查看是否所有字符都是字母,是则返回True,否则False
str. isdigit() # 查看是否所有字符都是数字,是则返回True,否则False
str. istitle() # 查看是否所有字符都是首字母大写,是则返回True,否则False
str. isspace() # 查看是否所有字符都是空格,是则返回True,否则False
str. islower() # 查看是否所有字符都是,都是小写,是则返回True,否则False
str. isupper() # 查看是否所有字符都是大写,是则返回True,否则False
str. splite([sep, [max]]) # 以sep为分隔符分隔字符串max次,默认用空格,都为选填
str. rsplite([sep, [max]]) # 从右开始分隔
str. join(s) # 返回以str分隔的s
str. strip([cub]) # 去掉字符串去掉开头结尾的sub,默认为空格
str. replace(sub, new_sub) # 用new_sub代替字符串中的sub
str. capitalize() # 返回开头字母大写的字符串
str. lower() # 返回所有字母小写的字符串
str. upper() # 返回所有字母大写的字符串
str. swapcase() # 返回大小写全部反转的字符串
str. title() # 将开头字母全部大写(以空格分开)
str. center(width) # 在width长度内将字符串居中
str. ljust(width) # 在width长度内将字符串居右
str. rjust(width) # 在width长度内将字符串居左

3.3 词典

dict.values()   # 返回词典所有值(可用于循环遍历)
dict.keys()     # 返回词典所有键(可用于循环遍历)
dict.clear()    # 清空词典

不光是这些数据结构,就连面向过程的一些方法,都是用面向对象实现的。

3.4 循环

每一个for循环都会自动寻找下一个变量的元素,事实上,它是通过迭代器和迭代器中的__next__()方法实现循环的,只是for循环就是运用这个原理,可以省略这样的写法。当next方法后已经是末尾元素,系统会返回StopIteration异常告知循环结束。我们可以尝试利用面向对象的方法直接实现循环:

list = [1, 2]
iterator = iter(list)  # iter是迭代器转换函数,可以将变量转换成迭代器
iterator.next()
iterator.__next__()
iterator.__next__()  # 报错

如果我们想从头再次开始,则需要重新定义一个迭代器。上面的写法其实可直接用for循环实现:

for i in [1, 2]:

循环对象对于处理庞大数据的循环是有好处的,它可以边遍历,边导入变量的元素,这样可以让计算机少占用内存。其实我们可以借助生成器实现自定义循环对象,可以一次性循环多种不同的元素。生成器是一个函数,用def关键字定义,内部每完成一种对象的定义就可以用yield关键字截断一次,程序会对这个元素先做一次循环遍历,再找下一个。举个例子:

def gen():
    a = 100
    yield a
    a = a*8
    yield a
    yield 1000

for i in gen():
    print(i)

可以看到,for循环首先截断在第一个a的元素并做一次循环,并用相同的方法进行后两次循环相当于将数据分成3组循环,在数据量庞大的情况下,可以节省内存。

3.5 函数

函数也是一个对象,事实上它是利用创建类里的__call__()方法实现的,我们可以看看:

class function(object):

def __call__(self, a)
    return a+5

其实这种写法完全等价于:

def function(a):
    return a+5

只是后者本质上还是调用前者的方法实现的,之后我们只需用正常的方法调用:

print(function(3))  # 输出8

所以说Python的函数其实是使用面向对象的方法实现的。

3.6 模块和异常

我们之前讲到许多调用函数的方法,包括自己定义的,以及Python库。事实上他们也可以看做对象,所以这些库又可以称为模块,我们是利用importfrom这两个方法调用这些模块。那么同理,异常也是对象,所以异常对象其实也有一些方法,下面举例:

try:
    a = 1/0
except :
    ZeroDivisionError as e:
        print('catch ZeroDivisionError')
        print(e.message)

这里首先我们利用as关键字,将ZeroDivisionError这个错误名称用e代替,事实上这个在其他地方也可以用。我们调用e对象的message方法,查看异常信息。

具体实例代码可以看我的码云:第四章样例代码

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

推荐阅读更多精彩内容