• 第二章 序列构成的数组


    2.1内置序列类型概览

    Python标准库用C实现了丰富的序列模型,列举如下:

    1>    序列模型 list、tuple和collection.deque这些序列能存放不同类型的序列

    2>    扁平模型 str、bytes、bytearray、memoryview和array.array这类序列只能存放一种类型

    容器序列存放的是它们所包含任意类型对象的引用,而扁平序列存放的是值而不是引用。

    序列类型还能按照能否被修改来分类:

    1>    可变序列    list  bytearray  array.array  collections.deque  memoryview

    2>    不可变序列  tuple  str  bytes

    下图显示了可变序列(MutableSequence)和不可变序列(Sequence)的差异,同时也能看出前者从后者那里继承了一些方法。了解基类可以帮助我们总结出那些完整的序列类型包含了哪些功能。

     

    2.2 列表推导和生成器表达式

           列表推导是构建列表的快捷方式,而生成器表达式则可以创建其他任何类型的序列。( 列表推导(list comprehension)简称为listcomps;生成器表达式(generator expression)则称为genexps )

    2.2.1 列表推导和可读性

      通常原则:只用列表推导创建新的列表,并且尽量保持简短。如果列表推导的代码超过了两行就要考虑是不是得用for循环重写。Python会忽略代码里[]、{}、() 中的换行,故在多行的列表、列表推导、生成器表达式、字典这一类的,可以省略不太好看的续行符 。

     1 symbols = 'hseksdth'
     2 codes = []
     3 """
     4 ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
     5 """
     6 for symbol in symbols:
     7     codes.append(ord(symbol))
     8 print(codes) #[104, 115, 101, 107, 115, 100, 116, 104]
     9 
    10 symbols = 'dgshsgjh'
    11 codes = [ord(symbol) for symbol in symbols]
    12 print(codes) # [100, 103, 115, 104, 115, 103, 106, 104]
    13 
    14 x = 'ABC'
    15 dummy = [ord(x) for x in x]
    16 print(dummy) # [65, 66, 67]

    2.2.2 列表推导同filtermap的比较

    Filter和map合起来做的事情,列表推导也可以做,而且还不需要借助难以理解和阅读的lambda表达式:

    如下示例:

    1 symbols = '$%#*&'
    2 dummy = [ord(x) for x in symbols]
    3 beyound_ascii = [ord(s) for s in symbols if ord(s) > 36]
    4 beyound_asciii = list(filter(lambda c:c>36,map(ord,symbols)))
    5 print(beyound_ascii)    # [37, 42, 38]   
    6 print(dummy)            #  [36, 37, 35, 42, 38]
    7 print(beyound_asciii)   #  [37, 42, 38]

    2.2.3 笛卡尔积

           用列表推导可以生成两个或以上的可迭代类型的笛卡尔积,笛卡尔积是一个列表,列表里的元素是由输入的可迭代类型的元素对构成的元组,因此笛卡尔积的列表长度等于输入变量的乘积。

           如下示例:

    1 colors = ['black','white']
    2 sizes = ['S','M','L']
    3 tshirts = [(color,size) for color in colors for size in sizes]
    4 print(tshirts)
    5 输出:[('black', 'S'), ('black', 'M'), ('black', 'L'), ('white', 'S'), ('white', 'M'), ('white', 'L')]
    6 
    7 tshirts2 = [(color,size) for size in  sizes for color in colors]
    8 print(tshirts2)
    9 输出:[('black', 'S'), ('white', 'S'), ('black', 'M'), ('white', 'M'), ('black', 'L'), ('white', 'L')]

    列表推导的作用只有一个:生成列表。如果想生成其他类型的序列,生成器表达式就派上了用场。

    2.2.3 生成器表达式

           虽然也可以用列表推导来初始化元组、数组或其他序列类型,但是生成器表达式是更好的选择。这是因为生成器表达式背后遵循了迭代器协议,可以逐个的产生元素,而不是先建立一个完整的列表,然后再把这个列表传递到某个构造函数里。生成器表达式的语法跟列表推到差不多,只不过把方括号换成圆括号。

           如下示例:

     1 symbols = '*&%$#@'
     2 print(tuple(ord(symbol)for symbol in symbols))
     3 输出:(42, 38, 37, 36, 35, 64)
     4 
     5 print(array.array('I',(ord(symbol)for symbol in symbols)))
     6 输出:array('I', [42, 38, 37, 36, 35, 64])
     7 
     8 colors = ['R','G','B']
     9 sizes = ['S','M','L']
    10 for tshirt in ('%s %s'%(c,s)for c in colors for s in sizes):
    11      print(tshirt)
    12 输出:
    13 R S
    14 R M
    15 R L
    16 G S
    17 G M
    18 G L
    19 B S
    20 B M
    21 B L

    【注】1、 如果生成器表达式是一个函数调用过程中的唯一参数,那么不需要额外再用括号把它括起来。

               2、array的构造方法需要两个参数,因此括号是必须的。array构造方法的第一个参数指定了数组中数字的存储方式。(后面会讲到生成器的工作原理,这里一笔带过) 

    2.3 元组不仅仅是不可变的列表

    2.3.1 元组和记录

    元组其实是对数据的记录:元组中每个元素都存放了记录中一个字段的数据,外加这个字段的位置。正是这个位置信息给数据赋予了意义。如果只把元组理解为不可变的列表,其它的信息---它所含有的元素的总数和它们的位置—似乎变得可有可无。但如果把元组当做一些字段的集合,那么数量和位置信息就变得非常重要了。

    如下示例:

     1 lax_coordinates = (55.236,864.1259)
     2 city,year,pop,chg,area = ('Tokyo',2019,1547,0.152,8746)
     3 traveler_ids = [('USA','1857642'),('BRA','845696'),('BHY','45963')]
     4 for passport in sorted(traveler_ids):
     5      print('%s/%s'%passport)
     6 输出:
     7 BHY/45963
     8 BRA/845696
     9 USA/1857642
    10 
    11 for country,_ in traveler_ids:
    12      print(country)
    13 输出:
    14 USA
    15 BRA
    16 BHY

    【注】1、在迭代过程中,passport变量被绑定到每个元组上。

               2、%格式运算符能被匹配到对应元组元素上。

               3、for 循环可以分别提取元组里的元素,也叫作拆包(unpacking)。因为元组中第二个元素对我们没什么用,所以将它赋给 ‘_’ 占位符。

    2.3.2 元组拆包

      元组拆包可以应用到任何可迭代的对象上,唯一的硬性要求是:被可迭代对象中元素数量必须跟接受这些元素的元组空挡数一致,除非我们用 * 号来表示忽略多余的元素。

    最好辨认的元组拆包形式就是平行赋值,也就是说把一个可迭代对象里的元素,一并赋值到由对应的变量组成的元组中。

    1 lax_corrdinates = (33.1258,-56.4895)
    2 latitude,longtitude = lax_corrdinates
    3 print(latitude)
    4 print(longtitude)
    5 输出:33.1258
    6      -56.4895

    还可以用 * 运算把一个可迭代运算拆开作为函数的参数。

    1 b,a = a,b           # 这种写法可以在不使用中间变量的情况下交换 a,b 的值
    2 print(divmod(20,8)) # divmod 把除数和余数运算结合起来,返回包含商和余数的元组
    3 t = (20,8)
    4 print(divmod(*t))
    5 quotient,remainder = divmod(*t)
    6 print(quotient,remainder)
    7 输出:(2, 4)
    8      (2, 4)
    9       2 4

    下面一个示例,元组拆包的用法则是让一个函数可以用元组的形式返回多个值,然后调用函数的代码就能轻松地接受这些返回值。比如os.path.split() 函数就会返回以路径和最后一个文件名组成的元组(path,last_part):

    1 filepath,filename = os.path.split('/home/download/myfile/study.docx')
    2 print('filepath = ',filepath)
    3 输出:filepath =  /home/download/myfile
    4 print('filename = ',filename)
    5 输出:filename =  study.docx
    6 print('full path = '+filepath+'/'+filename)
    7 输出:full path = /home/download/myfile/study.docx

    在进行拆包的时候,我们不总是对元组里所有的数据都感兴趣,_占位符能帮助处理这种情况,如上所示。

    除此之外,元组拆包中使用 * 也可以帮助我们将注意力集中在部分元素上面,如下示例用 * 来处理剩下的元素。在python中,函数用*args来获取不确定数量的参数算是一种经典写法。

    1 a,b,*rest = range(10)
    2 print(a,b,rest)
    3 输出:0 1 [2, 3, 4, 5, 6, 7, 8, 9]
    4 a1,b1,*rest1 = range(2)
    5 print(a1,b1,rest1)
    6 输出:0 1 []

    在平行赋值中,*前缀只能用在一个变量名前面,但是这个变量可以出现在赋值表达式的任意位置

    1 a,*body,c,d = range(5)
    2 print(a,body,c,d)
    3 输出:0 [1, 2] 3 4
    4 *head,b,c,d = range(5)
    5 print(head,b,c,d)
    6 输出:[0, 1] 2 3 4

    2.3.3 嵌套元组拆包

    接受表达式的元组可以是嵌套的,例如 (a,b,(c,d)) 。只要这个接受元组的嵌套结构符合表达式本身的嵌套结构,python 就可以做出正确的对应。

    如下示例:

     1 metro_areas = [
     2         ('Tokyo','JP',50.3654,(12.458,63.45896)),
     3         ('ShangHai', 'JSH', 62.3789, (85.34596, 111.25)),
     4         ('GuangHou', 'GZ', 50.3654, (23.214, 98.125)),
     5         ('Zhengzhou', 'ZH', 50.3654, (658.145, 985.12)),
     6         ('LaSha', 'LS', 50.3654, (528.461, 753.159)),
     7     ]
     8 
     9 print('{:15}|{:^9}|{:^9}'.format('','lat.','long.'))
    10 fmt = '{:15}|{:9.4f}|{:9.4f}'
    11 for name,cc,pop,(latitude,longitude) in metro_areas:
    12        print(fmt.format(name,latitude,longitude))
    13 输出:
    14                     |  lat.    |  long.  
    15 Tokyo               |  12.4580 |  63.4590
    16 ShangHai            |  85.3460 | 111.2500
    17 GuangHou            |  23.2140 |  98.1250
    18 Zhengzhou           | 658.1450 | 985.1200
    19 LaSha               | 528.4610 | 753.1590

    【注】python3之前,元组可以作为形参放在函数声明中,例如:def fn(a,(b,c),d),然而Python3 不在支持这种形式。

    2.3.4 具名元组

           collections.namedtuple是一个工厂函数,它用来构建一个带字段名的元组和一个有名字的类。

           用namedtuple构建的类的实例所消耗的内存跟元组是一样的,因为字段名都被存在对应的类里面。这个实例跟普通的对象实例比起来也要小一些,因为Python不会用__dict__来存放这些实例的属性。

     1 City = namedtuple('City','name country population coordinates')
     2 tokyo = City('Tokyo','JP',36.4598,(86.56987,84.1259))
     3 print('tokyo = ',tokyo)
     4 print('tokyo.population = ',tokyo.population)
     5 print('tokyo.coordinates = ',tokyo.coordinates)
     6 print('City._fields = ',City._fields)
     7 输出:
     8 tokyo =  City(name='Tokyo', country='JP', population=36.4598, coordinates=(86.56987, 84.1259))
     9 tokyo.population =  36.4598
    10 tokyo.coordinates =  (86.56987, 84.1259)
    11 City._fields =  ('name', 'country', 'population', 'coordinates')

    由上可知:

    1、创建一个具名元组需要两个参数:类名/类的各个字段名。后者可以是由数个字符串组成的可迭代的对象,或者是由空格分隔开的字段名组成的字符串。

    2、存放在对应字段里的数据要以一串参数的形式传入到构造函数中(注意:元组的构造函数只接受单一的可迭代对象)

    3、可通过字段名或者位置来获取一个字段的信息

    具名元组也有自己的属性,列举几个最有用的:_fields类属性、类方法_make(iterable)和实例方法_asdict()

    1 Latlong = namedtuple('Latlong','lat long')
    2 delhi_data = ('Delhi NCR','IN',21.459,Latlong(25.465,87.1276))
    3 delhi = City._make(delhi_data)
    4 print('delhi._asdict() = ',delhi._asdict())
    5 输出:delhi._asdict() =  OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.459), ('coordinates', Latlong(lat=25.465, long=87.1276))])

    由上可知:

    1、_fields属性是一个包含这个类所有字段名称的元组

    2、用_make()通过接受一个可迭代对象来生成这个类的一个实例,它的作用跟City(*delhi_data) 是一样的

    3、_asdict() 把具名元组以collections.OrderedDict的形式返回,我们可以利用它把元组里的信息友好的呈现出来

  • 相关阅读:
    Ansible 的初步使用
    HBase 和 Hive 的差别是什么,各自适用在什么场景中?Spark SQL能做什么?
    spark安装配置
    scala安装配置
    Apache Spark 3.0.0重磅发布 —— 重要特性全面解析
    hbase的安装与配置(三台集群分布式)
    Flv的结构分析
    几种直播流媒体协议
    rtmp推送aac没有声音的问题记录
    c++中SetEvent和ResetEvent的使用
  • 原文地址:https://www.cnblogs.com/IamJiangXiaoKun/p/10217443.html
Copyright © 2020-2023  润新知