Python面向对象类编写细节分析【类,方法,继承,超类,接口等】


Posted in Python onJanuary 05, 2019

本文实例讲述了Python面向对象类编写技术细节。分享给大家供大家参考,具体如下:

类代码编写细节

继续学习类、方法和继承。

class语句

以下是class语句的一般形式:

class <name>(superclass,...):
  data = value
  def method(self,...):
    self.member = value

在class语句内,任何赋值语句都会产生类属性,而且还有特殊名称方法重载运算符。例如,名为__init__的函数会在实例对象构造时调用(如果定义过的话)。

例子

类是命名空间,也就是定义变量名(属性)的工具。

1.就像函数一样,class语句是本地作用域,由内嵌的赋值语句建立的变量名,就存在于这个本地作用域内。

2.就像模块内的变量名,在class语句内赋值的变量名会变成类对象中的属性。

因为class是复合语句,所以任何种类的语句都可位于其主体内:print、=、if、def等。当class语句自身执行时,class语句内的所有语句都会执行。在class语句内赋值的变量名,会创建类属性,而内嵌的def则会创建类方法。

例如,把简单的非函数的对象赋值给类属性,就会产生数据属性,由所有实例共享。

>>> class ShareData:
  spam = 42
>>> x = ShareData()
>>> y = ShareData()
>>> x.spam,y.spam
(42, 42)

在这里,因为变量名spam是在class语句的顶层进行赋值的,因此会附加在这个类中,从而为所有的实例共享。我们可通过类名称修改它,或者是通过实例或类引用它。

>>> ShareData.spam = 99
>>> x.spam,y.spam,ShareData.spam
(99, 99, 99)

这种类属性可以用于管理贯穿所有实例的信息。例如,所产生的实例的数目的计数器。

现在,如果通过实例而不是类来给变量名spam赋值时,看看会发生什么:

>>> x.spam = 88
>>> x.spam,y.spam,ShareData.spam
(88, 99, 99)

对实例的属性进行赋值运算会在该实例内创建或修改变量名,而不是在共享的类中。

对对象属性进行赋值总是会修改该对象,除此之外没有其他的影响。例如,y.spam会通过继承而在类中查找,但是,对x.spam进行赋值运算则会把该变量名附加在x本身上。

看下面这个例子,可以更容易理解这种行为,把相同的变量名储存在两个位置:

>>> class MixedNames:
  data = 'spam'
  def __init__(self,value):
    self.data = value
  def display(self):
    print(self.data,MixedNames.data)

当创建这个类的实例的时候,变量名data会在构造函数方法内对self.data进行赋值运算,从而把data附加到这些实例上。

>>> x = MixedNames(1)
>>> y = MixedNames(2)
>>> x.display(),y.display()
1 spam
2 spam
(None, None)

【这里的(None,None)是调用display函数的返回值】

结果就是,data存在于两个地方:在实例对象内(由__init__中的self.data赋值运算所创建)以及在实例继承变量名的类中(由类中的data赋值运算所创建)。类的display方法打印了这两个版本,先以点号运算得到self实例的属性,然后才是类。

利用这些技术把属性储存在不同对象内,我们可以决定其可见范围。附加在类上时,变量名是共享的;附加在实例上时,变量名是属于每个实例的数据,而不是共享的数据。

方法

方法即函数。方法在class中是由def语句创建的函数对象。从抽象的角度来看,方法替实例对象提供了要继承的行为。从程序的角度看,方法与简单函数的工作方式完全一致,只是有一个重要的差别:方法的第一个参数总是接收方法调用的隐性主体,也就是实例对象。

Python会自动把实例方法的调用对应到类方法函数。如下所示,方法调用需要通过实例,就像这样:

instance.method(args...)

这会自动翻译成以下形式的类方法函数调用:

class.method(instance,args...)

class通过Python继承搜索流程找出方法名称所在之处。事实上,两种调用形式在Python中都有效。

在类方法中,按惯例第一个参数通常都称为self(严格来说,只有其位置重要,而不是它的名称)。这个参数给方法提供了一个钩子,从而返回调用的主体,也就是实例对象:因为类可以产生许多实例对象,所以需要这个参数来惯例每个实例彼此各不相同的数据。

例子

定义下面这个类:

>>> class NextClass:
  def printer(self,text):
    self.message = text
    print(self.message)

我们通过实例调用printer方法如下:

>>> x = NextClass()
>>> x.printer('instance call')
instance call
>>> x.message
'instance call'

当通过实例进行点号运算调用它时,printer会先通过继承将其定位,然后它的self参数会自动赋值为实例对象(x)。text参数会获得在调用时传入的字符串('instance call')。注意:因为Python会自动传递第一个参数给self,实际上只需要传递一个参数。在printer中,变量名self是用于读取或设置每个实例的数据的,因为self引用的是当前正在处理的实例。

方法能通过实例或类本身两种方法其中的任意一种进行调用。例如,我们也可以通过类的名称调用printer,只要明确地传递了一个实例给self参数。

>>> NextClass.printer(x,'class call')#Direct Class Call
class call
>>> x.message
'class call'

通过实例和类的调用具有相同的效果,只要在类形式中传递了相同的实例对象。实际上,在默认情况下,如果尝试不带任何实例调用的方法时,就会得到出错信息。

>>> NextClass.printer('bad call')
Traceback (most recent call last):
 File "<pyshell#35>", line 1, in <module>
  NextClass.printer('bad call')
TypeError: printer() missing 1 required positional argument: 'text'

调用超类构造函数

在构造时,Python会找出并且只调用一个__init__。如果保证子类的构造函数也会执行超类构造时的逻辑,一般都必须通过类明确地调用超类的__init__方法。

class Super:
  def __init__(self,x):
    ...default code...
class Sub(Super):
  def __init__(self,x,y):
    Super.__init__(self,x)
    ...custom code...
I = Sub(1,2)

这种写法便于维护代码,之前也介绍过。这种方法扩展了超类的方法,而不是完全取代了它。

类接口技术

扩展只是一种与超类接口的方法。下面所示的specialize.py文件定义了多个类,示范了一些常用技巧。

Super:定义一个method函数以及在子类中期待一个动作的delegate。
Inheritor:没有提供任何新的变量名,因此会获得Super中定义的一切内容。
Replacer:用自己的版本覆盖Super的method
Extender:覆盖并回调默认method,从而定制Super的method
Provider:实现Super的delegate方法预期的action方法。

下面是这个文件:

class Super:
  def method(self):
    print('in Super.methon')
  def delegate(self):
    self.action()
class Inheritor(Super):
  pass
class Replacer(Super):
  def method(self):
    print('in Replacer.method')
class Extender(Super):
  def method(self):
    print('starting Extender.method')
    Super.method(self)
    print('ending Extender.method')
class Provider(Super):
  def action(self):
    print('in Provider.action')
if __name__=='__main__':
  for klass in (Inheritor,Replacer,Extender):
    print('\n'+klass.__name__+'...')
    klass().method()
  print('\nProvider...')
  x = Provider()
  x.delegate()

执行结果如下:

Inheritor...
in Super.methon
Replacer...
in Replacer.method
Extender...
starting Extender.method
in Super.methon
ending Extender.method
Provider...
in Provider.action

抽象超类

注意上例中的Provider类是如何工作的。当通过Provider实例调用delegate方法时,有两个独立的继承搜索会发生:

1.在最初的x.delegate的调用中,Python会搜索Provider实例和它上层的对象,直到在Super中找到delegate的方法。实例x会像往常一样传递给这个方法的self参数。

2.在Super.delegate方法中,self.action会对self以及它上层的对象启动新的独立继承搜索。因为self指的是Provider实例,在Provider子类中就会找到action方法。

这种“填空式”的代码结构一般就是OOP的软件框架。这个例子中的超类有时也称作是抽象超类——也就是类的部分行为默认是由其子类所提供的。如果预期的方法没有在子类中定义,当继承搜索失败时,Python会引发未定义变量名的异常。

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python实现的简单发送邮件脚本分享
Nov 07 Python
进一步理解Python中的函数编程
Apr 13 Python
Python压缩解压缩zip文件及破解zip文件密码的方法
Nov 04 Python
Python 自动化表单提交实例代码
Jun 08 Python
Python使用win32com实现的模拟浏览器功能示例
Jul 13 Python
使用python读取txt文件的内容,并删除重复的行数方法
Apr 18 Python
Python爬虫实现抓取京东店铺信息及下载图片功能示例
Aug 07 Python
详解python-图像处理(映射变换)
Mar 22 Python
python 生成任意形状的凸包图代码
Apr 16 Python
关于matplotlib-legend 位置属性 loc 使用说明
May 16 Python
python 实现德洛内三角剖分的操作
Apr 22 Python
宝塔更新Python及Flask项目的部署
Apr 11 Python
Python面向对象程序设计OOP深入分析【构造函数,组合类,工具类等】
Jan 05 #Python
Python面向对象程序设计OOP入门教程【类,实例,继承,重载等】
Jan 05 #Python
Python3爬虫全国地址信息
Jan 05 #Python
Python图像处理之图像的读取、显示与保存操作【测试可用】
Jan 04 #Python
Python图像处理之图像的缩放、旋转与翻转实现方法示例
Jan 04 #Python
Python图像处理实现两幅图像合成一幅图像的方法【测试可用】
Jan 04 #Python
Python小游戏之300行代码实现俄罗斯方块
Jan 04 #Python
You might like
备份mysql数据库的php代码(一个表一个文件)
2010/05/28 PHP
php中使用接口实现工厂设计模式的代码
2012/06/17 PHP
php随机显示图片的简单示例
2014/02/15 PHP
php验证邮箱和ip地址最简单方法汇总
2015/10/30 PHP
Yii2.0使用阿里云OSS的SDK上传图片、下载、删除图片示例
2017/09/20 PHP
laravel ORM 只开启created_at的几种方法总结
2018/01/29 PHP
基于jquery的一个OutlookBar类,动态创建导航条
2010/11/19 Javascript
你必须知道的Javascript知识点之&quot;this指针&quot;的应用
2013/04/23 Javascript
JavaScript中的定时器之Item23的合理使用
2015/10/30 Javascript
jQuery实现选项联动轮播效果【附实例】
2016/04/19 Javascript
JS组件Bootstrap实现下拉菜单效果代码
2016/04/26 Javascript
JS中append字符串包含onclick无效传递参数失败的解决方案
2016/12/26 Javascript
vue如何获取点击事件源的方法
2017/08/10 Javascript
利用js编写网页进度条效果
2017/10/08 Javascript
RequireJS用法简单示例
2018/08/20 Javascript
JavaScript继承的特性与实践应用深入详解
2018/12/30 Javascript
解决IOS端微信H5页面软键盘弹起后页面下方留白的问题
2019/06/05 Javascript
jQuery实现动态操作table行
2020/11/23 jQuery
[06:33]DOTA2亚洲邀请赛小组赛第二日 TOP10精彩集锦
2015/01/31 DOTA
Python编程之微信推送模板消息功能示例
2017/08/21 Python
简单了解python代码优化小技巧
2019/07/08 Python
django 配置阿里云OSS存储media文件的例子
2019/08/20 Python
python 3.74 运行import numpy as np 报错lib\site-packages\numpy\__init__.py
2019/10/06 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
简单了解pytest测试框架setup和tearDown
2020/04/14 Python
Python通过Schema实现数据验证方式
2020/11/12 Python
美国波道夫·古德曼百货官网:Bergdorf Goodman
2017/11/07 全球购物
PatPat德国:妈妈的每日优惠
2019/10/02 全球购物
大学生英语演讲稿
2014/04/24 职场文书
家长给学校的建议书
2014/05/15 职场文书
2014审计局领导班子民主生活会对照检查材料思想汇报
2014/09/20 职场文书
单位委托书格式范本
2014/09/29 职场文书
感谢信怎么写
2015/01/21 职场文书
幼儿园教师教育随笔
2015/08/14 职场文书
创业计划书之淘宝网店
2019/10/08 职场文书
go语言map与string的相互转换的实现
2021/04/07 Golang