第七章 再谈抽象

第七章 再谈抽象

对象魔法

多态:可对不同类型的对象执行相同的操作,而这些操作就像“被施了魔法”一样能够正常运行。(即:无需知道对象的内部细节就可使用它)(无需知道对象所属的类(对象的类型)就能调用其方法
封装:对外部隐藏有关对象工作原理的细节。(无需知道对象的构造就能使用它
继承:可基于通用类创建出专用类。

1,多态

即便你不知道变量指向的是哪种对象,也能够对其执行操作,且操作的行为将随对象所属的类型(类)而异。

2,多态和方法

与对象属性相关联的函数称为方法
如果有一个变量x,你无需知道它是字符串还是列表就能调用方法count:只要你向这个方法提供一个字符作为参数,它就能正常运行。count方法返回指定字符串出现的个数。

'abc'.count('a')#结果为:1
['a',1, 2, 'a','a'].count('a')#结果为:3

标准库模块random包含一个名为choice的函数,它从序列中随机选择一个元素。

from random import choice 
x = choice(['Hello, world!', [1, 2, 'e', 'e', 4]])#x可能包含字符串'Hello, world!',也可能包含列表[1, 2, 'e', 'e', 4]
x.count('e')#可能是1,也可能是2

多态形式多样。每当无需知道对象是什么样的就能对其执行操作时,都是多态在起作用。这不仅仅适用于方法,还可以通过内置运算符和函数使用多态。

参数可以是任何支持加法的对象
加法运算符(+)既可用于数(这里是整数),也可用于字符串(以及其他类型的序列)

1 + 2#结果为:3
'beyond' + 'huangjiaju'#结果为:'beyondhuangjiaju'

等价于下面函数:

def add(x, y): return x + yadd(1,2)#结果为:3
add('beyond','huangjiaju')#结果为:'beyondhuangjiaju'

编写一个函数,通过打印一条消息来指出对象的长度

def length_message(x): print("The length of", repr(x), "is", len(x))length_message('beyond')#结果为:The length of 'beyond' is 6
length_message([1, 2, 3])#结果为:The length of [1, 2, 3] is 3

要破坏多态,唯一的办法是使用诸如type、issubclass等函数显式地执行类型检查,但你应尽可能避免以这种方式破坏多态。

3,封装

封装(encapsulation)指的是向外部隐藏不必要的细节
属性是归属于对象的变量,就像方法一样。
对象有自己的状态。对象的状态由其属性(如名称)描述。
对象的方法可能修改这些属性,因此对象将一系列函数(方法)组合起来,并赋予它们访问一些变量(属性)的权限,而属性可用于在两次函数调用之间存储值。
多态让你无需知道对象所属的类(对象的类型)就能调用其方法
而封装让你无需知道对象的构造就能使用它。

4,继承

继承是另一种偷懒的方式。
如果你已经有了一个类,并要创建一个与之很像的类(可能只是新增了几个方法),可以让创建的这个类去继承已有的类。

类到底是什么?

类的定义——一种对象。
每个对象都属于特定的类,并被称为该类的实例。
对于类的名称,在Python中,约定使用单数并将首字母大写,如Bird和Lark。
类是由其支持的方法定义的,类的所有实例都有该类的所有方法,因此子类的所有实例都有超类的所有方法。

创建自定义类

对huangjiaju调用set_name和greet时,huangjiaju都会作为第一个参数自动传递给它们,这就是self。
self很有用,甚至必不可少。如果没有它,所有的方法都无法访问对象本身——要操作的属性所属的对象

class Band: def set_name(self, name): self.name = name def get_name(self): return self.name def greet(self): print("Hello! {} band.".format(self.name))huangjiaju = Band()
bar = Band()
huangjiaju.set_name('Beyond')
bar.set_name('The Beatles')huangjiaju.greet()#结果为:Hello! Beyond band.
bar.greet()#结果为:Hello! The Beatles band.huangjiaju.name#结果为:'Beyond'bar.name = 'The Rolling Stones'
bar.greet()#结果为:Hello! The Rolling Stones band.

属性、函数和方法

方法和函数的区别表现在于参数self
方法(更准确地说是关联的方法)将其第一个参数关联到它所属的实例,因此无需提供这个参数。

class Class:def method(self):print("I like beyond band!")def function():print("huangjiaju")instance = Class()
instance.method()#结果为:I like beyond band!instance.method = function
instance.method()#结果为:huangjiaju

有没有参数self并不取决于是否以刚才使用的方式(如instance.method)调用方法。

当然,也完全可以让另一个变量指向同一个方法。

class Band:songer = "huangjiaju"def sing(self):print(self.songer)beyond = Band()
beyond.sing()#结果为:huangjiajubeyondsonger = beyond.sing
beyondsonger()#结果为:huangjiaju

私有属性不能从对象外部访问,而只能通过存取器方法(如get_name和set_name)来访问。
Python没有为私有属性提供直接的支持,而要让方法或属性成为私有的(不能从外部访问),只需让其名称以两个下划线打头即可。

class Band:def __huangjiaju(self):print("beyond can't see")def huangjiaqiang(self):print("beyond can see")self.__huangjiaju()yy = Band()
yy.__huangjiaju()#报错!!!yy.huangjiaqiang()#结果为:
'''
beyond can see
beyond can't see
'''

从外部不能访问__huangjiaju,但在类中(如huangjiaqiang中)依然可以使用它。
以两个下划线打头,这样的方法类似于其他语言中的标准私有方法。
在类定义中,对所有以两个下划线打头的名称都进行转换,即在开头加上一个下划线和类名。

class Band:def __huangjiaju(self):print("beyond can't see")def huangjiaqiang(self):print("beyond can see")self.__huangjiaju()yy = Band()
yy._Band__huangjiaju()#结果为:beyond can't see

类的命名空间

以下两条语句大致等价:它们都创建一个返回参数平方的函数

def Hjj(x):return x*xHjq = lambda x:x*xHjj(8)#结果为:64
Hjq(9)#结果为:81

在class语句中定义的代码都是在一个特殊的命名空间(类的命名空间)内执行的,而类的所有成员都可访问这个命名空间。

class NumberCounter:number = 0def init(self):NumberCounter.number += 1m1 = NumberCounter()
m1.init()
NumberCounter.number#结果为:1m2 = NumberCounter()
m2.init()
NumberCounter.number#结果为:2
'''
上述代码在类作用域内定义了一个变量,所有的成员(实例)都可访问它,
这里使用它来计算类实例的数量,注意到这里使用了init来初始化所有实例!!!
'''#每个实例都可访问这个类作用域内的变量,就像方法一样
m1.number#结果为:1
m2.number#结果为:2#在一个实例中给属性number赋值
m1.number = "yy"
m1.number#结果为:'yy'
m2.number#结果为:2
#新值被写入m1的一个属性中,这个属性遮住了类级变量。

指定超类

子类扩展了超类的定义,要指定超类,可在class语句中的类名后加上超类名,并将其用圆括号括起。

class Filter:def init(self):self.blocked = []def filter(self,sequence):return [x for x in sequence if x not in self.blocked]class SPAMFilter(Filter):#SPAMFilter是Filter的子类def init(self): #重写超类Filter的方法initself.blocked = ['SPAM']#Filter是一个过滤序列的通用类。实际上,它不会过滤掉任何东西。
f = Filter()
f.init()
f.filter([1,2,3])#结果为:[1, 2, 3]#Filter类的用途在于可用作其他类(如将'SPAM'从序列中过滤掉的SPAMFilter类)的基类(超类)。
s = SPAMFilter()
s.init()
s.filter(['SPAM','SPAM','SPAM','SPAM','beyond','huangjiaju','SPAM'])#结果为:['beyond', 'huangjiaju']

请注意SPAMFilter类的定义中有两个要点。
Ⅰ以提供新定义的方式重写了Filter类中方法init的定义
Ⅱ直接从Filter类继承了方法filter的定义,因此无需重新编写其定义

深入探讨继承

要确定一个类是否是另一个类的子类,可使用内置方法issubclass
如果你有一个类,并想知道它的基类,可访问其特殊属性__bases__
要确定对象是否是特定类的实例,可使用isinstance
要获悉对象属于哪个类,可使用属性__class__,还可使用type(s)来获悉其所属的类。

class Filter:def init(self):self.blocked = []def filter(self,sequence):return [x for x in sequence if x not in self.blocked]class SPAMFilter(Filter):#SPAMFilter是Filter的子类def init(self): #重写超类Filter的方法initself.blocked = ['SPAM']issubclass(SPAMFilter, Filter)#结果为:True
issubclass(Filter, SPAMFilter)#结果为:FalseSPAMFilter.__bases__#结果为:(__main__.Filter,)
Filter.__bases__#结果为:(object,)s = SPAMFilter()
isinstance(s,SPAMFilter)#结果为:True
isinstance(s,Filter)#结果为:True
isinstance(s,str)#结果为:Falses.__class__#结果为:__main__.SPAMFilter
type(s)#结果为:__main__.SPAMFilter

多个超类

子类TalkingCalculator本身无所作为,其所有的行为都是从超类那里继承的。关键是通过从Calculator那里继承calculate,并从Talker那里继承talk。这被称为多重继承,是一个功能强大的工具。除非万不得已,否则应避免使用多重继承,因为在有些情况下,它可能带来意外的“并发症”。

class Calculator:def calulate(self,expression):self.value = eval(expression)class Talker:def talk(self):print('Hi,my value is',self.value)class TalkingCalulator(Calculator,Talker):passyy = TalkingCalulator()
yy.calulate('5+2+1*1314')
yy.talk()#结果为:Hi,my value is 1321

多个超类的超类相同时,查找特定方法或属性时访问超类的顺序称为方法解析顺序(MRO),它使用的算法非常复杂。

接口和内省

接口这一概念与多态相关。处理多态对象时,你只关心其接口(协议)——对外暴露的方法和属性
检查所需的方法是否存在hasattr(对象名,方法名)
检查属性是否是可调用的 callable(getattr(对象名, '方法名', None))
getattr(它让我能够指定属性不存在时使用的默认值,这里为None),然后对返回的对象调用callable
setattrgetattr功能相反,可用于设置对象的属性
要查看对象中存储的所有值,可检查其__dict__属性

class Calculator:def calulate(self,expression):self.value = eval(expression)class Talker:def talk(self):print('Hi,my value is',self.value)class TalkingCalulator(Calculator,Talker):passyy = TalkingCalulator()
yy.calulate('5+2+1*1314')
yy.talk()#结果为:Hi,my value is 1321hasattr(yy, 'talk')#结果为:True
hasattr(yy, 'hahaha')#结果为:Falsecallable(getattr(yy, 'talk', None))#结果为:True
callable(getattr(yy, 'hahah', None))#结果为:Falsesetattr(yy, 'name', 'huangjiaju')
yy.name#结果为:'huangjiaju'yy.__dict__#结果为:{'name': 'huangjiaju', 'value': 1321}

抽象基类

Python几乎都只依赖于鸭子类型,即假设所有对象都能完成其工作,同时偶尔使用hasattr来检查所需的方法是否存在。
很多其他语言(如Java和Go)都采用显式指定接口的理念,而有些第三方模块提供了这种理念的各种实现。

Python通过引入模块abc提供了官方解决方案。这个模块为所谓的抽象基类提供了支持。标准库(如模块collections.abc)提供了多个很有用的抽象类,有关模块abc的详细信息。
抽象类是不能(至少是不应该)实例化的类,其职责是定义子类应实现的一组抽象方法

from abc import ABC,abstractmethod
class Talker(ABC):#形如@this的东西被称为装饰器@abstractmethod#使用@abstractmethod来将方法标记为抽象的——在子类中必须实现的方法。def talk(self):pass'''抽象类(即包含抽象方法的类)最重要的特征是不能实例化'''
Talker()#报错!!!'''
从它派生出一个子类,由于没有重写方法talk,因此这个类也是抽象的,不能实例化。
现在实例化它没有任何问题。这是抽象基类的主要用途,而且只有在这种情形下使用isinstance才是妥当的:
如果先检查给定的实例确实是Talker对象,就能相信这个实例在需要的情况下有方法talk。
'''
class Knigger(Talker):def talk(self):print("Ni!")k = Knigger()
isinstance(k,Talker)#结果为:True
k.talk()#结果为:Ni!'''
然而,还缺少一个重要的部分——让isinstance的多态程度更高的部分。
来创建另一个类,这个类的实例能够通过是否为Talker对象的检查,可它并不是Talker对象。
可从Talker派生出Herring,但Herring可能是从他人的模块中导入的。
在这种情况下,就无法采取这样的做法。
'''
class Herring:def talk(self):print("beyond.")h = Herring()
isinstance(h,Talker)#结果为:False'''
可将Herring注册为Talker(而不从Herring和Talker派生出子类),
这样所有的Herring对象都将被视为Talker对象。
'''
Talker.register(Herring)#结果为:__main__.Herring
isinstance(h,Talker)#结果为:True
issubclass(Herring,Talker)#结果为:True#上述做法存在一个缺点,就是直接从抽象类派生提供的保障没有了。class Clam:passTalker.register(Clam)#结果为:__main__.Clamissubclass(Clam,Talker)#结果为:Truec = Clam()
isinstance(c,Talker)#结果为:Truec.talk()#报错!!!
#Clam有成为Talker的意图,相信它能承担Talker的职责,但可悲的是它失败了。

关于面向对象设计的一些思考

将相关的东西放在一起。如果一个函数操作一个全局变量,最好将它们作为一个类的属性和方法。
不要让对象之间过于亲密。方法应只关心其所属实例的属性,对于其他实例的状态,让它们自己去管理就好了。
慎用继承,尤其是多重继承。继承有时很有用,但在有些情况下可能带来不必要的复杂性。要正确地使用多重继承很难,要排除其中的bug更难。
保持简单。让方法短小紧凑。一般而言,应确保大多数方法都能在30秒内读完并理解。对于其余的方法,尽可能将其篇幅控制在一页或一屏内。

小结

概念解释
对象对象由属性和方法组成。属性不过是属于对象的变量,而方法是存储在属性中的函数。相比于其他函数,(关联的)方法有一个不同之处,那就是它总是将其所属的对象作为第一个参数,而这个参数通常被命名为self。
类表示一组(或一类)对象,而每个对象都属于特定的类。类的主要任务是定义其实例将包含的方法。
多态多态指的是能够同样地对待不同类型和类的对象,即无需知道对象属于哪个类就可调用其方法。
封装对象可能隐藏(封装)其内部状态。在有些语言中,这意味着对象的状态(属性)只能通过其方法来访问。在Python中,所有的属性都是公有的,但直接访问对象的状态时程序员应谨慎行事,因为这可能在不经意间导致状态不一致。
继承一个类可以是一个或多个类的子类,在这种情况下,子类将继承超类的所有方法。你可指定多个超类,通过这样做可组合正交(独立且不相关)的功能。为此,一种常见的做法是使用一个核心超类以及一个或多个混合超类。
接口和内省一般而言,你无需过于深入地研究对象,而只依赖于多态来调用所需的方法。然而,如果要确定对象包含哪些方法或属性,有一些函数可供你用来完成这种工作。
抽象基类使用模块abc可创建抽象基类。抽象基类用于指定子类必须提供哪些功能,却不实现这些功能。
面向对象设计关于该如何进行面向对象设计以及是否该采用面向对象设计,有很多不同的观点。无论你持什么样的观点,都必须深入理解问题,进而创建出易于理解的设计。

本章节介绍的新函数

函数描述
callable(object)判断对象是否是可调用的(如是否是函数或方法)
getattr(object,name[,default])获取属性的值,还可提供默认值
hasattr(object, name)确定对象是否有指定的属性
isinstance(object, class)确定对象是否是指定类的实例
issubclass(A, B)确定A是否是B的子类
random.choice(sequence)从一个非空序列中随机地选择一个元素
setattr(object, name, value)将对象的指定属性设置为指定的值
type(object)返回对象的类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378338.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c语言math乘法,JavaScript用Math.imul()方法进行整数相乘

1. 基本概念Math.imul()方法用于计算两个32位整数的乘积,它的结果也是32位的整数。JavaScript的Number类型同时包含了整数和浮点数,它没有专门的整型和浮点型。因此,Math.imul()方法能提供类似C语言的整数相乘的功能。我们将Math.imul()方法的…

java scanner_Java Scanner nextLong()方法与示例

java scanner扫描器类的nextLong()方法 (Scanner Class nextLong() method) Syntax: 句法: public long nextLong();public long nextLong(int rad);nextLong() method is available in java.util package. nextLong()方法在java.util包中可用。 nextLong() method…

技术总监和CTO的区别 浅谈CTO的作用----软件公司如何开源节流(一)

我一直在思考软件公司如何开源节流。当然,老板也在思考开源节流。当然,老板思考的开源节流在公司运营层面上,而我作为CTO,我考虑的则是在产品运营角度上来思考这个问题。否则,一个软件公司,它的生存与发展就…

梯度下降法预测波士顿房价以及简单的模型评估

目录原理代码关于归一化的思考原理 观察数据可知属性之间差距很大,为了平衡所有的属性对模型参数的影响,首先进行归一化处理。 每一行是一个记录,每一列是个属性,所以对每一列进行归一化。 二维数组归一化:1、循环方式…

Windows Phone 内容滑动切换实现

在新闻类的APP中,有一个经常使用的场景:左右滑动屏幕来切换上一条或下一条新闻。 那么通常我们该使用哪种方式去实现呢?可以参考一下Demo的实现步骤。 1,添加Windows Phone用户自定义控件。例如: 这里我为了演示的方便…

c语言interrupt函数,中断处理函数数组interrupt[]初始化

在系统初始化期间,trap_init()函数将对中断描述符表IDT进行第二次初始化(第一次只是建一张IDT表,让其指向ignore_intr函数),而在这次初始化期间,系统的0~19号中断(用于分NMI和异常的中断向量)均被设置好。与此同时,用于…

bytevalue_Java Number byteValue()方法与示例

bytevalueNumber类byteValue()方法 (Number Class byteValue() method) byteValue() method is available in java.lang package. byteValue()方法在java.lang包中可用。 byteValue() method is used to return the value denoted by this Number object converted to type byt…

第二章 染色热力学理论单元测验

1,()测定是染色热力学性能研究的基础 吸附等温线。 2,吸附是放热反应,温度升高,亲和力() 减小 3,染色系统中包括() 染料。 染深色介质。 染色助剂。 纤维。 4,下列对状态函数特点叙述正确的为() 状态函数只有在平衡状态的系统中才有确定值。 在非平衡状态的系统…

使用鸢尾花数据集实现一元逻辑回归、多分类问题

目录鸢尾花数据集逻辑回归原理【1】从线性回归到广义线性回归【2】逻辑回归【3】损失函数【4】总结TensorFlow实现一元逻辑回归多分类问题原理独热编码多分类的模型参数损失函数CCETensorFlow实现多分类问题独热编码计算准确率计算交叉熵损失函数使用花瓣长度、花瓣宽度将三种鸢…

开源HTML5应用开发框架 - iio Engine

随着HTML5的发展,越来越多的基于HTML5技术的网页开发框架出现,在今天的这篇文章中,我们将介绍iio Engine,它是一款开源的创建HTML5应用的web框架。整个框架非常的轻量级,只有45kb大小,并且整合了debug系统&…

c语言double root,C语言修仙

root(1)(2/2)AD1AD4林浔合理推测,青城山剑宗,也就是祁云所在的剑修一脉,掌握着一些道修并不知道的传承。譬如——怎样找到赤霄龙雀剑,又或者,怎样使用它。这样一来,青城的守卫阵法没有反应也能解释了&#…

【转】Black Box

Introduction BlackBox是FPGA设计中一个重要的技巧,不过觉得Xilinx的文档没有很好地将它讲清楚。 BlackBox的主要想法就是把设计的某一个子模块单独综合,综合的结果作为一个黑盒子子模块,上层设计不再对这个模块进行优化,只能看到…

Java Compiler disable()方法与示例

编译器类disable()方法 (Compiler Class disable() method) disable() method is available in java.lang package. disable()方法在java.lang包中可用。 disable() method is used to cause the compiler to stop operation. disable()方法用于使编译器停止操作。 disable() m…

【神经网络计算】——神经网络实现鸢尾花分类

本blog为观看MOOC视频与网易云课堂所做的笔记 课堂链接: 人工智能实践:TensorFlow笔记 吴恩达机器学习 疑问与思考 为什么按照batch喂入数据 之前看的视频里面处理数据都是一次性将所有数据喂入,现在看的这个视频对数据进行了分组投入。这是为何&#…

第三章 染色动力学理论单元测试

1,准二级动力学模型认为,染色速率与()的二次方成正比 纤维上未被占满的位置(空位)数量 2,研究染色动力学的意义有() 了解染料走向平衡的速率。 初染速率。 匀染性。 3,求出染料的扩散系数的意义有() 了解各因素对扩散系数的影响。 求出不同温度下的扩散系数,计算…

CDOJ--1668

原题链接:http://acm.uestc.edu.cn/problem.php?pid1668 由于题目意思指的是将分数拆分成不同的单位分数之和,所以就不用考虑将2/3拆成1/31/3这种情况了;又由于好的拆分要求项数即len要少,最小的项要大,故可以采用迭代…

c# xaml语言教程,c#学习之30分钟学会XAML

1.狂妄的WPF相对传统的Windows图形编程,需要做很多复杂的工作,引用许多不同的API。例如:WinForm(带控件表单)、GDI(2D图形)、DirectXAPI(3D图形)以及流媒体和流文档等,都需要不同的API来构建应用程序。WPF就是看着上面的操作复杂和…

(Android实战)AsyncTask和Handler两种异步方式实现原理和优缺点比较

1 AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程. 使用的优点: l 简单,快捷 l 过程可控 使用的缺点…

Java Collections list()方法与示例

集合类list()方法 (Collections Class list() method) list() method is available in java.util package. list()方法在java.util包中可用。 list() method is used to return an array list that contains all the elements returned by the given Enumeration and the way o…

第八章 异常

第八章 异常 异常事件可能是错误(如试图除以零),也可能是通常不会发生的事情。 Python提供功能强大的替代解决方案——异常处理机制。 异常是什么? Python使用异常对象来表示异常状态,并在遇到错误时引发异常。异常…