Python面向对象编程基础解析(一)


Posted in Python onOctober 26, 2017

1.什么是面向对象

面向对象(oop)是一种抽象的方法来理解这个世界,世间万物都可以抽象成一个对象,一切事物都是由对象构成的。应用在编程中,是一种开发程序的方法,它将对象作为程序的基本单元。

2.面向对象与面向过程的区别

我们之前已经介绍过面向过程了,面向过程的核心在‘过程'二字,过程就是解决问题的步骤,面向过程的方法设计程序就像是在设计一条流水线,是一种机械式的思维方式

优点:复杂的问题简单化,流程化

缺点:扩展性差

主要应用场景有:Linux内核,git,以及http服务

面向对象的程序设计,核心是对象,对象就是特征(变量)与技能(函数)的结合体。

优点:解决了程序扩展性差的问题

缺点:可控性差,无法预测最终结果

主要应用场景是需求经常变化的软件,即与用户交互比较频繁的软件

需要注意的是:面向对象的程序设计并不能解决全部问题,只是用来解决扩展性。当然,现在的的互联网软件,扩展性是最重要的

3.对象与类的概念

在python中,一切皆对象,一个对象应该具有自己的属性,也就是特征,还有有自己的功能,即方法

在Python中,特征用变量表示,功能用函数表示,所以对象就是变量与函数的结合体

而从各种各样的对象中抽取出来具有相同特征和相同功能组成的,就是类,所以说类是一系列对象共同特征与功能的结合体
下面让我们来定义一个类,方法与定义一个函数有些类似:

#定义一个中国人的类
class Chinese:
 #共同的特征
 country='China'
 
 #共同的技能
 def talk(self):
 print('is talking Chinese')
 def eat(self):
 print('is eating Chinese food')

这样我们就定义好了一个类,注意:

1.定义类用class关键字
2.类名一般首字母大写,且冒号前面不需要括号(非必须,有括号也不报错,一般需要继承object类来保证是新式类),区别于函数定义
3.与函数不同,类在定义阶段就会执行类里面的代码
4.类有两种属性,共同的特征叫数据属性,共同的功能叫函数属性

怎样由这个类产生一个对象呢?实例化:

#实例化的方式产生一个对象
p1=Chinese()
p2=Chinese()

我们可以得出结论了,不管现实世界中怎么样,但是在程序中,确实是先有类,才有的对象

我们已经通过实例化的方式得到两个对象了,但是有一个问题,得到的两个对象,特征和功能都是一样的,这根万物皆对象的理念完全不符啊,应该是每个对象都是不同的,这样的世界才有意思啊

事实上,我们在定义类的时候,忘记了定义 __init__() 这个函数,正确的定义方法应该是这样的:

#定义一个中国人的类
class Chinese:
 #共同的特征
 country='China'
 #初始化
 def __init__(self,name,age):
 self.name=name #每个对象都有自己的名字
 self.age=age #每个对象都有自己的年龄
 #共同的技能
 def talk(self):
 print('is talking Chinese')
 def eat(self):
 print('is eating Chinese food')
#实例化的方式产生一个对象
p1=Chinese('zhang',18)

类名加括号就是实例化,实例化就会自动触发__init__ 函数运行,可以用它来为每个对象定制自己的特征

我们在定义__init__函数的时候,括号里有三个参数,但是我们实例化调用的时候却只传了两个值,为什么不报错呢?这是因为self的作用就是:实例化的时候,自动将对象本身传给__init__函数的第一个参数,当然self只是个名字了。

注意:这种自动传值的机制只是在实例化的时候才会体现,类除了实例化还有一种作用就是属性引用,方法是类名.属性

从上面报错的代码可以看出,属性引用的时候,没有自动传值这回事,如果是类调用类里面的方法,需要手动把类当作参数传给它

Chinese.talk(Chinese)

我们学过名称空间的概念,定义一个变量,或者定义一个函数都会在内存中开辟一块内存空间,类里面也有定义变量(数据属性),定义函数(函数属性),他们也有名称空间,可以通过.__dict__的方法查看

p1=Chinese('zhang',18)
print(Chinese.__dict__)
#{'__module__': '__main__', 'country': 'China', '__init__': <function Chinese.__
# init__ at 0x000002187F35D158>, 'talk': <function Chinese.talk at 0x000002187F35D1E0>, 
# 'eat': <function Chinese.eat at 0x000002187F35D268>, '__
# dict__': <attribute '__dict__' of 'Chinese' objects>,
# '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None}
print(p1.__dict__)
#{'name': 'zhang', 'age': 18}

通过上面代码显示的结果我们知道了,打印实例化后的对象的名称空间,只显示自己特有的属性,如果想要找到和其他对象共有的属性,就要去类的名称空间里面去找

还有一个问题,对象的名称空间中没有函数属性,当然也是去类里面找,但是不同对象指定的函数,是一个函数吗

p1=Chinese('zhang',18)
p2=Chinese('li',19)
print(Chinese.talk)#<function Chinese.talk at 0x000001B8A5B7D1E0>
print(p1.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x000001B8A5B7BD68>>
print(p2.talk) #<bound method Chinese.talk of <__main__.Chinese object at 0x000001B8A5B7BDA0>>

可以看到,并不是,他们的内存地址都不一样。而且注意bound method,是绑定方法

对象本身只有数据属性,但是Python的class机制将类的函数也绑定到对象上,称为对象的方法,或者叫绑定方法。绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法。我们可以验证一下:

当用到这个函数时:类调用的是函数属性,既然是函数,就是函数名加括号,有参数传参数

而对象用到这个函数时,对象没有函数属性,他是绑定方法,绑定方法怎么用呢,也是直接加括号,但不同的是,绑定方法会默认把对象自己作为第一个参数

class Chinese:
 country='China'
 def __init__(self,name,age):
 self.name=name 
 self.age=age 
 def talk(self):
 print('%s is talking Chinese'%self.name)
 def eat(self):
 print('is eating Chinese food')

p1=Chinese('zhang',18)
p2=Chinese('li',19)
Chinese.talk(p1) #zhang is talking Chinese
p1.talk()  #zhang is talking Chinese

只要是绑定方法,就会自动传值!其实我们以前就接触过这个,在python3中,类型就是类。数据类型如list,tuple,set,dict这些,实际上也都是类,我们以前用的方法如l1.append(3),还可以这样写:l1.append(l1,3)

继承与派生

我们已经说过,Python中一切皆对象。我们从对象中抽取了共同特征和技能,得到了类的概念。类与类之间也有共同特征,我们可以从有共同特征和技能的类中提取共同的技能和特征,叫做父类。

比如老师和学生,都有名字,年纪,生日,性别等等,都会走,说话,吃饭。。。我们就可以从老师和学生中总结出来一个‘人'类,称为父类,那老师和学生就是‘人'类的子类,子类继承父类,就有了父类的特征和方法。

继承是一种什么‘是'什么的关系,继承是一种产生新类的方法,当然目的也是为了减少代码重用。

继承的基本形式是:

class People:
 pass
class Student(People):#People称为基类或者父类
 pass

1.在Python中支持多继承,一个子类可以继承多个父类

我们可以通过__bases__的方法查看继承的所有父类,会返回一个元组。 

class People:
 pass
class Animals:
 pass
class Student(People,Animals):
 pass

print(Student.__bases__)#(<class '__main__.People'>, <class '__main__.Animals'>)
print(People.__bases__)#(<class 'object'>,)

可以看到,在People父类中,默认也继承了一个object类,这就是新式类和经典类的区别:

凡是继承了object类的类及其子类,都称为新式类,没有继承object类的类,称为经典类。

在Python 3中,默认就是新式类,而在Python2.X中,默认都是是经典类

继承怎么减少代码呢?看例子

class People:
 def __init__(self,name,age):
 self.name=name
 self.age=age
 def walk(self):
 print('%s is walkig'%self.name)
class Teacher(People):
 def __init__(self,name,age,level):
 People.__init__(self,name,age)
 self.level=level
t1=Teacher('zhang',18,10)
print(t1.level) #10
print(t1.name) #zhang  子类可以用父类定义的属性
t1.walk() #zhang is walking 子类无需定义就可以用父类的方法
print(issubclass(Teacher,People)) #True查看Teacher类是不是People类的子类

从上面的例子中可以看到,Teacher类继承了父类People类,但是Teacher又有自己特有的属性level,子类也可以定义自己独有的方法,甚至可以和父类的方法重名,但是执行时会以子类定义的为准。

这就叫做派生

2.组合

继承是解决什么‘是'什么的问题,那还有一种场景就是什么有什么,比如老师有生日,学生也有生日,生日有年月日这些属性,如果每个类都写的话,又是重复代码。但是又不能让学生和老师继承生日类。这时就用到了组合。组合就是解决什么‘有'什么的问题。看例子

class Date:
 def __init__(self,year,mon,day):
 self.year=year
 self.mon=mon
 self.day=day
 def tell_birth(self):
 print('出生于%s年%s月%s日'%(self.year,self.mon,self.day))

class Teacher:
 def __init__(self,name,age,year,mon,day):
 self.name=name
 self.age=age
 self.birth=Date(year,mon,day)
t=Teacher('egon',19,2010,10,10)
print(t.birth)  #<__main__.Date object at 0x0000017E559380F0>
t.birth.tell_birth() #出生于2010年10月10日

什么?嫌参数太多?*args学过吧,你高兴就好

class Date:
 def __init__(self,year,mon,day):
 self.year=year
 self.mon=mon
 self.day=day
 def tell_birth(self):
 print('出生于%s年%s月%s日'%(self.year,self.mon,self.day))
class Teacher:
 def __init__(self,name,age,*args):
 self.name=name
 self.age=age
 self.birth=Date(*args)
t=Teacher('egon',19,2010,10,10)
print(t.birth)  #<__main__.Date object at 0x0000017E559380F0>
t.birth.tell_birth() #出生于2010年10月10日

3.抽象类与接口

继承有两种用途:

1.代码重用,子类继承父类的方法
2.声明某个子类兼容于某父类,定义一个接口类Interface,接口类中定义了一些接口名(就是函数名)且并未实现接口的功能,子类继承接口类,并且实现接口中的功能

需要注意的是,Python中并没有接口的关键字,我们只能是模仿接口的功能

比如在 Python中,一切皆文件嘛,那程序是文件,硬件是文件,文本文档也是文件,我们知道什么叫文件呢,就是能读能写,那程序,文本文档这些,都应该有读和写的功能,我们来模拟一下

class Interface:
 def read(self):
 pass
 def write(self):
 pass
class Txt(Interface):
 def read(self):
 print('文本文档的读取方式')
 def write(self):
 print('文本文档的写入方式')
class Sata(Interface):
 def read(self):
 print('硬盘文件的读取方式')
 def write(self):
 print('硬盘文件的写入方式')
class process(Interface):
 def read(self):
 print('进程数据的读取方式')
 def write(self):
 print('进程数据的写入方式')

这么做的意义就是:我们不需要知道子类有什么具体的方法,既然他们继承了文件类,那他们就是文件,那他们就有读和写这两个功能

父类限制了子类子类必须有read和write这两个方法,而且名字也必须一样(当然现在只是我们主观上的限制,一会我们说完抽象类,就可以从代码级别上限制了),这样就实现了统一,模拟了接口的概念,这就是归一化设计。在归一化设计中,只要是基于一个接口设计的类,那么所有的这些类实例化出来的对象,在用法上是一样的

我们再来说一下抽象类:

Python中的抽象类需要导入一个模块来实现。抽象类只能被继承,不能被实现

抽象类的写法:

import abc
class File(metaclass=abc.ABCMeta):
 @abc.abstractmethod
 def read(self):
 pass
 @abc.abstractmethod
 def write(self):
 pass
#父类使用了抽象类,那子类就必须继承父类的方法,而且名字也必须一样
#这样就实现了代码级别的限制

class Txt(File):
 def read(self):
 print('文本文档的读取方式')
 def write(self):
 print('文本文档的写入方式')

继承原理:

当我们定义一个类后,Python就会根据上面的继承规律解析出一个继承顺序的列表(MRO列表),可以通过mro()查看,但是这个方法只有在新式类中才有,经典类没有

super()方法

我们之前用继承是怎么用的来着,

class Parent(object):
 def __init__(self,name,age):
 self.name=name
 self.age=age

class Child(Parent):
 def __init__(self,name,age,salary):
 Parent.__init__(self,name,age,salary)
 self.salary=salary

这其实是和继承没啥关系的写法,如果父类名字改了,在子类中也要改,更优雅的写法是用super()

class Parent(object):
 def __init__(self,name,age):
 self.name=name
 self.age=age

class Child(Parent):
 def __init__(self,name,age,salary):
 super().__init__(name,age)
 self.salary=salary

这是python3中的写法,如果是python2,super后面的括号里要写(Child,self)

注意:super()方法只适用于新式类

如果是多继承的关系,就用到mro列表,如果就是要继承多个父类的方法,那就还是乖乖的用以前指名道姓的方法引用

看完这篇,可以继续参阅:

Python面向对象编程基础解析(二)

总结

以上就是本文关于Python面向对象编程基础解析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:Python探索之ModelForm代码详解、Python_LDA实现方法详解等,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
python 远程统计文件代码分享
May 14 Python
使用Python内置的模块与函数进行不同进制的数的转换
Mar 12 Python
深入解析Python中的上下文管理器
Jun 28 Python
Python时间获取及转换知识汇总
Jan 11 Python
详解Python 协程的详细用法使用和例子
Jun 15 Python
python读取txt文件,去掉空格计算每行长度的方法
Dec 20 Python
python命令行参数用法实例分析
Jun 25 Python
pycharm部署、配置anaconda环境的教程
Mar 24 Python
Python内建序列通用操作6种实现方法
Mar 26 Python
python上传时包含boundary时的解决方法
Apr 08 Python
解决python对齐错误的方法
Jul 16 Python
Django视图类型总结
Feb 17 Python
获取Django项目的全部url方法详解
Oct 26 #Python
Python探索之ModelForm代码详解
Oct 26 #Python
启动targetcli时遇到错误解决办法
Oct 26 #Python
Mac中Python 3环境下安装scrapy的方法教程
Oct 26 #Python
python实现分页效果
Oct 25 #Python
python+pyqt实现12306图片验证效果
Oct 25 #Python
python编程羊车门问题代码示例
Oct 25 #Python
You might like
火影忍者:这才是千手柱间和扉间的真正死因,角都就比较搞笑了!
2020/03/10 日漫
PHP中4种常用的抓取网络数据方法
2015/06/04 PHP
php中分页及SqlHelper类用法实例
2017/01/12 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
imagettftext() 失效,不起作用
2021/03/09 PHP
JS 自动完成 AutoComplete(Ajax 查询)
2009/07/07 Javascript
JS模板实现方法
2013/04/03 Javascript
js 控制页面跳转的5种方法
2013/09/09 Javascript
基于javascript编写简单日历
2016/05/02 Javascript
全面解析bootstrap格子布局
2016/05/22 Javascript
JavaScript编程中实现对象封装特性的实例讲解
2016/06/24 Javascript
js智能获取浏览器版本UA信息的方法
2016/08/08 Javascript
JQuery PHP图片在线裁剪实例
2020/07/27 Javascript
基于 webpack2 实现的多入口项目脚手架详解
2017/06/26 Javascript
jQuery层级选择器_动力节点节点Java学院整理
2017/07/04 jQuery
JavaScript实现的仿新浪微博原生态输入字数即时检查功能【兼容IE6】
2017/09/26 Javascript
基于 D3.js 绘制动态进度条的实例详解
2018/02/26 Javascript
利用Console来Debug的10个高级技巧汇总
2018/03/26 Javascript
vue+echarts实现动态绘制图表及异步加载数据的方法
2018/10/17 Javascript
微信小程序保持session会话的方法
2020/03/20 Javascript
原生JavaScript实现幻灯片效果
2021/02/19 Javascript
Python常用库推荐
2016/12/04 Python
python对象与json相互转换的方法
2019/05/07 Python
django项目中使用手机号登录的实例代码
2019/08/15 Python
python dataframe NaN处理方式
2019/12/26 Python
浅谈cookie和localStorage那些事
2019/08/27 HTML / CSS
罗马尼亚在线杂货店:Pilulka.ro
2019/09/28 全球购物
香港演唱会订票网站:StubHub香港
2019/10/10 全球购物
adidas菲律宾官网:adidas PH
2020/02/07 全球购物
什么是servlet
2012/05/08 面试题
2014年党的群众路线整改措施思想汇报
2014/10/12 职场文书
银行职员工作失误检讨书
2014/10/14 职场文书
机械生产实习心得体会
2016/01/22 职场文书
mongodb的安装和开机自启动详细讲解
2021/08/02 MongoDB
nginx负载功能+nfs服务器功能解析
2022/02/28 Servers
git中cherry-pick命令的使用教程
2022/06/25 Servers