Python面向对象,站在更高的角度来思考

开篇

面向过程编程和面向对象编程是两种基本的编程思想。

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789


在这之前,你学习到的都是面向过程编程,即:首先分析出解决问题的每个步骤 ,然后编写函数实现每个步骤,最后通过合适的顺序依次调用函数来解决问题。

不同于面向过程编程,面向对象编程的思想其实是从自然界的机理中借鉴而来的。正所谓物以类聚,在渺渺的大自然中,有数不胜数的类别,按照不同的星球来分类,可以分为:地球类、太阳类、火星类、月球类、海王星类等等


按照地球上的不同形态的生命,可以分为植物类、动物类、细菌类等等


你还可以按照自己的喜好,划分出自己的分类标准。

使用面向对象编程,可以让你站在一个更高的视角来审视问题,此时,你所解决的不仅仅是一个问题,而是一类问题。

 

对比面向过程,初识面向对象

在介绍各种概念之前,让我们先做一个小栗子,通过对比面向过程编程的方式去感受面向对象编程的奥妙。

栗子如下:

小明家里养了3只小猫,每到中午饭点,这三只小猫便要吃饭了。吃饭前,小猫们都会 先洗手,然后喵喵叫三声。请写一个程序,来分别模拟这三只小猫完整的吃饭过程,部分细节可自行脑部。

分析问题,可以绘制出解决问题的流程图

如果采用面向过程思想进行编程,可以这样写:

首先分别用函数实现以上流程图中的三个步骤

import time
#模拟吃饭
def eat(number,food):
    #number:小猫编号,可取1,2,3
    #food:某编号的小猫所吃食物的名称
    wash(number)#模拟洗手
    miao(number)#模拟喵喵叫三声
    print("{}号小猫正在吃{}".format(number,food))
    time.sleep(5)#吃饭需要5秒种
    print('{}号小猫进食完毕,睡觉睡觉咯'.format(number))
    print()

#模拟洗手
def wash(number):
    #number:小猫编号,可取1,2,3
    print('{}号小猫正在洗手...'.format(number))
    time.sleep(3)#洗手花费3秒种
    print('{}号小猫洗手结束'.format(number))

#模拟喵喵叫三声
def miao(number):
    #number:小猫编号,可取1,2,3
    print('第{}号小猫喵喵喵~~'.format(number))

有了函数之后,就可以按照合适的序列步骤调用函数来解决问题了:

#模拟第一只小猫的吃饭过程
eat(1,'树叶子')

#模拟第二只小猫的吃饭过程
eat(2,'海带丝')

#模拟第三只小猫的吃饭过程
eat(3,'石头')

得到如下输出:

1号小猫正在洗手...
1号小猫洗手结束
第1号小猫喵喵喵~~
1号小猫正在吃树叶子
1号小猫进食完毕,睡觉睡觉咯

2号小猫正在洗手...
2号小猫洗手结束
第2号小猫喵喵喵~~
2号小猫正在吃海带丝
2号小猫进食完毕,睡觉睡觉咯

3号小猫正在洗手...
3号小猫洗手结束
第3号小猫喵喵喵~~
3号小猫正在吃石头
3号小猫进食完毕,睡觉睡觉咯

而若采用面向对象编程,则可以写出如下代码(稍后会对每一句代码进行解释,这里只是先直观感受下面向对象编程的feel):

import time
class CatMeal():

    #number:小猫编号,可取1,2,3
    #food:某编号的小猫所吃食物的名称
    def __init__(self,number,food):
        self.number=number
        self.food=food

    #模拟吃饭
    def eat(self):
        self.wash()
        self.miao()
        print("{}号小猫正在吃{}".format(self.number,self.food))
        time.sleep(5)#吃饭需要5秒种
        print('{}号小猫进食完毕,睡觉睡觉咯'.format(self.number))
        print()

    #模拟洗手
    def wash(self):
        #number:小猫编号,可取1,2,3
        print('{}号小猫正在洗手...'.format(self.number))
        time.sleep(3)#洗手花费3秒种
        print('{}号小猫洗手结束'.format(self.number))

    #模拟喵喵叫三声
    def miao(self):
        #number:小猫编号,可取1,2,3
        print('第{}号小猫喵喵喵~~'.format(self.number))

#模拟第一只小猫的吃饭过程
miao1=CatMeal(1,'树叶子')
miao1.eat()

##模拟第二只小猫的吃饭过程
miao2=CatMeal(2,'海带丝')
miao2.eat()

#模拟第三只小猫的吃饭过程
miao3=CatMeal(3,'石头')
miao3.eat()

得到同样的输出:

1号小猫正在洗手...
1号小猫洗手结束
第1号小猫喵喵喵~~
1号小猫正在吃树叶子
1号小猫进食完毕,睡觉睡觉咯

2号小猫正在洗手...
2号小猫洗手结束
第2号小猫喵喵喵~~
2号小猫正在吃海带丝
2号小猫进食完毕,睡觉睡觉咯

3号小猫正在洗手...
3号小猫洗手结束
第3号小猫喵喵喵~~
3号小猫正在吃石头
3号小猫进食完毕,睡觉睡觉咯

在上面的代码中,我们使用关键字class定义了一个小猫吃饭的类,名字叫做CatMeal,然后实例化出来了3只小猫,分别模拟了它们吃饭的完整过程。你可能还是对有些代码,比如__init__那里搞不清,没关系,先别着急,在下一部分,我们将详解上述代码,在此之前,有一些东西需要讲一下:

如果你阅读过之前的文章,你会发现,那些文章的思路都是先通过介绍某种方法的不足,然后引出正题,比如由于需要用多个变量来存储多个数字,于是引出了列表这一简单高效的数据结构。在这里,我们先介绍了面向过程的方法,之后才介绍了面向对象的方法,那是不是面向对象比面向过程更具有优势呢?

并不是,这两种编程思想各有千秋。

我们可以按照从简到繁的顺序来捋清这一切:

(1)对于一个非常简单的问题,比如计算两数之和,我们甚至可以直接在命令行中敲下a+b,点击回车即可看到问题的答案:

>>> 1+2
3

(2)接着将问题变复杂些,这次也是计算两数之和,只不过待计算的数值对不止一个,于是我们可以定义一个函数,专门用于做两数之和:

def add(a,b):
    print('{}+{}={}'.format(a,b,a+b))
#调用函数计算两数之和
>>> add(1,2)
1+2=3

(3)然后,将问题更加复杂化,需要一个简易版计算器,可以用来计算加法和减法,并且要求用户只需输入操作数(待运算的数字,比如1,2,3),而无需输入运算符(比如+,-):

采用面向过程的思想,我们需要定义加法和减法两个函数

def add(a,b):
    print('{}+{}={}'.format(a,b,a+b))
def minus(a,b):
    print('{}-{}={}'.format(a,b,a-b))
#调用函数
add(1,2)
minus(2,1)
#输出
1+2=3
2-1=1

而若采用面向对象的思想,我们可以这样写:

class Cal():
    def add(self,a,b):
        print('{}+{}={}'.format(a,b,a+b))
    def minus(self,a,b):
        print('{}-{}={}'.format(a,b,a-b))

在上面的代码中,我们定义了一个计算类,名字叫做Cal

接下来从这个类中实例化出来一个对象,名字叫做z

#实例化出来一个对象z
z=Cal()

#调用方法
z.add(1,2)
z.minus(3,6)

输出结果

1+2=3
3-6=-3

(4)如果现在去除了用户不能输入运算符的限制,那么,使用面向过程编程的代码如下:

def add_or_minus(a,b,op):
    if op=='+':
        res=a+b
    elif op=='-':
        res=a-b
    print('{}{}{}={}'.format(a,op,b,res))
#调用
>>> add_or_minus(1,2,'+')
1+2=3
>>> add_or_minus(1,2,'-')
1-2=-1

使用面向对象编程代码如下:

class Cal():
    def add_or_minus(self,a,b,op):
        if op=='+':
            res=a+b
        elif op=='-':
            res=a-b
        print('{}{}{}={}'.format(a,op,b,res))
#实例化类并计算
>>> z=Cal()
>>> z.add_or_minus(1,2,'+')
1+2=3
>>> z.add_or_minus(1,2,'-')
1-2=-1

再次对比这两者,可以看出,面向对象编程给人的感觉是代码变得麻烦了些,而面向过程编程则清爽很多。

原因在于,实现加法和减法是个很简单的问题,在这个问题上使用面向对象的编程方式不能很好的体现出面向对象编程的灵活性和规范性,反而简单的面向过程编程更加易懂、易实现。

在今后,你会看到,当问题规模变大时,面向对象编程将会体现出来显著的优势。

总结来说:面向过程和面向对象这两种编程方式各有千秋,在解决规模较小的问题时使用前者更为方便,而对于大规模问题,后者具有显著优势。当然这也不是绝对的。在性能和维护成本等方面,两者也是不同的。所以,对于不同的应用场景,要学会灵活选用不同的编程方式。

面向对象编程的语法

在这一部分,我们将对之前栗子中面向对象代码进行拆分讲解。

定义一个类

class CatMeal():中:

class是一个Python关键字,表明要定义一个类,它的作用就好比定义函数时用到的关键字def

CatMeal是类的名字,一般约定首字母大写的代表类名,而首字母为小写的代表变量命

在还没有学习后面的内容之前,当你想创建一个新的类时,只需修改类名即可,其余照抄,比如定义一个动物类,可以写class Animal():。当然,在后面的学习中,你会解锁关于这一句代码的更高级用法。

在类中定义方法

我们知道,以关键字def定义的一个代码块叫做函数,用于完成特定的功能。其实,在类里面,也是使用def定义一个代码块,同样是用于完成特定的功能,只不过此时的名字不再是“函数”,而是“方法”(本质上还是函数)。在这个栗子中,定义的__init__eatwashmiao都是方法。

__init__方法默认第一个参数是self(约定俗成的叫法),后面的参数可以自行指定,就像给函数传参那样。

__init__方法里,以参数形式定义特征,称之为属性。

比如本栗中的numberfood就是类中定义的两个属性,你可以类比着添加更多属性。

当然,如果像之前的简易计算器栗子一样,不要求在创建类时手动指定属性,那么就可以不显式地写出__init__方法(可以回看那个栗子来加深理解)。

观察eatwashmiao方法,你会发现,每个方法里面都和__init__方法一样,第一个参数为self

其实,无论是__init__方法也好,其余自定义的方法也罢,它们的第一个参数都必须是self,它代表一个完整的实例化对象。与类相关联的属性和方法都会自动传递给self,通过使用self,可以轻松调用类中的一些属性和方法:

比如在eat方法中,我们传入了self,于是便可以在该方法中使用self.number获取在__init__方法中定义好的number具体值,同理可以使用self.food获取food的具体值。

从类中实例化对象

在编写完成这个CatMeal类之后,便可以通过类来实例化对象了,比如:

miao1=CatMeal(1,'树叶子')

运行这一句代码,会从抽象的类中实例化出来一个对象,名字叫做miao1,同时自动调用__init__方法,并将1树叶子分别传给__init__方法中的numberfood

其内部具体实现过程如下:先在内存中开辟一块空间,然后调用__init__方法,并让self指向那块内存空间,最后让实例化的对象miao1也指向那块内存空间

这个实例化的对象miao1可以通过.来访问类中的属性和调用类中的方法,比如访问food属性,可以写miao1.food;调用wash方法,可以写miao1.wash()

总结

初学者很容易被一堆self所困扰,所以这里尽量使用精炼的语言来总结第二部分所讲内容:

在定义类之后,实例化之前,self这个抽象的东西具有访问类中属性和调用类中方法的权限,所以无论是增加属性,还是增加方法,都离不开self。增加属性可以写self.new_attribute=new_attribute,增加方法可以写

def new_method(self):
    pass

(可以根据需要自行设定方法中的参数)

比如在定义eatwashmiao方法时,由于还没有实例化对象,因此若要访问类中的属性numberfood,必须借助具有权限的self。因此,每个方法都需要传入self这个参数,以使用self.来进行访问。self仿佛就是一把钥匙。

而在实例化(比如miao1=CatMeal(1,'树叶子'))之后,原先self具有的权限将会复制一份给这个实例化的对象。

之前需要使用self.number来访问number属性,现在依然可以,但同时也支持使用实例化对象.number来进行访问了,比如miao1.number


以上就是本期全部内容了,大家好好消化一下吧

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值