流畅的python第一章_流畅的python学习笔记-第1章

第1章 序幕

这一章中作者简要的介绍了python数据模型,主要是python的一些特殊方法。比如__len__,__getitem__. 并用一个纸牌的程序来讲解了这些方法

tuple和nametuple的区别Nametuple是类似于元组的数据类型。

除了能够用索引来访问数据,还支持用方便的属性名来访问数据。

传统的元组访问如下。

tup1 = ('abc', 'def', 'ghi')

print(tup1[1])

对每个元素的访问都必须通过索引来找到。这种找法很不直观

使用nametuple来构造:

import collections

tup2 = 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')

33

175

zhf

namedtupe1也支持迭代访问:

for t in t1:

print(t)

和元组一样,namedtupel中的元素也是不可变更的

如果执行t1.age+=1。将会提示无法设置元素

t1.age += 1

AttributeError: can't set attribute

下面来看下书中的纸牌例子,代码如下

from collections import namedtuple

Card = namedtuple('Card', ['rank', 'suit'])

class FrenchDeck(object):

ranks = [str(n) for n in range(2, 11)] + list('JQKA')

suits = 'spades diamonds clubs hearts'.split()

def __init__(self):

self._cards = [Card(rank, suit) for suit in self.suits for rank in self.ranks]

def __len__(self):

return len(self._cards)

def __getitem__(self, position):

return self._cards[position]

if __name__ == '__main__':

deck = FrenchDeck()

# 如果类中有一个__len__方法, len函数首先就会调用类里面的方法

print(len(deck))

print(deck[1])

首先定义了的纸牌元组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__方法,

还可以进行迭代访问,如下:

for d in deck:

print (d)

既然是可迭代的,那么我们可以模拟随机发牌的机制。

from random import choice

print (choice(deck))随机抽取,使用random.choice标准库 要看看choice底层实现原理,搞明白机制

接下来看另外一个例子,关于向量运算的。 比如有向量1 vector1(1,2),向量2 vector2(3,4)。 那么vector1+vector2的结果应该是(4,6)。

Vector1和vector2都是向量,如何实现运算呢。方法是__add__,__mul__

代码如下:

from math import hypot

class vector(object):

def __init__(self, x=0, y=0):

self.x = x

self.y = y

def __repr__(self):

return 'Vector(%r,%r)' % (self.x, self.y)

def __abs__(self):

return hypot(self.x, self.y)

def __bool__(self):

return bool(abs(self))

def __add__(self, other):

x = self.x + other.x

y = self.y + other.y

return vector(x, y)

def __mul__(self, scalar):

return vector(self.x * scalar, self.y * scalar)

if __name__ == '__main__':

v1 = vector(1, 2)

v2 = vector(2, 3)

print(v1 + v2)

print(abs(v1))

print(v1 * 3)

返回结果:

Vector(3,5)

2.23606797749979

Vector(3,6)

在这里__add__,_ _mul_ __,__abs__分别实现了向量加法,乘法,以及求模的运算。

值得一提的是__repr__的方法。

这个方法是在需要打印对象的时候调用。

例如print vector(1,2)的时候得到vector(1,2).

否则就是表示对象的字符串:.

这个repr和str的作用是类似的

这里要知道__repr__和__str__的区别在哪里 ^1

__repr__和__str__的区别

主要区别如下:区别1 如果__str__ 存在会首先调用__str__ 如果__str__ 不存在会调用__repr__

测试代码如下:

class DemoClass(object):

def __repr__(self):

return 'DemoClass repr'

def __str__(self):

return 'DemoClass str'

demo = DemoClass()

print(demo) #返回是:DemoClass str

class DemoClass(object):

def __repr__(self):

return 'DemoClass repr'

demo = DemoClass()

print(demo) # DemoClass repr

区别2

class DemoClass(object):

def __init__(self, name):

self.name = name

def __repr__(self):

# %r 字符串 (采用repr()的显示)

return "repr My name is%r" % (self.name)

def __str__(self):

return "str My name is%s" % (self.name)

demo = DemoClass("tony")

print(demo) # 返回"str My name is tony"

# 使用repr(obj)的时候,会自动调用__repr__函数

print(repr(demo)) # 返回"repr My name is tony"

demo1 = DemoClass("'tony1")

print(demo1) # str My name is 'tony1

print(repr(demo1)) # repr My name is "'tony1"

可以看到有一些区别的__repr__所返回的字符串应该准确、无歧义,并且尽可能表达出如何用代码创建出这个被打印的对象。

__repr__和__str__的区别在于,后者是在str()函数被使用,或是在用print函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。

再比如下面一个例子:

class DemoClass(object):

def __init__(self, name):

self.name = name

def say(self):

print(self.name)

def __repr__(self):

return "DemoClass('jacky')"

def __str__(self):

return "str My name is%s" % (self.name)

demo = DemoClass("tony")

demo.say() # tony

demo_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() # jacky

区别3 这里额外比较repr和str函数的区别

在 Python 中要将某一类型的变量或者常量转换为字符串对象通常有两种方法,即str() 或者 repr()

>>> a = 10

>>> type(str(a))

>>> type(repr(a))

但是这二者之间有什么区别呢? 因为提供两个功能完全相同的内建函数是没有意义的。

先看一个例子。

>>> print(str('123'))

123

>>> print(str(123))

123

>>> print(repr('123'))

'123'

>>> print(repr(123))

123

从例子中不难发现,当我们把一个字符串传给 str() 函数再打印到终端的时候,输出的字符不带引号。

而将一个字符串传给 repr() 函数再打印到终端的时候,输出的字符带有引号。

造成这两种输出形式不同的原因在于:

print 语句结合 str() 函数实际上是调用了对象的__str__方法来输出结果。 而 print 结合 repr() 实际上是调用对象的__repr__ 方法输出结果。

下例中我们用 str 对象直接调用这两个方法,输出结果的形式与前一个例子保持一致。

>>> print('123'.__repr__())

'123'

>>>> print('123'.__str__())

123

不过这个例子可能还是无法很好表达到底 str() 与 repr() 各有什么意义 我们再来看一个例子。

>>> from datetime import datetime

>>> now = datetime.now()

>>> print(str(now))

2017-04-22 15: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))

结果:

Hello Will

'Hello\tWill\n'

再比如:

a = "哈哈"

print(str(a))

print(repr(a))

# 返回结果

"""哈哈'哈哈'"""

因此 str() 与 repr() 的不同在于: str() 的输出追求可读性,输出格式要便于理解,适合用于输出内容到用户终端。 repr() 的输出追求明确性,除了对象内容,还需要展示出对象的数据类型信息,适合开发和调试阶段使用。

另外如果想要自定义类的实例能够被 str() 和 repr() 所调用, 那么就需要在自定义类中重载__str__ 和 __repr__ 方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值