Chapter1序言本章作者简单介绍了python的数据模型,主要是python的一些特殊方法。比如__len__、__getitem__。并用一个卡片程序来解释这些方法的区别tuple和nametuplenametuple是一种类似于元组的数据类型。除了可以使用索引访问数据外,还支持方便的属性名访问数据。传统的元组访问如下。tup1=('abc','def','ghi')print(tup1[1])访问每个元素必须通过索引找到。这种查找方法非常不直观。使用nametuple构造:importcollectionstup2=collections.namedtuple('tuple2',['name','age','height'])t1=tup2('zhf','33','175')print(t1)print(t1.age)print(t1.height)print(t1.name)得到结果如下,namedtupel中的tuple2是类型名,name,age,height是属性名从上面访问可以看出直接使用t1.age方法访问更直观。当然也可以使用t1[0]等索引访问程序执行返回结果如下:tuple2(name='zhf',age='33',height='175')33175zhfnamedtupe1也支持迭代访问:fortint1:print(t)与元组相同,如果执行t1.age+=1,则namedtupel中的元素是不可变的。会提示元素t1.age+=1AttributeError:can'tsetattributecan'tsetattributecan'tsetattribute我们看书上的卡片例子,代码如下fromcollectionsimportnamedtupleCard=namedtuple('Card',['rank','suit'])classFrenchDeck(object):ranks=[str(n)forninrange(2,11)]+list('JQKA')suits='spadesdiamondsclubshearts'。split()def__init__(self):self._cards=[Card(rank,suit)forsuitinself.suitsforrankinself.ranks]def__len__(self):returnlen(self._cards)def__getitem__(self,position):returnself._cards[position]if__name__=='__main__':deck=FrenchDeck()#如果类中有__len__方法,len函数会先调用类中的方法print(len(deck))print(deck[1])first定义的牌元组Card,rank代表牌号,suit代表牌的花色。然后在FrenchDeck中首先定义ranks和suit的具体值。在__init__中初始化self._cards。__len__返回self._cards的长度。__getitem__返回特定的卡值。结果如下:卡片的长度为52,其中deck[1]为Card(rank='3',suit='spades'),可以看到len(deck)实际上调用了__len__方法。deck[1]调用__getitem__。因为有__getitem__方法,所以也可以迭代访问,如下:fordindeck:print(d)由于是可迭代的,我们可以模拟随机发牌的机制。fromrandomimportchoiceprint(choice(deck))随机选择,使用random.choice标准库,看看choice的底层实现原理,了解机制,再看另一个例子,关于向量操作。比如有vector1vector1(1,2)和vector2vector2(3,4)。那么vector1+vector2的结果应该是(4,6)。Vector1和vector2都是向量,如何实现运算。方法为__add__,__mul__代码如下:frommathimporthypotclassvector(object):def__init__(self,x=0,y=0):self.x=xself.y=ydef__repr__(self):返回'Vector(%r,%r)'%(self.x,self.y)def__abs__(self):returnhypot(self.x,self.y)def__bool__(self):returnbool(abs(self))def__add__(self,other):x=self.x+other.xy=self.y+other.y返回向量(x,y)def__mul__(self,scalar):返回向量(self.x*标量,self.y*scalar)if__name__=='__main__':v1=vector(1,2)v2=vector(2,3)print(v1+v2)print(abs(v1))print(v1*3)returnResult:Vector(3,5)2.23606797749979Vector(3,6)这里__add__,__mul___,__abs__分别实现向量的加法、乘法、取模运算。值得一提的是__repr__方法。当需要打印对象时调用此方法。例如,当您打印vector(1,2)时,您将得到vector(1,2)。否则,它是表示对象的字符串:。__repr__和__str__的功能类似。这里你需要知道_repr__和__str__有什么区别?1、__repr__和__str__的主要区别如下:区别1、如果__str__存在,__str__会先被调用。如果__str__不存在,将调用__repr__。测试代码如下:classDemoClass(object):def__repr__(self):return'DemoClassrepr'def__str__(self):return'DemoClassstr'demo=DemoClass()print(demo)#返回的是:DemoClassstrclassDemoClass(object):def__repr__(self):return'DemoClassrepr'demo=DemoClass()print(demo)#DemoClassrepr区别2classDemoClass(object):def__init__(self,name):self.name=namedef__repr__(self):#%rstring(使用repr()显示)return"reprMynameis%r"%(self.name)def__str__(self):return"strMynameis%s"%(self.name)demo=DemoClass("tony")print(demo)#return"strMynameistony"#当使用repr(obj)时,会自动调用__repr__函数print(repr(demo))#return"repr我叫托尼"demo1=DemoClass("'tony1")print(demo1)#strMynameis'tony1print(repr(demo1))#reprMynameis"'tony1"可以看到__repr__返回的字符串有一些差异,应该是准确的,没有歧义的,尽可能表达如何使用创建打印对象__repr__和__str__的代码之间的区别在于,后者在使用str()函数时调用,或者在使用print函数打印对象时调用,它返回的字符串对最终用户更友好。另一个例子如下:)"def__str__(self):return"strMynameis%s"%(self.name)demo=DemoClass("tony")demo.say()#tonydemo_repr=type(repr(demo))print(demo_repr)#demo_eval=type(eval(repr(demo)))print(demo_eval)##用于重建对象,如果eval(repr(obj))会得到的对象副本study=eval(repr(demo))study.say()#jackydifference3这里再比较一下repr和str函数的区别。在Python中,通常有两种方法可以将某种类型的变量或常量转换为字符串对象。即str()或repr()>>>a=10>>>type(str(a))>>>type(repr(a))但两者是什么之间的区别因为提供两个做完全相同事情的内置函数没有意义。我们先来看一个例子。>>>打印(str('123'))123>>>打印(str(123))123>>>打印(repr('123'))'123'>>>打印(repr(123))123从例子中不难发现,当我们传递一个字符串给str()函数并打印到终端时,输出的字符是不带引号的。当一个字符串被传递给repr()函数然后打印到终端时,输出字符被引用。两种输出形式不同的原因是print语句结合str()函数实际调用了对象的__str__方法来输出结果。而print结合repr()其实就是调用对象的__repr__方法输出结果。在下面的例子中,我们使用str对象直接调用这两个方法,输出结果的形式和前面的例子一致。>>>print('123'.__repr__())'123'>>>>print('123'.__str__())123但是这个例子可能还是不能很好的表达。str()和repr()有自己的意思是什么?让我们再看一个例子。>>>fromdatetimeimportdatetime>>>now=datetime.now()>>>print(str(now))2017-04-2215:41:33.012917>>>print(repr(now))datetime.datetime(2017,4,22,15,41,33,12917)通过str()的输出,我们可以很好的知道now实例的内容,但是丢失了now实例的数据类型信息。而通过repr()的输出我们不仅可以得到now实例的内容,还可以知道now是datetime.datetime对象的一个??实例。另一个例子:string="Hello\tWill\n"print(str(string))print(repr(string))结果:HelloWill'Hello\tWill\n'另一个例子:a="haha"print(str(a))print(repr(a))#returnresult"""haha'haha'"""因此,str()和repr()的区别在于:str()的输出追求可读性,输出格式应易于理解。适用于向用户终端输出内容。repr()的输出追求清晰。除了对象内容外,还需要显示对象的数据类型信息,适合在开发调试阶段使用。另外,如果你想让自定义类的实例被str()和repr()调用,那么你需要重载自定义类中的__str__和__repr__方法。__repr__和__str__之间的区别?