Python程序设计开发宝典
上QQ阅读APP看书,第一时间看更新

2.4 Python常用内置函数用法精要

内置函数(built-in functions, BIF)是Python内置对象类型之一,不需要额外导入任何模块即可直接使用,这些内置对象都封装在内置模块__builtins__之中,用C语言实现并且进行了大量优化,具有非常快的运行速度,推荐优先使用。使用内置函数dir()可以查看所有内置函数和内置对象:

     >>>dir(__builtins__)

使用help(函数名)可以查看某个函数的用法。另外,也可以不导入模块而直接使用help(模块名)查看该模块的帮助文档,例如help('math')。常用的内置函数及其功能简要说明如表2-5所示,其中方括号内的参数可以省略。

表2-5 Python常用内置函数

续表

续表

续表

内置函数数量众多且功能强大,很难一下子全部解释清楚,下面先简单介绍其中一部分,后面的章节中将根据内容组织的需要逐步展开和演示更多函数更加巧妙的用法。遇到不熟悉的函数可以通过内置函数help()查看使用帮助。另外,在编写程序时应优先考虑使用内置函数,因为内置函数不仅成熟、稳定,而且速度相对较快。

2.4.1 类型转换与类型判断

(1)内置函数bin()、oct()、hex()用来将整数转换为二进制、八进制和十六进制形式,这3个函数都要求参数必须为整数。

        >>>bin(555)                       #把数字转换为二进制串
        '0b1000101011'
        >>>oct(555)                       #转换为八进制串
        '0o1053'
        >>>hex(555)                       #转换为十六进制串
        '0x22b'

内置函数int()用来将其他形式的数字转换为整数,参数可以为整数、实数、分数或合法的数字字符串,当参数为数字字符串时,还允许指定第二个参数base用来说明数字字符串的进制。其中,base的取值应为0或2~36之间的整数,其中0表示按数字字符串隐含的进制进行转换。

    >>>int(-3.2)                      #把实数转换为整数
    -3
    >>>from fractions import Fraction, Decimal
    >>>x=Fraction(7,3)
    >>>x
    Fraction(7,3)
    >>>int(x)                         #把分数转换为整数
    2
    >>>x=Decimal(10/3)
    >>>x
    Decimal('3.333333333333333481363069950020872056484222412109375')
    >>>int(x)                         #把高精度实数转换为整数
    3
    >>>int('0x22b',16)                #把十六进制数转换为十进制数
    555
    >>>int('22b',16)                  #与上一行代码等价
    555
    >>>int(bin(54321),2)              #二进制与十进制之间的转换
    54321
    >>>int('0b111')                   #非十进制字符串进,必须指定第二个参数
    ValueError: invalid literal for int() with base 10: '0b111'
    >>>int('0b111',0)                 #第二个参数0表示使用字符串隐含的进制
    7
    >>>int('0b111',6)                 #第二个参数应与隐含的进制一致
    ValueError: invalid literal for int() with base 6: '0b111'
    >>>int('0b111',2)
    7
    >>>int('111',6)                   #字符串没有隐含进制
                                      #第二个参数可以为2~36之间的数字
    43

内置函数float()用来将其他类型数据转换为实数,complex()可以用来生成复数。

    >>>float(3)                       #把整数转换为实数
    3.0
    >>>float('3.5')                   #把数字字符串转换为实数
    3.5
    >>>float('inf')                   #无穷大,其中inf不区分大小写
    inf
    >>>complex(3)                     #指定实部
    (3+0j)
    >>>complex(3,5)                   #指定实部和虚部
    (3+5j)
    >>>complex('inf')                 #无穷大
    (inf+0j)
    >>>float('nan')                   #非数字,not a number的缩写
    nan
    >>>complex('nan')
    (nan+0j)
    >>>nan=float('nan')
    >>>nan==float('nan')              #无法比较大小
    False
    >>>nan>=float('nan')
    False
    >>>nan<=float('nan')
    False

(2)ord()和chr()是一对功能相反的函数,ord()用来返回单个字符的Unicode码,而chr()则用来返回Unicode编码对应的字符,str()则直接将其任意类型参数转换为字符串。

        >>>ord('a')                       #查看指定字符的Unicode编码
        97
        >>>chr(65)                        #返回数字65对应的字符
        'A'
        >>>chr(ord('A')+1)                #Python不允许字符串和数字之间的加法操作
        'B'
        >>>chr(ord(’国’)+1)               #支持中文
        '图’
        >>>ord(’董’)                      #这个用法仅适用于Python 3.x
        33891
        >>>ord(’付’)
        20184
        >>>ord(’国’)
        22269
        >>>''.join(map(chr, (33891,20184,22269)))
        '董付国’
        >>>str(1234)                      #直接变成字符串
        '1234'
        >>>str([1,2,3])
        '[1,2,3]'
        >>>str((1,2,3))
        '(1,2,3)'
        >>>str({1,2,3})
        '{1,2,3}'

内置类ascii可以把对象转换为ASCII码表示形式,必要的时候使用转义字符来表示特定的字符。

        >>>ascii('a')
        "'a'"
        >>>ascii(’董付国’)
        "'\\u8463\\u4ed8\\u56fd'"
        >>>eval(_)                        #对字符串进行求值
        '董付国’

内置类bytes用来生成字节串,或者把指定对象转换为特定编码的字节串。

        >>>bytes()                        #生成空字节串
        b''
        >>>bytes(3)                       #生成长度为3的字节串
        b'\x00\x00\x00'
        >>>bytes(’董付国’, 'utf-8')        #把字符串转换为字节串
        b'\xe8\x91\xa3\xe4\xbb\x98\xe5\x9b\xbd'
        >>>bytes(’董付国’, 'gbk')          #可以指定不同的编码格式
        b'\xb6\xad\xb8\xb6\xb9\xfa'
        >>>str(_, 'gbk')                  #使用同样的编码格式进行解码
        '董付国’
        >>>’董付国’.encode('gbk')          #等价于使用bytes()进行转换
        b'\xb6\xad\xb8\xb6\xb9\xfa'
        >>>_.decode('gbk')                #等价于使用str()进行转换
        '董付国’
        >>>x=’董付国’.encode()
        >>>list(x)                        #把字节串转换为列表
        [232,145,163,228,187,152,229,155,189]
        >>>bytes(_)                       #把整数列表转换为字节串
        b'\xe8\x91\xa3\xe4\xbb\x98\xe5\x9b\xbd'
        >>>_.decode()
        '董付国’

(3)list()、tuple()、dict()、set()、frozenset()用来把其他类型的数据转换成为列表、元组、字典、可变集合和不可变集合,或者创建空列表、空元组、空字典和空集合。

        >>>list(range(5))                  #把range对象转换为列表
        [0,1,2,3,4]
        >>>tuple(_)                        #一个下画线表示上一次正确的输出结果
        (0,1,2,3,4)
        >>>dict(zip('1234', 'abcde'))      #创建字典
        {'4': 'd', '2': 'b', '3': 'c', '1': 'a'}
        >>>set('1112234')                  #创建可变集合,自动去除重复
        {'4', '2', '3', '1'} 
        >>>_.add('5')
        >>>_
        {'2', '1', '3', '4', '5'}
        >>>frozenset('1112234')            #创建不可变集合,自动去除重复
        frozenset({'2', '1', '3', '4'})
        >>>_.add('5')                      #不可变集合frozenset不支持元素添加与删除
        AttributeError: 'frozenset'object has no attribute'add'

实际上,这里的list、tuple、dict、set、frozenset以及前面刚刚介绍的int、float、complex、bytes、str都是Python的内置类。之所以可以把这些类像函数一样调用,是因为构造方法的存在。也就是说,上面的操作实际上是间接地调用了这些类的构造方法。另外,由于这些函数的调用会生成新的类型,也往往被称为“工厂函数”。

(4)内置函数type()和isinstance()可以用来判断数据类型,常用来对函数参数进行检查,可以避免错误的参数类型导致函数崩溃或返回意料之外的结果。不过,从另一方面来讲,过多的使用type()和isinstance()函数会在一定程度上影响多态,建议谨慎使用。

        >>>type(3)                           #查看3的类型
        <class'int'>
        >>>type([3])                         #查看[3]的类型
        <class'list'>
        >>>type({3}) in (list, tuple, dict)  #判断{3}是否为list、tuple或dict类型的实例
        False
        >>>type({3}) in (list, tuple, dict, set)
                                                      #判断{3}是否为list、tuple、dict或set的实例
        True
        >>>isinstance(3, int)                #判断3是否为int类型的实例
        True
        >>>isinstance(3j, int)
        False
        >>>isinstance(3j, (int, float, complex))
                                                     #判断3是否为int、float或complex类型的实例
        True

内置函数callable()用来测试对象是否可调用,Python中函数和类都是可调用的,包含__call__()方法的类的对象也是可调用的。另外,Python标准库inspect提供了isclass()、ismethod()、isgenerator()、isroutine()等大量函数用来测试对象类型,感兴趣的读者可以深入挖掘一些,或许会有意外惊喜。

2.4.2 最值与求和

max()、min()、sum()这3个内置函数分别用于计算列表、元组或其他包含有限个元素的可迭代对象中所有元素最大值、最小值以及所有元素之和。sum()默认(可以通过start参数来改变)支持包含数值型元素的序列或可迭代对象,max()和min()则要求序列或可迭代对象中的元素之间可比较大小。

        >>>from random import randint
        >>>a=[randint(1,100) for i in range(10)]    #包含10个[1,100]之间随机数的列表
        >>>print(max(a), min(a), sum(a))            #最大值、最小值、所有元素之和
        >>>sum(a)/len(a)                            #平均值

函数max()和min()还支持default参数和key参数,其中default参数用来指定可迭代对象为空时默认返回的最大值或最小值,而key参数用来指定比较大小的依据或规则,可以是函数或lambda表达式。函数sum()还支持start参数,用来控制求和的初始值。

        >>>max(['2', '111'])               #不指定排序规则
        '2'
        >>>max(['2', '111'], key=len)      #返回最长的字符串
        '111'
        >>>print(max([], default=None))    #对空列表求最大值,返回空值None
        None
        >>>from random import randint
        >>>lst=[[randint(1,50) for i in range(5)] for j in range(30)]
                                           #列表推导式,生成包含30个子列表的列表
                                           #每个子列表中包含5个介于[1,50]区间的整数
        >>>max(*lst, key=sum)              #返回元素之和最大的子列表,略去结果
        >>>max(lst, key=sum)               #与上面的代码等价,这是max()的另一个用法
        >>>max(lst, key=lambda x: x[1])    #所有子列表中第2个元素最大的子列表
        >>>sum(range(1,11))                #sum()函数的start参数默认为0
        55
        >>>sum(range(1,11),5)              #指定start参数为5,等价于5+sum(range(1,11))
        60
        >>>sum([[1,2], [3], [4]], [])      #这个操作占用空间较大,慎用
        [1,2,3,4]
        >>>sum(2**i for i in range(200))   #等比数列前n项的和,1+2+4+8+…+2^199
        1606938044258990275541962092341162602522202993782792835301375
        >>>int('1'*200,2)                  #等价于上一行代码,但速度快很多
        1606938044258990275541962092341162602522202993782792835301375
        >>>int('1'*200,7)                  #比值q为2~36之间的整数时,都可以这样做
        1743639715219059529169816601969468943303198091695038943325023347339187627904043
        7086290637691515606750488442080420910523623438633906139318646917923778899694224
        39576020000
        >>>sum(range(101))                 #101个人开会,互相握手次数,不重复握手
        5050
        >>>101*100//2                      #每个人都与其他所有人握手,但不重复握手
        5050

如果用help()函数查看sum()函数的帮助文档时,会发现sum()函数的最后一个参数是斜线,实际上这个斜线并不是sum()函数的参数,只是用来表明这个函数只接收位置参数,而不允许以关键参数的形式进行传值,如果遇到其他函数或对象方法显示这样的帮助文档也表示同样的含义。这样的函数是用C开发的,并对参数传值形式做了要求,在Python中并不允许定义这样的函数。这涉及Argument Clinic的概念,感兴趣的朋友可以查阅有关资料。

        >>>help(sum)                     #查看sum()函数的帮助
        Help on built-in function sum in module builtins:

        sum(iterable, start=0, /)
            Return the sum of a'start'value (default: 0) plus an iterable of numbers
  
            When the iterable is empty, return the start value.
            This function is intended specifically for use with numeric values and may
            reject non-numeric types.
        >>>sum([1,2,3],4)                #按位置参数对start进行传值
        10
        >>>sum([1,2,3], start=4)         #不允许使用关键参数,引发异常
        TypeError: sum() takes no keyword arguments
        >>>def demo(a, b, /):            #在Python中不允许这样定义函数,语法错误
        SyntaxError: invalid syntax

2.4.3 基本输入输出

input()和print()是Python的基本输入输出函数,前者用来接收用户的键盘输入,后者用来把数据以指定的格式输出到标准控制台或指定的文件对象。不论用户输入什么内容,input()一律作为字符串对待,必要的时候可以使用内置函数int()、float()或eval()对用户输入的内容进行类型转换。

        >>>x=input('Please input: ')
        Please input: 345
        >>>x
        '345'
        >>>type(x)                      #把用户的输入作为字符串对待
        <class'str'>
        >>>int(x)                       #转换为整数
        345
        >>>eval(x)                      #对字符串求值,或类型转换
        345
        >>>x=input('Please input: ')
        Please input: [1,2,3]
        >>>x
        '[1,2,3]'
        >>>type(x)
        <class'str'>
        >>>eval(x)
        [1,2,3]
        >>>x=input('Please input:')     #不论用户输入什么,都作为一个字符串来对待
        Please input:'hello world'
        >>>x                            #如果本来就想输入字符串,就不用再输入引号了
        "'hello world'"
        >>>eval(x)
        'hello world'

内置函数print()用于输出信息到标准控制台或指定文件,语法格式为

print(value1, value2, …, sep='', end='\n', file=sys.stdout, flush=False)

其中,sep参数之前为需要输出的内容(可以有多个); sep参数用于指定数据之间的分隔符,默认为空格;file参数用于指定输出位置,默认为标准控制台,也可以重定向输出到文件。例如:

        >>>print(1,3,5,7, sep='\t')        #修改默认分隔符
        1  3  5  7
        >>>for i in range(10):             #修改end参数,每个输出之后不换行
            print(i, end='')
        0 1 2 3 4 5 6 7 8 9
        >>>with open('test.txt', 'a+') as fp:
            print('Hello world! ', file=fp)#重定向,将内容输出到文件中

另外,Python标准库sys还提供了read()和readline()方法用来从键盘接收指定数量的字符。例如:

        >>>import sys
        >>>x=sys.stdin.read(5)           #读取5个字符,如果不足5个,等待继续输入
        asd
        s
        >>>x
        'asd\ns'
        >>>x=sys.stdin.read(5)           #读取5个字符,如果超出5个,截断
        abcdefghijklmnop
        >>>x
        'abcde'
        >>>x=sys.stdin.read(5)           #从缓冲区内继续读取5个字符
        >>>x
        'fghij'
        >>>x=sys.stdin.read(5)
        >>>x
        'klmno'
        >>>x=sys.stdin.read(5)           #缓冲区内不足5个字符,就等待用户继续输入
        1234
        >>>x
        'p\n123'
        >>>x=sys.stdin.readline()        #从缓冲区内读取字符,遇到换行符就结束
        >>>x
        '4\n'
        >>>x=sys.stdin.readline()
        abcd
        >>>x
        'abcd\n'
        >>>x=sys.stdin.readline(13)      #如果缓冲区内容比需要的少,遇到换行符也结束
        abcdefg
        >>>x
        'abcdefg\n'
        >>>x=sys.stdin.readline(13)      #如果缓冲区内容比需要的多,就截断
        abcdefghijklmnopqrst
        >>>x
        'abcdefghijklm'
        >>>x=sys.stdin.readline(13)      #从缓冲区继续读取
        >>>x
        'nopqrst\n'

Python标准库pprint则提供了更加友好的输出函数(pretty printer)pprint(),可以更好地控制输出格式,如果要输出的内容多于一行则会自动添加换行和缩进来更好地展示内容的结构。例如:

        >>>import pprint
        >>>t=[[[['black', 'cyan'], 'white', ['green', 'red']], [['magenta', 'yellow'],
        'blue']]]
        >>>pprint.pprint(t)               #默认width=80
        [[[['black', 'cyan'], 'white', ['green', 'red']],
          [['magenta', 'yellow'], 'blue']]]
        >>>pprint.pprint(t, width=50)
        [[[['black', 'cyan'], 'white', ['green', 'red']],
          [['magenta', 'yellow'], 'blue']]]
        >>>pprint.pprint(t, width=30)     #根据需要进行换行和缩进
        [[[['black', 'cyan'],
            'white',
            ['green', 'red']],
          [['magenta', 'yellow'],
            'blue']]]

2.4.4 排序与逆序

sorted()对列表、元组、字典、集合或其他可迭代对象进行排序并返回新列表,reversed()对可迭代对象(生成器对象和具有惰性求值特性的zip、map、filter、enumerate等类似对象除外)进行翻转(首尾交换)并返回可迭代的reversed对象。

        >>>x=list(range(11))
        >>>import random
        >>>random.shuffle(x)                            #打乱顺序
        >>>x
        [2,4,0,6,10,7,8,3,9,1,5]
        >>>sorted(x)                                    #以默认规则排序
        [0,1,2,3,4,5,6,7,8,9,10]
        >>>sorted(x, key=lambda item:len(str(item)), reverse=True)
                                                        #按转换成字符串以后的长度降序排列
        [10,2,4,0,6,7,8,3,9,1,5]
        >>>sorted(x, key=str)                           #按转换成字符串以后的大小升序排列
        [0,1,10,2,3,4,5,6,7,8,9]
        >>>x                                            #不影响原来列表的元素顺序
        [2,4,0,6,10,7,8,3,9,1,5]
        >>>x=['aaaa', 'bc', 'd', 'b', 'ba']
        >>>sorted(x, key=lambda item: (len(item), item))
                                                        #先按长度排序,长度一样的正常排序
        ['b', 'd', 'ba', 'bc', 'aaaa']
        >>>reversed(x)                                  #逆序,返回reversed对象
        <list_reverseiterator object at 0x0000000003089E48>
        >>>list(reversed(x))                            #reversed对象是可迭代的
        [5,1,9,3,8,7,10,6,0,4,2]

2.4.5 枚举与迭代

enumerate()函数用来枚举可迭代对象中的元素,返回可迭代的enumerate对象,其中每个元素都是包含索引和值的元组。

        >>>list(enumerate('abcd'))                            #枚举字符串中的元素
        [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
        >>>list(enumerate(['Python', 'Greate']))              #枚举列表中的元素
        [(0, 'Python'), (1, 'Greate')]
        >>>list(enumerate({'a':97, 'b':98, 'c':99}.items()))  #枚举字典中的元素
        [(0, ('c',99)), (1, ('a',97)), (2, ('b',98))]
        >>>for index, value in enumerate(range(10,15)):       #枚举range对象中的元素
            print((index, value), end='')
        (0,10) (1,11) (2,12) (3,13) (4,14)

内置函数enumerate()还支持一个start参数,用来指定枚举时的索引起始值。

        >>>for item in enumerate(range(5),6):              #索引从6开始
            print(item, end=', ')
        (6,0), (7,1), (8,2), (9,3), (10,4),

iter()函数用来返回指定对象的迭代器,有两种用法:iter(iterable)和iter(callable, sentinel),前者要求参数必须为序列或者有自己的迭代器,后者会持续调用参数callable直至其返回sentinel。next()函数用来返回可迭代对象中的下一个元素,适用于生成器对象以及zip、enumerate、reversed、map、filter、iter等对象,等价于这些对象的__next__()方法。

    >>>x=[1,2,3]
    >>>next(x)
    TypeError: 'list'object is not an iterator
    >>>y=iter(x)                                   #根据列表创建迭代器对象
    >>>next(y)
    1
    >>>next(y)
    2
    >>>x=range(1,100,3)
    >>>next(x)                                     #range对象不是迭代器对象
    TypeError: 'range'object is not an iterator
    >>>x=iter(x)                                   #把range对象转换为迭代器对象
    >>>next(x)
    1
    >>>next(x)
    4
    >>>x={1,2,3}
    >>>y=iter(x)                                   #根据集合创建迭代器对象
    >>>next(y)
    1
    >>>class T:
      def__init__(self, seq):
        self.__data=list(seq)
      def__iter__(self):                           #特殊方法,对应于内置函数iter()
        return iter(self.__data)
    >>>t=T(range(3))
    >>>next(t)                                     #对象t不可迭代
    TypeError: 'T'object is not an iterator
    >>>ti=iter(t)                                  #根据t创建迭代器对象
    >>>next(ti)
    0
    >>>next(ti)
    1
    >>>from queue import Queue
    >>>q=Queue()                                   #创建队列对象
    >>>for i in range(5):
        q.put(i)                                   #依次放入5个数字
    >>>q.put('END')                                #放入结束标志
    >>>def test():
        return q.get()
    >>>for item in iter(test, 'END'):              #持续执行test()函数,直到返回’END'
        print(item, end='')
    0 1 2 3 4
    >>>x=map(int, '1234')                          #map对象支持next()函数
    >>>next(x)
    1
    >>>x.__next__()                                #特殊方法,等价于next(x)
    2
    >>>x=reversed('12345678')                      #reversed对象支持next()函数
    >>>for i in range(4):
        print(next(x), end='')
    8765
    >>>x=enumerate({'a':97, 'b':98, 'c':99}.items())
                                                   #enumerate对象支持next()函数
      >>>next(x)
      (0, ('c',99))

2.4.6 map()、reduce()、filter()

map()、reduce()、filter()是Python中很常用的几个函数,也是Python支持函数式编程的重要体现。要注意的是,在Python 3.x中,reduce()不是内置函数,而是放到了标准库functools中,需要先导入再使用。

函数式编程把问题分解为一系列的函数操作,输入依次流入和流出一系列函数,最终完成预定任务和目标。在理想状态下,每个函数只是接收输入并在简单处理后产生输出,这些输出完全取决于输入和函数指定的操作并且仅通过函数返回值来体现,而不会引入或造成任何副作用(例如,输入和一些内部状态共同决定输出、修改输入的数据、把一些数据和状态输出到屏幕或文件等)。函数式编程具有很多优点,例如,容易构建一个数学模型来证明程序的正确性,程序更加模块化,容易调试和测试,代码复用率高,等等。

内置函数map()把一个函数func依次映射到序列或迭代器对象的每个元素上,并返回一个可迭代的map对象作为结果,map对象中每个元素是原序列中元素经过函数func处理后的结果,map()函数不对原序列或迭代器对象做任何修改。

        >>>list(map(str, range(5)))                   #把列表中元素转换为字符串
        ['0', '1', '2', '3', '4']
        >>>def add5(v):                               #单参数函数
            return v+5
        >>>list(map(add5, range(10)))                 #把单参数函数映射到一个序列的所有元素
        [5,6,7,8,9,10,11,12,13,14]
        >>>def add(x, y):                             #可以接收2个参数的函数
            return x+y
        >>>list(map(add, range(5), range(5,10)))      #把双参数函数映射到两个序列上
        [5,7,9,11,13]
        >>>list(map(lambda x, y: x+y, range(5), range(5,10)))
        [5,7,9,11,13]
        >>>def myMap(lst, value):                     #自定义函数
            return map(lambda item: item+value, lst)
        >>>list(myMap(range(5),5))                    #每个数字加5
        [5,6,7,8,9]
        >>>list(myMap(range(5),8))                    #每个数字加8
        [8,9,10,11,12]
        >>>def myMap(iterable, op, value):            #自定义函数
            if op not in'+-*/':                       #实现序列与数字的四则运算
              return'Error operator'
            func=lambda i:eval(repr(i)+op+repr(value))
            return map(func, iterable)
        >>>list(myMap(range(5), '+',5))
        [5,6,7,8,9]
        >>>list(myMap(range(5), '-',5))
        [-5, -4, -3, -2, -1]
        >>>list(myMap(range(5), '*',5))
        [0,5,10,15,20]
        >>>list(myMap(range(5), '/',5))
        [0.0,0.2,0.4,0.6,0.8]
        >>>import random
        >>>x=random.randint(1,1e30)                   #生成指定范围内的随机整数
        >>>x
        839746558215897242220046223150
        >>>list(map(int, str(x)))                     #提取大整数每位上的数字
        [8,3,9,7,4,6,5,5,8,2,1,5,8,9,7,2,4,2,2,2,0,0,4,6,2,2,3,1,5,0]

标准库functools中的函数reduce()可以将一个接收2个参数的函数以迭代累积的方式从左到右依次作用到一个序列或迭代器对象的所有元素上,并且允许指定一个初始值。例如,reduce(lambda x, y:x+y, [1,2,3,4,5])计算过程为((((1+2)+3)+4)+5),第一次计算时x为1而y为2,再次计算时x的值为(1+2)而y的值为3,再次计算时x的值为((1+2)+3)而y的值为4,以此类推,最终完成计算并返回((((1+2)+3)+4)+5)的值。

        >>>from functools import reduce
        >>>seq=list(range(1,10))                      #也可以不用转换为列表
        >>>reduce(add, seq)                           #add是上一段代码中定义的函数
        45
        >>>reduce(lambda x, y: x+y, seq)              #使用lambda表达式实现相同功能
        45

上面实现数字累加的代码运行过程如图2-1所示。

        >>>import operator                            #标准库operator提供了大量运算
        >>>operator.add(3,5)                          #可以像普通函数一样直接调用
        8 
        >>>reduce(operator.add, seq)                  #使用add运算
        45
        >>>reduce(operator.add, seq,5)                #指定累加的初始值为5
        50
        >>>reduce(operator.mul, seq)                  #乘法运算
        362880
        >>>reduce(operator.mul, range(1,6))           #5的阶乘
        120
        >>>reduce(operator.add, map(str, seq))        #转换成字符串再累加
        '123456789'
        >>>''.join(map(str, seq))                     #使用join()方法实现字符串连接
        '123456789'
        >>>reduce(operator.add, [[1,2], [3]], [])     #这个操作占用空间较大,慎用
        [1,2,3]

与上面代码中最后演示的用法类似,作为一种技巧,reduce()函数还支持下面的用法(感谢浙江省浦江中学方春林老师提供本例用法):

图2-1 reduce()函数执行过程示意图

        >>>from random import randint
        >>>lst=[randint(1,10) for i in range(50)]    #随机数列表
        >>>def tjNum(dic, k):                        #统计元素出现次数
            if k in dic:
              dic[k]+=1
            else:
              dic[k]=1
            return dic
        >>>reduce(tjNum, lst, {})
        {1: 6,2: 3,3: 6,4: 3,5: 4,6: 7,7: 5,8: 5,9: 6,10: 5}

内置函数filter()将一个单参数函数作用到一个序列上,返回该序列中使得该函数返回值为True的那些元素组成的filter对象,如果指定函数为None,则返回序列中等价于True的元素。

      >>>seq=['foo', 'x41', '? ! ', '***']
      >>>def func(x):
          return x.isalnum()                           #测试是否为字母或数字
      >>>filter(func, seq)                             #返回filter对象
      <filter object at 0x000000000305D898>
      >>>list(filter(func, seq))                       #把filter对象转换为列表
      ['foo', 'x41']
      >>>seq                                           #不对原列表做任何修改
      ['foo', 'x41', '? ! ', '***']
      >>>[x for x in seq if x.isalnum()]               #使用列表推导式实现相同功能
      ['foo', 'x41']
      >>>list(filter(lambda x: x.isalnum(), seq))      #使用lambda表达式实现相同功能
      ['foo', 'x41']
      >>>list(filter(None, [1,2,3,0,0,4,0,5]))         #指定函数为None
      [1,2,3,4,5]

2.4.7 range()

range()是Python开发中非常常用的一个内置函数,语法格式为range([start, ] end[, step]),有range(stop)、range(start, stop)和range(start, stop, step)3种用法。该函数返回具有惰性求值特点的range对象,其中包含左闭右开区间[start, end)内以step为步长的整数。参数start默认为0, step默认为1。

        >>>range(5)                                     #start默认为0, step默认为1
        range(0,5)
        >>>list(_)
        [0,1,2,3,4]
        >>>list(range(1,10,2))                          #指定起始值和步长
        [1,3,5,7,9]
        >>>list(range(9,0, -2))                         #步长为负数时,start应比end大
        [9,7,5,3,1]

在循环结构中经常使用range()函数来控制循环次数,例如:

        >>>for i in range(4):                           #循环4次
            print(3, end='')
        3333

当然,也可以使用range()函数来控制数值范围,例如,下面的程序片段可以用来输出200以内能被17整除的最大正整数。

        for i in range(200,0, -1):
              if i% 17==0:
                  print(i)
                  break

2.4.8 zip()

zip()函数用来把多个可迭代对象中的元素压缩到一起,返回一个可迭代的zip对象,其中每个元素都是包含原来的多个可迭代对象对应位置上元素的元组,最终结果中包含的元素个数取决于所有参数序列或可迭代对象中最短的那个。可以这样理解这个函数,把多个序列或可迭代对象中的所有元素左对齐,然后像拉拉链一样往右拉,把所经过的每个序列中相同位置上的元素都放到一个元组中,只要有一个序列中的所有元素都处理完了就不再拉拉链了,返回包含若干元组的zip对象。

        >>>list(zip('abcd', [1,2,3]))                  #压缩字符串和列表
        [('a',1), ('b',2), ('c',3)]
        >>>list(zip('abcd'))                           #对1个序列也可以压缩
        [('a', ), ('b', ), ('c', ), ('d', )]
        >>>list(zip('123', 'abc', ', .! '))            #压缩3个序列
        [('1', 'a', ', '), ('2', 'b', '.'), ('3', 'c', '! ')]
        >>>for item in zip('abcd', range(3)):          #zip对象是可迭代的
            print(item)
        ('a',0)
        ('b',1)
        ('c',2)
        >>>x=zip('abcd', '1234')
        >>>list(x)
        [('a', '1'), ('b', '2'), ('c', '3'), ('d', '4')]
        >>>list(x)                                     #zip对象只能遍历一次
        []

2.4.9 eval()、exec()

内置函数eval()用来计算字符串的值,在有些场合也可以用来实现类型转换的功能,这个用法在2.4.1节和2.4.3节出现过多次,这里不再重复。除此之外,eval()也可以对字节串进行求值,还可以执行内置函数compile()编译生成的代码对象。

        >>>eval(b'3+5')
        8
        >>>eval(compile('print(3+5)', 'temp.txt', 'exec'))
        8
        >>>eval('9')                               #把数字字符串转换为数字
        9
        >>>eval('09')                              #抛出异常,不允许以0开头的数字
        SyntaxError: invalid token
        >>>int('09')                               #这样转换是可以的
        9

另外,由于eval()并不对参数字符串进行安全性检查,如果精心构造一些语句的话可能会引发安全漏洞,应尽量使用标准库ast提供的安全求值函数literal_eval()。

     >>>eval("__import__('os').startfile(r'C:\Windows\\notepad.exe')")
                                                #打开记事本程序
     >>>import ast
     >>>ast.literal_eval("__import__('os').startfile(r'C:\Windows\\notepad.exe')")
                                                #无法执行,引发异常
     ValueError: malformed node or string:<_ast.Call object at 0x00000000033E2C18>

内置函数exec()用来执行指定的Python源代码或者由compile()编译的代码对象。

     >>>exec('x=3')                             #执行语句x=3
     >>>x
     3
     >>>exec('help(sum)')                       #查看内置函数sum()的帮助文档
     >>>obj=compile('for i in range(5):print(i, end=" ")', 'temp.txt', 'exec')
     >>>obj                                     #内置函数compile()生成的代码对象
     <code object<module>at 0x00000000033A29C0, file "temp.txt", line 1>
     >>>exec(obj)                               #使用exec()执行代码对象
     01234
     >>>eval(obj)                               #也可以使用eval()执行代码对象
     01234
     >>>obj=compile('x=666', 'temp.txt', 'exec')
     >>>eval(obj)
     >>>x
     666
     >>>for i, v in enumerate(range(5,10)):     #动态创建变量名
         exec('element'+str(i)+'='+str(v))

     >>>element0
     5
     >>>element1
     6