1 元组记录
元组可以当做存放数据的记录。元组中的元素用于存放记录字段数据,而元素所在的位置用于表达该字段的隐含含义。
Luciano Ramalho 举了这样一个示例:
lax_coordinates=(33.33,-11.92)
logging.info('longitude -> %s',lax_coordinates[0])
logging.info('latitude -> %s',lax_coordinates[1])
city,year,pop,chg,area=('Tokyo',2013,232,0.22,281)
logging.info('city -> %s',city)
logging.info('area -> %s',area)
traveler_ids=[('USA','232'),('ESP','11')]
for passport in sorted(traveler_ids):
logging.info('%s/%s' % passport)
for country,_ in traveler_ids:
logging.info('country -> %s',country)
运行结果:
INFO - longitude -> 33.33
INFO - latitude -> -11.92
INFO - city -> Tokyo
INFO - area -> 281
INFO - ESP/11
INFO - USA/232
INFO - country -> USA
INFO - country -> ESP
- 可利用元组的位置,得到相关数据,但必须清楚该位置所指向的数据含义。
- 也可以利用元组,给多个变量一次性赋值。
- 利用 for 循环可以分别提取出元组里的元素,这叫作拆包(unpacking)。
- 第三个用例是先排序,然后再拆包到一个变量,最后用
%
格式运算符将自动匹配到对应的元组元素中打印出来。 - 第四个用例是先拆包到一个变量和一个占位符
_
,然后打印出这个变量。
2 元组拆包
利用元组拆包,我们可以使用一行代码,同时给多个变量一次性初始化赋值。
元组拆包可以应用到任何可迭代对象上。这些可迭代对象中的元素数必须要跟接受这些元素的元组的空档数一致。
(1)数据交换
利用元组拆包,可以很优雅地通过一行代码实现两个变量之间的数据交换。
a = 1
b = 2
a, b = b, a
logging.info('a -> %s',a)
logging.info('b -> %s',b)
运行结果:
INFO - a -> 2
INFO - b -> 1
(2)拆分函数入参
利用 * 运算符可以把一个可迭代对象拆分成某个函数的多个入参。
r = divmod(20, 8)
logging.info('r -> %s', r)
i = (20, 8)
r = divmod(*i)
logging.info('r -> %s', r)
quotient, remainder = divmod(*i)
logging.info('quotient -> %s', quotient)
logging.info('remainder -> %s', remainder)
运行结果:
INFO - r -> (2, 4)
INFO - r -> (2, 4)
INFO - quotient -> 2
INFO - remainder -> 4
python 的 divmod() 函数,会返回一个包含商和余数的元组。divmod(a, b) 中,a 是被除数,b 是除数,而返回的结果是 (商,余数)。
(3)占位符方式
在进行拆包的时候,如果有些返回值不是我们想要的,可以使用 _
占位符忽略它们。
_,filename=os.path.split('/home/xxx/readme.txt')
logging.info('filename -> %s',filename)
运行结果:
INFO - filename -> readme.txt
os.path.split() 函数会返回以路径和最后一个文件名组成的元 组 (path, last_part),而我们只需要文件名,那么这里就可以使用 _
占位符忽略路径值。
(4)处理多余的值
在多值赋值的场景下,我们可以使用 *
来处理剩下的值。所谓的剩下的值,就是没有明确被赋值的值。
a, b, *rest = range(5)
logging.info('a,b,rest -> %s,%s,%s', a, b, rest)
a, b, *rest = range(3)
logging.info('a,b,rest -> %s,%s,%s', a, b, rest)
a, b, *rest = range(2)
logging.info('a,b,rest -> %s,%s,%s', a, b, rest)
运行结果:
INFO - a,b,rest -> 0,1,[2, 3, 4]
INFO - a,b,rest -> 0,1,[2]
INFO - a,b,rest -> 0,1,[]
带 *
的变量可以是左侧变量表达式中的任意一个变量。
a, *body, c, d = range(5)
logging.info('a, body, c, d -> %s,%s,%s,%s', a, body, c, d)
*head, b, c, d = range(5)
logging.info('head,b,c,d -> %s,%s,%s,%s', head, b, c, d)
运行结果:
INFO - a, body, c, d -> 0,[1, 2],3,4
INFO - head,b,c,d -> [0, 1],2,3,4
3 嵌套元组拆包
只要接受元组的嵌套结构与表达式本身的嵌套结构一致, Python 就可以自动拆包。
metro_areas = [('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name, cc, pop, (latitude, longitude) in metro_areas:
if longitude <= 0:
logging.info(fmt.format(name, latitude, longitude))
运行结果:
INFO - Mexico City | 19.4333 | -99.1333
INFO - New York-Newark | 40.8086 | -74.0204
INFO - Sao Paulo | -23.5478 | -46.6358
经纬度是经度与纬度的合称组成一个坐标系统,称为地理坐标系统,它是一种利用三度空间的球面来定义地球上的空间的球面坐标系统,能够标示地球上的任何一个位置。
负纬度表示位于南半球(S)的位置而负经度表示西半球(W)的位置。示例代码中,元组拆包后,过滤出西半球的坐标,然后打印出来。
4 具名元组
4.1 用法
我们可以利用 collections.namedtuple 这个工厂函数来构建一个带名字的元组。
用 namedtuple 构建的类的实例跟普通的对象实例相比,所占用的内存要小一些,因为 Python 没有使用 dict 来存放这些实例的属性。
from collections import namedtuple
City=namedtuple('City','name country population coordinates')
tokyo=City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
logging.info('tokyo -> %s',tokyo)
logging.info('population -> %s',tokyo.population)
logging.info('tokyo[0] -> %s',tokyo[0])
logging.info('tokyo[1] -> %s',tokyo[1])
运行结果:
INFO - tokyo -> City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
INFO - population -> 36.933
INFO - tokyo[0] -> Tokyo
INFO - tokyo[1] -> JP
创建一个具名元组需要两个入参,一个是类名,另一个是类中各个字段的名字。后者是由空格分隔开的字段名所组成的字符串。City=namedtuple('City','name country population coordinates')
放入数据时,需要把数据以逗号分隔的形式传入到具名类的构造函数中。tokyo=City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
然后就可以通过字段名或者位置来获取具名元组中的某个字段的信息。tokyo.population
或 tokyo[0]
。
4.2 专有属性
除了从普通元组那里继承来的属性之外,具名元组还定义了一些自己的专有属性。
- _fields 属性包含了这个类所有字段名称元组。
- _make() 方法接受一个可迭代对象来生成这个类的一个实例。
- _asdict() 方法会把具名元组以 collections.OrderedDict 的形式返 回出来,我们可以利用它来打印出元组中的信息。
logging.info('_fields -> %s',City._fields)
LatLong = namedtuple('LatLong', 'lat long')
delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
delhi = City._make(delhi_data)
logging.info('delhi._asdict() -> %s',delhi._asdict())
运行结果:
INFO - _fields -> ('name', 'country', 'population', 'coordinates')
INFO - delhi._asdict() -> OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])
示例中的 delhi_data 是一个嵌套结构,内部嵌套了包含经纬度信息的具名元组。