python亲密度

编程入门 行业动态 更新时间:2024-10-11 00:29:20

python亲<a href=https://www.elefans.com/category/jswz/34/1769071.html style=密度"/>

python亲密度

1、对象魔法

在面对对象编程中,术语对象大致意味着一系列数据(属性)以及一套访问和操作这些数据的方法。

使用对象而非全局变量以及函数的原因有多个,而最重要的好处不过以下几点:

多态:可对不同类型的对象执行相同的操作,而这些操作全部能够正常运行。

封装:对外部隐藏有关对象工作的具体细节。

继承:可基于通用类创建专用类。

1.1多态

术语多态源于希腊语,意思是有多种形态,这大致意味着即使你不知道变量指向的是哪个对象,也能够对其执行操作,且操作的行为随着对象所属的类型(类)而异。

1.2多态和方法

>>> 'abc'.count('a')1

>>> [1,2,3,'a','a'].count('a')2

下面我们做一个实验:

模块random中有一个函数random,它从序列中随机选择一个元素。

>>> from random importchoice>>> x=choice(['Hello','Python',[1,2,3,4,'e','e',]])>>> x.count('e')1

执行这些代码后,你不知道x到底包含什么,你只关心x包含了多少个'e'。从结果看,x应该包含的是  'Hello'  。但关键是你无需执行相关的检查,只要x有一个名为count的方法,他将单个字符作为参数并返回一个整数就行了。如果有人创建了包含这个方法的对象,你也可以像使用字符串一样使用这个对象。

好吧!上面这句话并不太好理解!!!!

多态形式多样:

多态不仅仅适用于方法,我们还通过内置运算符和函数大量使用多态。

>>> defadd(x,y):return x+y>>> add(1,2)3

>>> add('jiamemg','is so cool!')'jiamemg is so cool!'

>>> 1+2

3

>>> 'jiameng'+'is so cool!'

'jiameng is so cool!'

#加法运算符不仅可以用于数,也可以用于字符串

>>> deflength_message(x):print('the length of',repr(x),'is',len(x))#如你所见,这里使用了repr函数,repr函数是多态的集大成者之一,可用于任何对象

>>> length_message('jiameng')

the length of'jiameng' is 7

>>> length_message([1,2,3,4,5,6,7,8,9])

the length of [1, 2, 3, 4, 5, 6, 7, 8, 9] is 9

#如你所见,这个函数也是支持多态的,虽然你编写的时候可能不没有意识到这一点

很多函数和运算符都是多态的,你编写的大部分函数也可能如此,即使你不是有意为之。每当你使用多态的函数和运算符时,多态都将发挥作用。事实上,要想破坏多态,除非使用诸如type、issubclass等函数显式地执行类型检查,但你应该尽可能避免这样破坏多态。本章后面将会学到抽象基类和模块abc后,函数issubclass本身也是多态的。

1.3封装

封装指的是向外部隐藏不必要的细节。这听起来有点像多态。这两个概念很像,因为它们都是抽象的原则。

但是封装不同于多态。多态让你无需知道对象所属的类(对象的类型)就能调用其方法,而封装让你无需知道对象的构造就能使用它。下面看一下一个使用多态而没有使用封装的示例。假装你有一个名为OpenObject 的类

1.4继承

继承是另一种(偷懒)的方式。因为已经学过JAVA有着相关基础,此处简介。

你已经有了一个类,现在你要创建一个新类,这两个类功能很相似,甚至需要部分的相同代码,你总不至于去copy代码,你只需要继承即可。你只需要让新类去继承老类方法即可,当你用新类对象去调用这个继承来的方法时,将自动调用老类的这个方法。

具体如何继承,将在后续实例中展示。

2、类

2.1什么是类

这一章节总是在提到类,并将其用作类型的同义词,那么到底什么是类哪?从很多方面来说,这正是类的定义——一种对象。每个对象都属于特定的类,并被称为这个类的实例。

举个例子:鸟类、云雀。云雀是鸟类的子类,鸟类是云雀的超类。

在面向对象编程中,子类关系意味深长,因为类是由其支持的方法定义的。类的所有实例都有该类的所有的方法,因此子类的所有实例都能实现超类的所有方法。因此,要定义子类,只需要定义多出来的方法(或者重写的一些方法)即可。

2.2创建自定义类

终于,终于要自定义类了!!!!!太高兴了!!!!

>>> classperson:defset_name(self ,name ):

self.name=name;defget_name(self):returnself.namedefgrett(self):print("Hello,Python!I'm {}.".format(self.name))#关键来了,关键来了,虽然这个示例很简单,但是它说清了self是什么!

>>> foo=person()>>> bar=person()>>> foo.set_name('jiameng')>>> bar.set_name('wangweili')>>>foo.grett()

Hello,Python!I'm jiameng.

>>>bar.grett()

Hello,Python!I'm wangweili.

#对foo调用set_name和grett时,foo都会作为第一个参数自动传递给它们。我们将这个参数命名为self。#显然self很有用,如果没有它,所有的方法都无法访问对象本身——要操作的属性所属的对象#与以前一样,也可以从外部访问他们

>>>foo.name'jiameng'

>>>bar.name'wangweili'

2.3属性、函数和方法

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

无疑可以将属性关联到一个普通函数,但这样就没有什么特殊的self参数了。

>>> classClass:defmethod(self):print('I have a self')>>> deffunction():print("I don't have")>>> instance=Class()>>>instance.method()

I have a self>>> instance.method=function>>>instance.method()

I don't have

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

实际上,你也可以让另一个参数指向同一个方法:

>>> bird=Bird()>>>bird.sing()

Squaawk!>>> birdsong=bird.sing>>>birdsong()

Squaawk!#虽然最后一种方法调用看起来像是函数调用,单变量birdsong指向的是关联的方法bird.sing,这意味着它也能够访问参数self(即它也能够被关联到类的实例)

2.4再谈隐藏

默认情况下,可以从外部访问对象的属性。

有人认为违反了封装原则,认为应该对外部完全隐藏对象的状态(即不能从外部访问它们)。

Python没有为私有属性提供直接的支持,而是要求程序员知道在什么情况下从外部修改属性是安全的。毕竟,你只有知道如何使用对象之后才能使用它。

要想方法或者属性成为私有的(无法从外部访问),只需要让其名称以两个下划线打头即可:

>>> classSecretive:def __inaccessible(self):print("Bet you can't see me ...")defaccessible(self):print("The secret message is ...")

self.__inaccessible()>>> s=Secretive()>>> s.__inaccessible()

Traceback (most recent call last):

File"", line 1, in s.__inaccessible()

AttributeError:'Secretive' object has no attribute '__inaccessible'

>>>s.accessible()

The secret messageis...

Bet you can't see me ...

#虽然以两个下划线打头有点怪异,但这样的方法类似于其它语言中的标准私有方法。

然而,幕后的处理方法却并不标准:在类定义中,对所有以两个下划线打头的名称都进行替换,即在开头加上一个下划线和类名。

>>>Secretive._Secretive__inaccessible

#只要知道这种幕后处理方式之后,你就可以从外部继续访问了,但你不应该这样做。

>>>s._Secretive__inaccessible()

Bet you can't see me ...

#总之,你无法阻止别人访问对象的私有方法和属性,你只是以这种方式告诉他们不要这么做.....

2.5类的命名空间

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

>>> classMemberCounter:

members=0definit(self):

MemberCounter.members+=1

>>> m1=MemberCounter()>>>m1.init()>>>MemberCounter.members1

>>> m2=MemberCounter()>>>m2.init()>>>MemberCounter.members2

每个实例都可以访问这个类作用域内的变量,就像方法一样。

但如果在实例中给属性赋值

>>> m1.members='AAA'

>>>m1.members'AAA'

>>>m2.members2

新值被写入m1的一个属性中,这个属性遮住了类级变量。这类似于前边讨论的局部变量与全局变量的关系。

2.6指定超类

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

>>> classFilter:definit(self):

self.blocked=[]deffiliter(self,sequence):return [x for x in sequence if x not inself.blocked ]>>> classFilter:definit(self):

self.blocked=[]deffilter(self,sequence):return [x for x in sequence if x not inself.blocked ]>>> classSPAMFilter(Filter):def init(self):#重写方法

self.blocked=['SPAM']>>> f=Filter()>>>f.init()>>> f.filter([1,2,3,4,5,6])

[1, 2, 3, 4, 5, 6]>>> s=SPAMFilter()>>>s.init()>>> s.filter(['SPAM','SPAM','jiameng','wangweili'])

['jiameng', 'wangweili']

2.7深入探讨继承

要确定一个类是不是另一个类的子类,可以使用内置方法:

>>>issubclass(SPAMFilter,Filter)

True>>>issubclass(Filter,SPAMFilter)

False

如果以有一个类,想知道它的基类可以访问它的特殊属性:__bases__

>>> SPAMFilter.__bases__(,)

同样要确定对象是不是特定类的实例,可以使用isinstance

>>>isinstance(s,SPAMFilter)

True>>>isinstance(s,Filter)

True#如你所见,s也是Filter的实例

如果你要获悉对象属于哪个类,可以使用属性__class__

>>> s.__class__

>>>type(s)

#对于新式类(无论是通过使用 __mataclass__=type还是通过object继承创建的)的实例,还可以使用type(s)来获悉其所属的类。对于旧式类的实例,type都是返回instance

2.8多个超类

一个类的基类可能有很多。这里被称为多重继承,但这里要注意的是,如果多个超类以不同的方式实现了同一个方法(即有多个同名方法),必须在class语句中小心排列这些超类,因为位于前面的类将会覆盖后面类的方法(这好像有点不太好理解,难道不应该从上到下执行的吗?但事实上前面的方法的确会覆盖后面的方法)。

多个超类同时使用时,查找特定方法或者属性时访问的顺序被称为方法解析顺序(MRO),它使用的算法很复杂,但很有效,你根本无需担心。

2.9接口和内省

接口这个概念与多态相关。处理多态对象时,你只关心接口(协议)——对外暴露的方法和属性。

Python中,不显式地指定对象必须包含哪些方法才能做参数。例如,你不会像在JAVA中那样显式地编写接口,而是假定对象能够完成你要求它完成的任务。如果不能完成,程序失败!!!

通常,你要求对象遵守特定的接口(即实现特定的方法),但如果需要,你可以非常灵活的提出要求:不是直接调用方法并期待一切顺利,而是检查所需的方法是否存在,如果不存在,就不再使用,避免程序失败。

>>> hasattr(tc,'talk')#tc 包含属性talk

>>>Ture>>> callable(getattr(tc,'talk',None))#检查属性talk是否可以被调用,请注意,这里没有在if语句中使用hasattr来直接访问属性,而是使用了getattr(它让我能够指定属性不存在时使用的默认值,这里为None),然后对返回的对象调用callable

>>>Ture

setattr与getattr功能相反,可用于设置对象的属性:

>>> setattr(tc,'name','Mr.Jia')>>>tc.name'Mr.Jia'

要查看这个对象中存储的所有值,可检查其__dict__属性。

如果要确定这个对象由什么组成,应该研究模块inspect,

2.10抽象基类

但是,有比手工检查(使用hasattr等)更好的方法!!!!

Python最终引进了abc模块提供了官方解决方法。这个模块为所谓的抽象基类提供了支持。一般而言,抽象类是不能被实例化的类,其职责是定义子类应该实现的一组抽象方法。,下面是一个简单示例:

>>> from abc importABC,abstractmethod>>> classTalker(ABC):

@abstractmethod#装饰器,这里的作用是将方法标记为抽象的

deftalk(self):pass

>>>Talker()

Traceback (most recent call last):

File"", line 1, in Talker()

TypeError: Can't instantiate abstract class Talker with abstract methods talk

#上边报错,是因为抽象类不能被实例化

#派生一个子类

>>> classknigget(Talker):pass

#上边实例化也会报错,下面重写方法talk

>>> classKnigget(Talker):deftalk(self):print("Hi!")#实例化完全没有问题。这是抽象类的主要用途。只有在这种情况下,使用isinstance 才是安全的:如果先检查给定的实例确实是Talker对象,就能相信这个实例在有需要的时候才有方法talk

>>> k=Knigget()>>>isinstance(k,Talker)

True>>>k.talk()

Hi!

然而,还缺少一个重要的部分——让isinstance多态程度更高的部分。正如我们不关心对象是什么,只关心对象能做什么。这样只要实现了talk方法,即使不是Talker的子类,都可以通过类型检查。下面新建一个类:

>>> classHerring:deftalk(self):print("Blub.")#这个类可以通过是否是Talker对象的检查,但它并不是Talker的对象

>>> h=Herring()>>>isinstance(h,Talker)

False#诚然,你可以直接从Talker派生出herring但这样Herring也可能是从其他人的模块中导入的。在这种情况下,就无法采用这种方法。#为了解决这个问题,你可以将Herring注册为Talker,这样所有的Herring对象都会被看做talker对象。

>>>Talker.register(Herring)

>>>isinstance(h,Talker)

True>>>issubclass(Herring,Talker)

True

然而,这样做存在一个缺点,就是直接从抽象类派生提供的保障没了.......

>>> classClam:pass

>>>

>>>Talker.register(Clam)

>>>issubclass(Clam,Talker)

True>>> c=Clam()>>>isinstance(c,Talker)

True>>>c.talk()

Traceback (most recent call last):

File"", line 1, in c.talk()

AttributeError:'Clam' object has no attribute 'talk'

换而言之,应将isinatance返回True视为一种意图表达。在这里,Clam有称为Talker的意图。本着鸭子类型的精神,我们相信它能承担Talker的职责,不行的是失败了。’

3、面向对象的思考

3.1将相关的东西放在一起。如果一个函数只操作一个全局变量,最好将它作为一个类的属性和方法。

3.2不要让对象之间过于亲密。方法应只关心其所属实例的属性,对其他实例的状态,让它们自己去管理。

3.3慎用继承,尤其是多重继承。

3.4保持简单。让方法短小紧凑。

小结:

本节学到的新函数

函数

描述

callable(object)

判断对象是否可调用(如是否是函数或方法)

getaeet(obiect,name[ , default])

获取属性的值,还可以提供默认值

hasattr(object,name)

确定对象是否有指定的属性

isinstance(object,class)

确定对象是否是指定类的实例

issubclass(A,B)

确定A是否是B的子类

random.choice(sequence)

从一个非空列表中随机地选取一个元素

setattr(object,name,value)

将对象的指定属性设置为指定值

type(object)

返回对象的类型

更多推荐

python亲密度

本文发布于:2024-02-13 07:23:34,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1757840.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:密度   python

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!