Namedtuple,顾名思义就是为了方便使得tuple每一项都有实际的意义,如果你想建立一个简单的不想改变的且没有method的类(尤其是数据库读出来的数据),那么使用namedtuple就会特别方便。
创建就用collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None),一个返回class的工厂方法。
一些实用代码举例如下:
from collections import namedtuple
# nametuple: Factory Function for Tuples with Named Fields
Person = namedtuple("Person", ["name","age", "sex"])
p1 = Person("Jason", 30, sex = "Man")
p2 = Person("Merry", 60, sex = "Woman")
print("person's information:", p1.name, p1.age, p1.sex)
print(p2)
输出如下:
person's information: Jason 30 Man
Person(name='Merry', age=60, sex='Woman')
三个自带方法分别是_asdict(),_replace() 和 _make
# unpacking like a regular tuple
name, age, sex = p1
print("unpacking:", name, age, sex)
# New an OrderedDict
mydict = p1._asdict()
print("OrderDict:", mydict.get("name"), mydict.get("age"), mydict.get("sex"))
# Return a new instance of the named tuple
new_person = p1._replace(name = "Kitty", age = 1, sex = "Woman")
print(new_person)
# make new instances with existing iterables
p3 = Person._make(["Kobe",38, "Man"])
print("_make an instance:",p3)
输出:
unpacking: Jason 30 Man
OrderDict: Jason 30 Man
Person(name='Kitty', age=1, sex='Woman')
_make an instance: Person(name='Kobe', age=38, sex='Man')
几种常用用法
1. 把dict转为namedtuple
mydict2 = {"name": "X", "age": 30, "sex": "Woman"}
print("Dict to namedtuple:", Person(**mydict2))
输出:
Dict to namedtuple: Person(name='X', age=30, sex='Woman')
2. 获取csv或者sqlite3的数据文件中的记录,使用_make()以及map()方法,然后iterate就可以了
print("\nRead csv file 'users.csv':")
import csv
Name = namedtuple("Name", ["first","last"])
for nm in map(Name._make, csv.reader(open("users.csv"))):
print(nm.first,nm.last)
print("\nConnect sqlite3 file 'my_friends.db':")
import sqlite3
conn = sqlite3.connect('my_friends.db')
cursor = conn.cursor()
cursor.execute('select * from friends where closeness > 700 order by closeness')
Friend = namedtuple("Friend", ["first",'last','closeness'])
for frd in map(Friend._make, cursor.fetchall()):
print(frd.first,frd.last,frd.closeness)
CSV输出:
Read csv file 'users.csv':
First Name Last Name
Grace Hopper
Alan Turing
SQLITE3输出:
Connect sqlite3 file 'my_friends.db':
niubi Gong 777
shenye Gong 888
Yeye Gong 999
zong Gong 999
后面还看到了一个在Fluent Python这本书里面很好的一个例子,不仅仅讲了namedtuple 在创造“小类”方面的精妙用法,还顺便给出了重载dunder methods的优势。比如我们需要创建一个类表示一堆牌,那么其中每一张就是单独的一个card, 有两个属性,大小和点数。那么这个card就非常适合用namedtuple.
Card = namedtuple("Card",['rank',"suit"])
class ClassicDeck():
ranks = [str(n) for n in range(2,11)] + list("JQKA")
suits = "spades diamonds clubs heart".split()
def __init__(self):
self._cards = [Card(rank,suit) for rank in self.ranks for suit in self.suits]
def __len__(self):
return len(self._cards)
def __getitem__(self, pos):
return self._cards[pos]
deck = ClassicDeck()
print(deck[2])
print(len(deck))
print(deck[2:6])
from random import choice
print("random:",choice(deck))
# print out last 8 cards
i = 8
for card in reversed(deck):
if i > 0:
print(card)
i = i-1
else:
break
其中精妙之处在于,"__getitem__"使得使用“collections[index]”获取元素成为可能,不仅如此,还让deck完全成为了一个iterable,可以执行slicing操作或者被iterate。 那么如果我要找一个随机的牌,按照传统用java写OOP的想法,本来是想在类里面写一个方法去产生随机牌,但是现在不需要了,直接用python库里面的函数random.choice()去做这个操作。 如果需要知道这堆牌的数量,原来还需要在类里面去写函数去执行,然后客户拿到类之后还需要记住是用deck.length() 或者deck.size() ,但是你直接重载dunder method __len__后,使用的时候直接拿len()就好了。这其实是一种非常pythonic的思想。
网友评论