问题

通过下标访问列表或元组中的元素,代码可读性不强,可以通过名称来访问元素。

解决方案

collections.namedtuple() 函数是继承自tuple的子类。可以创建自定义元素个数的tuple对象,它具备tuple的不变性,又可以根据属性而不是索引来引用其中的元素。代码示例:

from collections import namedtuple

User = namedtuple('User', ['mail_add', 'joined_date'])

print(User)

user = User('python@gmail.com', '2017-11-10')

print(user)

print(user.mail_add)

print(user.joined_date)

User(mail_add='python@gmail.com', joined_date='2017-11-10')

python@gmail.com

2017-11-10

尽管 namedtuple 的实例看起来像一个普通的类实例,但是它跟元组类型是可交换的,支持所有的普通元组操作,如索引和解压。 比如:

print(len(user))

mail, joined = user

print(mail)

print(joined)

2

python@gmail.com

2017-11-10

命名元组的主要用途是将代码从下标操作中解脱出来。 因此,如果在数据库调用中返回了一个很大的元组列表,通过下标去操作其中的元素, 当数据库表中添加了新的列时,代码可能就会出错,但如果使用了命名元组,就不会有这样的顾虑。

为了说明清楚,下面是使用普通元组的代码:

def cumputer_cost(records):

total = 0.0

for record in records:

total += record[1] * record[2]

return total

下标操作通常会让代码表意不清晰,并且非常依赖记录的结构。 下面是使用命名元组的版本:

from collections import namedtuple

Stock = namedtuple('Stock', ['name', 'shares', 'price'])

def cumputer_cost(records):

total = 0.0

for record in records:

r = Stock(*record)

total += s.shares * s.price

return total

讨论

命名元组另一个用途是作为字典的替代,因为字典存储需要更多的内存空间。 如果需要构建一个非常大的包含字典的数据结构,那么使用命名元组会更加高效。 但是需要注意的是,不像字典那样,一个命名元组是不可更改的。比如:

s = Stock('ACME', 100, 123.45)

s.shares = 75

Traceback (most recent call last):

File "", line 1, in

s.shares = 75

AttributeError: can't set attribute

如果确实需要改变属性的值,可以使用命名元组实例的 _replace() 方法, 它会创建一个全新的命名元组,并将对应的字段用新的值取代。比如:

s = Stock('ACME', 100, 123.45)

s = s._replace(shares=75)

print(s)

Stock(name='ACME', shares=75, price=123.45)

最后要说的是,如果目标是定义一个需要更新很多实例属性的高效数据结构,那么命名元组并不是最佳选择。 这时候应该考虑定义一个包含 slots 方法的类。