美文网首页
第3章 Python数据结构、函数

第3章 Python数据结构、函数

作者: whybask | 来源:发表于2019-09-28 22:55 被阅读0次

    以下内容主要学习自《利用Python进行数据分析》

    第3章 内建数据结构、函数及文件

    数据结构和序列

    序列是Python中最基本的数据结构,简单但强大。序列中的每个元素都分配一个数字:它的位置,或索引,第一个索引是0,第二个索引是1,依此类推。精通这些数据结构是成为优秀Python编程者的必要条件

    Python数据结构

    列表

    列表是一组可变的Python对象序列,可以简单的理解为可变长度的数组,但比数组更强大的是:列表中的元素可以是不同类型的对象

    创建列表

    创建列表最简单的方法就是在方括号中添加元素,并使用逗号隔开:

    In[1] : lst = ['a', 'b', 'c', None, '998']
    In[2] : lst
    out[2] : ['a', 'b', 'c', None, '998']
    

    也可以用list函数将任意序列或迭代器转换为列表:

    In [1]: lst1 = list('hello')
    
    In [2]: lst1
    Out[2]: ['h', 'e', 'l', 'l', 'o']
    
    In [3]: lst2 = list(range(10))
    
    In [4]: lst2
    Out[4]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    访问列表

    通过索引号访问列表,注意:索引号从0开始

    In [1]: lst = ['a', 'b', 'c', None, 985]
    
    # 用索引获取单个元素
    In [2]: lst[1]  
    Out[2]: 'b'
    
    # 获取多个元素,获取的数量等于两个索引的差
    In [3]: lst[1:3]
    Out[3]: ['b', 'c']
    
    # 不指定开始索引,将从开头获取
    In [4]: lst[:2]
    Out[4]: ['a', 'b']
    
    # 不指定结束索引,将获取到结尾
    In [5]: lst[2:]
    Out[5]: ['c', None, 985]
    
    # 索引为负数,将倒数获取
    In [6]: lst[-1]
    Out[6]: 985
    
    # 获取多个元素,索引为负数
    In [7]: lst[-3:-1]
    Out[7]: ['c', None]
    
    # 不指定开始索引,结束索引为负数
    In [8]: lst[:-1]
    Out[8]: ['a', 'b', 'c', None]
    
    修改列表

    使用append方法在列表末尾添加新的对象

    in[9]: lst.append(211)
    
    in[10]: lst
    out[10]: ['a', 'b', 'c', None, 985, 211]
    

    使用insert方法在将新的对象添加到列表的指定位置

    in[11]: lst.insert(3, 'd')
    
    in[12]: lst
    out[12]: ['a', 'b', 'c', 'd', None, 998, 211]
    

    使用reverse方法反向列表中元素

    In [13]: lst.reverse()
    
    In [14]: lst
    Out[14]: [211, 998, None, 'd', 'c', 'b', 'a']
    

    使用sort方法对原列表进行排序,注意:若列表中的对象类型不一样,使用默认参数的排序可能会失败、报错。排序方法的高级应用请参考教材。

    In [15]: lst_num = [3, 7, 1, 9, 12]
    
    In [16]: lst_num.sort()
    
    In [17]: lst_num
    Out[17]: [1, 3, 7, 9, 12]
    
    删除列表

    使用pop方法移除列表中的某个元素(默认最后一个元素),并且返回该元素的值:

    In [18]: item = lst.pop(5)
    
    In [18]: item
    Out[18]: 998
    
    In [19]: lst
    Out[19]: ['a', 'b', 'c', 'd', None, 211]
    

    使用pop方法移除列表中的某个元素,注意不是根据索引号移除:

    In [20]: lst.remove(None)
    
    In [21]: lst
    Out[21]: ['a', 'b', 'c', 'd', 211]
    

    如果对象并不在列表中,使用pop方法将会报错,因此可以用in先判断是否包含某对象

    In [22]: 'a' in lst
    Out[22]: True
    
    In [23]: 'f' in lst
    Out[23]: False
    
    列表内置函数

    使用len函数获取列表中元素的总数
    使用max函数获取列表元素最大值
    使用min函数获取列表元素最小值

    In [24]: lst = ['a', 'b', 'c', 'd']
    
    In [25]: len(lst)
    Out[25]: 4
    
    In [26]: max(lst)
    Out[26]: 'd'
    
    In [27]: min(lst)
    Out[27]: 'a'
    

    元组

    元组是一种固定、不可变的Python对象序列,这意味着元组内的对象是不能改变的,可理解为不可变的数组。

    创建元组

    创建元组最简单的办法就是在括号中添加元素,并使用逗号隔开:

    In[1] : tup = (4, 5 ,6)
    In[2] : tup
    out[2] : (4, 5, 6)
    

    也可以用tuple函数将任意序列或迭代器转换为元组:

    In[3] : tuple([4, 0, 2])
    out[3] : (4, 0, 2)
    
    In[4] : tup = tuple('hello')
    
    In[5] : tup
    out[5] : ('h', 'e', 'l', 'l', 'o')
    
    访问元组

    访问元组的元素的方法,与“访问列表”的方法一致,可参考上方的“访问列表”。

    修改元组

    由于元组是不可变的,因此不能对元组中的单个元素进行修改。

    可以使用+号连接元组来生成更长的元组:

    In[6] : (4, None, 'foo') + (6, 0) + ('bar')
    out[6] : (4, None, 'foo', 6, 0, bar')
    

    将元组乘以整数,将生成含有多分拷贝的元组:

    In[7] : ('foo', 'bar') * 3
    out[7] : ('foo', 'bar', 'foo', 'bar', 'foo', 'bar')
    
    删除元组

    元组中的元素值是不允许删除的,但我们可以使用del语句来删除整个元组

    In [8]: del tup
    
    In [9]: tup
    NameError: name 'tup' is not defined
    
    元组内置函数

    使用len函数获取元组中元素的总数;
    使用max函数获取元组元素最大值;
    使用min函数获取元组元素最小值。

    In [10]:  tup = tuple('hello')
    
    In [11]: len(tup)
    Out[11]: 5
    
    In [12]: max(tup)
    Out[12]: 'o'
    
    In [13]: min(tup)
    Out[13]: 'e'
    

    字典

    字典也可以称为哈希表或关联数组。字典的大小是可变的,其中包含的每一个元素以键值对形式呈现。字典的值可以是任何Python对象,但字典的键不可变,因此可以用数字、字符串、元组作为键。

    创建字典

    创建字典最简单的办法就是在大括号中添加元素,并使用逗号隔开,每个元组的键和值用冒号隔开:

    In[1] : d1 = {'a' : 'some value', 'b' : [1 ,2, 3, 4]}
    In[2] : d1
    out[2] : {'a' : 'some value', 'b' : [1 ,2, 3, 4]}
    

    从已有的两个序列生成字典

    In[3]: list1 = ['a', 'b', 'c']
    In[4]: list2 = [198, 236, 701]
    
    In[5]: d2 = dict(zip(list1, list2))
    
    In[16]: d2
    Out[6]: {'a': 198, 'b': 236, 'c': 701}
    
    访问字典

    根据键获取值

    In[7]: d1['b']
    Out[7]: [1, 2, 3, 4]
    

    如果访问一个字典中不存在的键,那么会报告KyeError异常,为避免这类错误,可以使用in运算符检查字典中是否含有一个键。

    In[8]: 'c' in d1
    Out[8]: False
    

    另外,使用get方法可以在字典中不包含某个键时,返回默认值

    In [9]: d1.get('c')  # 未指定默认值
    Out[9]: None
    
    In [10]: d1.get('c', 0)  # 指定默认值
    Out[10]: 0
    

    使用keys方法能获得字典所有的键,使用values能获得字典所有的值

    In[11]: list(d1.keys())
    Out[11]: ['a', 'b']
    
    In[12]: list(d1.values())
    Out[12]: ['some value', [1, 2, 3, 4]]
    
    修改字典

    字典不允许同一个键出现两次(同一个字典中的键都是唯一的),因此,当字典中不存在某键时,会给字典增加新的键值对;如果该键已经存在,那么就会修改它的值。

    In[13]: d1['c'] = 17  # 添加新键
    In[13]: d1
    Out[14]: {'a' : 'some value', 'b' : [1 ,2, 3, 4], 'c' : 17}
    
    In[15]: d1['c'] = 23  # 修改已有键的值
    Out[15]: {'a': 'some value', 'b': [1, 2, 3, 4], 'c': 23}
    

    使用update方法可以合并两个字典。注意:如果原字典中有相同的键,那么它的值将被覆盖,否则,就增加新的键值。

    In[16]: d1.update({'b': 'foo', 'd' : 3.1415})
    
    In[17]: d1
    Out[17]: {'a': 'some value', 'b': 'foo', 'c': 23, 'd': 3.1415}
    
    删除字典

    使用del删除字典

    In [18]: del d1['d']
    
    In [19]: d1
    Out[19]: {'a': 'some value', 'b': 'foo', 'c': 23}
    

    使用clear方法删除字典内所有元素

    In [20]: d1.clear()
    
    In [21]: d1
    Out[21]: {}
    
    字典内置函数

    使用len函数返回字典中键值对的个数

    In[22]: d1 = {'a': 198, 'b': 236, 'c': 701}
    
    In[23]: len(d1)
    Out[23]: 3
    

    使用str函数返回字典的字符串形式

    In [24]: str(d3)
    Out[24]: "{'a': 198, 'b': 236, 'c': 701}"
    

    集合

    集合是一种内容唯一的序列,而列表、元组中的内容是可以重复的。

    创建集合

    直接创建集合

    In [1]: s1 = {'张三', '李四', '李四', '王五', '杨六', '王五'}
    
    In [2]: s1
    Out[2]: {'张三', '李四', '杨六', '王五'}
    

    使用set函数创建集合

    In [3]: s2 = set([2, 2, 2, 1, 3, 3])
    
    In [4]: s2
    Out[4]: {1, 2, 3}
    
    访问集合

    如果想象访问列表、元组那样,使用索引来访问集合,是不行的。会报告TypeError异常

    In [5]: s1[2]
    TypeError: 'set' object does not support indexing
    

    如确实需要获得集合中的元素,可参考如下代码

    In [6]: l1 = list(s1)  # 把集合转换为列表,然后访问
    
    In [7]: l1[2]
    Out[7]: '李四'
    
    In [8]: for item in s1:  # 或者采用遍历的方式访问
        ...:     print(item)
        ...:
    王五
    张三
    李四
    杨六
    
    修改集合

    添加单个元素

    In [9]: s1.add('赵七')
    
    In [10]: s1
    Out[10]: {'张三', '李四', '杨六', '王五', '赵七'}
    

    添加多个元素

    In [11]: s1.update(['孙九', '孔八'])
    
    In [12]: s1
    Out[12]: {'孔八', '孙九', '张三', '李四', '杨六', '王五', '赵七'}
    
    删除集合

    使用remove方法删除集合中的某个元素

    In [13]: s1.remove('孔八')
    
    In [14]: s1
    Out[14]: {'孙九', '张三', '李四', '杨六', '王五', '赵七'}
    

    此外还有一个方法也是移除集合中的元素,且如果元素不存在,不会发生错误

    In [15]: s1.discard('孔八')
    

    使用clear方法删除集合内所有元素

    In [16]: s1.clear()
    
    In [17]: s1
    Out[17]: set()
    
    集合运算

    使用union方法得到两个集合的并集

    In [18]: s1 = {1, 3, 5, 6, 8}
    In [19]: s2 = {2, 4, 5, 6, 7, 9}
    
    In [20]: s1.union(s2)
    Out[20]: {1, 2, 3, 4, 5, 6, 7, 8, 9}
    

    使用intersection方法得到两个集合的交集

    In [21]: s1.intersection(s2)
    Out[21]: {5, 6}
    

    使用difference方法得到两个集合的差集

    In [22]: s1.difference(s2)
    Out[22]: {1, 3, 8}
    

    使用symmetric_difference方法得到两个集合不重复的元素的集合

    In [23]: s1.symmetric_difference(s2)
    Out[23]: {1, 2, 3, 4, 7, 8, 9}
    

    使用issubset方法判断一个集合是否是另一个集合的子集

    In [24]: s3 = {3, 8}
    
    In [25]: s3.issubset(s1)
    Out[25]: True
    

    使用issuperset方法判断一个集合是否包含另一个集合

    In [26]: s1.issuperset(s3)
    Out[26]: True
    

    使用isdisjoint方法判断两个集合是否没有交集

    In [27]: s2.isdisjoint(s3)
    Out[27]: True
    
    集合内置函数

    使用len函数返回集合中元素的个数

    In[28]: len(s1)
    Out[28]: 5
    

    使用str函数返回字典的字符串形式

    In [24]: str(s1)
    Out[24]:  '{1, 3, 5, 6, 8}'
    

    内建序列函数

    拆包

    Python提供拆包操作,可以将等号右边的序列包含的元素赋值给变量:

    In [1]: lst = [4, 5, 6]
    
    In [2]: a, b, c = lst
    
    In [3]: b
    Out[3]: 5
    

    拆包的一个常用场景就是遍历元组或列表中的元素,请参考下方的示例:

    In [4]: seq = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
    
    In [5]: for a, b, c in seq:
        ...:     print('a={0}, b={1}, c={2}'.format(a, b, c))
        ...:
    a=1, b=2, c=3
    a=4, b=5, c=6
    a=7, b=8, c=9
    

    注意:拆包操作不仅仅适用于元组,同样适用于列表、集合。上面的示例还演示了元组是可以嵌套的,这同样适用于列表,即可以构建多维序列。

    如果你想从序列中“部分拆包”,可以用如下的方法

    In [6]: s1 = {1, 3, 5, 6, 8}
    
    In [7]: a, b, *other = s1
    
    In [8]: b
    Out [8]: 3
    
    In [9]: other
    Out[9]: [5, 6, 8]
    
    # other部分有时是想丢弃的部分,很多Python编程者会使用下划线表示不想要的变量
    In [10]: a, b, *_ = s1
    
    enumerate

    我们经常需要在遍历一个序列的同时,追踪当前元素的索引,一种老套的方法如下

    i = 0
    for value in collection:
        # 使用value做点事
        i+=1
    

    由于这种场景很常见,所以Python内建了enumerate函数,通过返回(i, value)元组的方法来简化代码

    for i, value in enumerate(collection):
        # 使用value做点事
    
    zip

    列表、集合和字典的推导式

    推导式是最受欢迎的Python语言特性之一。它用一种简明的表达式完成从某个列表过滤元素、从而生成一个新的列表。
    列表推导式的基本形式为:[expr for val in collection if condition]
    集合推导式的基本形式为:{expr for val in collection if condition}
    字典推导式的基本形式为:{key-expr : val-expr for val in collection if condition}

    列表推导式案例:从给定的字符串列表、选出长度大于3的字符串,并将字符改为大写:

    In [1]: strs = ['c', 'python', 'c++', 'java', 'c#']
    In [2]: result = [x.upper() for x in strs if len(x)>=3]
    
    In[3]: result
    Out[3]: ['PYTHON', 'C++', 'JAVA']
    

    以上的案例,与下面的for循环伪代码是等价的:

    result = []
    for val in collection:
        if condition:
            result.append(expr)
    

    嵌套集合推导式案例:从二维列表中,选择姓名包含s、且不重复的名字。

    In [4]: all_data = [['Emma', 'Warren', 'Ben', 'Jason', 'Kevin', 'Sophia', 'Joyce', 'Ashley', 'Peter', 'May', 'Abel', 'Ivy', 'Hailey', 'Stella', 'Gloria', 'Denny'], ['Amy', 'Jessie', 'Lucy', 'Johnny', 'Amanda', 'Jennifer', 'Hailey', 'Abby', 'Albert', 'Bruce', 'Paul', 'Charles'], ['Denny', 'Andrew', 'Amanda', 'Abel', 'Kenny', 'Ben', 'Evan', 'Bill', 'Peter', 'Jessie', 'Jason', 'Gloria']]
    
    In [5]: result = {name for names in all_data for name in names if (name.lower()).count('s')>=1}
    
    In [6]: str(result)
    Out[6]: "{'Charles', 'Ashley', 'Jessie', 'Stella', 'Jason', 'Sophia'}"
    

    请牢记:推导式for表达式的嵌套顺序,应当和你写嵌套for循环的顺序一致

    函数

    函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。

    def 函数名(参数列表):
        函数体
        return xx
    

    定义Python函数的规则:

    1. 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()、冒号。
    2. 任何传入参数和自变量必须放在圆括号中间。
    3. 函数内容要缩进。
    4. return 关键字返回一个值给调用方。没有return相当于返回 None。

    返回多个值

    使用Python编程时,你一定会喜欢上它可以从函数返回多个值。在数据分析和其它科研应用中,会经常需要返回多个值。Python的函数实质上是返回了一个元组对象,而后又拆包为多个结果变量。

    In [1]: def f():
                    a=5
                    b=6
                    c=7
                    return a,b,c
    
    In [2]: x,y,z = f()
    
    In [3]: x
    Out[3]: 5
    

    函数是对象

    在Python中,函数也是个对象。这种特性使Python可以非常容易的实现在其它语言中较难实现的代码构造。

    字符串数据清洗案例:假设有一些姓名数据,要去除每个字符串收尾的空格、去除每个字符串中的特殊符号,并把每个字符串的首字母大写。

    传统的编程思路是:编写一个函数、完成既定功能的封装,如下所示:

    In [1]: import  re  # 引入正则表达式模块
    
    In [2]: def clean_strings(strings):
                result = []
                for value in strings:
                   value = value.strip()  # 去除空格
                   value = re.sub('[!#?]', '', value)  # 使用正则表达式模块,替换特殊字符
                   value = value.title()  # 把首字母转换为大写
                   result.append(value)
               return result
    
    In [3]: names = [' emma   ', 'Warren!', '#ben', '   jason', '??Kevin', 'sophia    ', '?joyce!']
    
    In [4]: clean_strings(names)
    Out[4]: ['Emma', 'Warren', 'Ben', 'Jason', 'Kevin', 'Sophia', 'Joyce']
    

    在Python中,实现以上的需求,还可以这样写:

    In [1]: import  re  # 引入正则表达式模块
    
    In [2]: def remove_punctuation(value):
                return re.sub('[!#?]', '', value)  # 使用正则表达式模块,替换特殊字符
    
    In [3]: def clean_strings(strings, ops):
                result = []
                for value in strings:
                    for function in ops:
                        value = function(value)
                    result.append(value)
                return result
    
    In [4]: clean_ops = [str.strip, remove_punctuation, str.title]  # 把函数名组装在列表中
    
    In [5]: names = [' emma   ', 'Warren!', '#ben', '   jason', '??Kevin', 'sophia    ', '?joyce!']
    
    In [6]: clearn_strings(names, clean_ops)
    Out[6]: ['Emma', 'Warren', 'Ben', 'Jason', 'Kevin', 'Sophia', 'Joyce']
    

    因为“在Python中,函数也是个对象”,所以,在上面的例子中,可以把函数名组装在列表中,然后遍历调用。像这种更为函数化的模式,使clearn_strings函数具有更强的复用性和通用性。

    匿名(Lambda)函数

    匿名(Lambda)函数是一种通过单个语句生成函数的方式。和用def关键字声明一个函数不同,匿名函数对象自身没有一个显式的__name__属性,这就是lambda函数被称为匿名函数的原因。

    In [3]: f = lambda x: x * 2
    In [4]: ints = [4, 0, 1, 5, 7]
    
    In [5]: [f(x) for x in ints]
    Out[5]: [8, 0, 2, 10, 14]
    

    匿名函数的代码量小、意图清晰,在功能不是很复杂的前提下,比写一个完整的函数更加高效。

    Lambda应用案例:根据字符串中不同字母的数量进行排序

    In [6]: strings = ['foo', 'card', 'bar', 'aaaa', 'abab']
    
    In [7]: strings.sort(key = lambda x: len(set(list(x))))
    
    In [8]: strings
    Out[8]: ['aaaa', 'foo', 'abab', 'bar', 'card']
    

    错误和异常处理

    优雅地处理错误或异常是构建稳定程序的重要工作。Python对异常处理的伪代码如下:

    tyr:
        # 要执行的代码
        raise ValueError(10)  # 抛出一个指定的异常
    except ValueError as e:
        # 捕获到异常时,对异常进行处置的代码
        print(e.value)
    finally:
        # 无论是否有异常,都要执行的善后代码
    

    另外,except语句可以捕获指定的一个或多个异常,方法是将异常类型写成元组(封装在小括号中),并跟在except关键字后。

    except (TypeError, ValueError):
    

    上面的TypeErrorValueErorr,都是Python内置的异常类。更多的内置异常类、创建自定义异常类,请参考Python官方文档。

    文件I/O

    Python中处理文件非常简单,这也是Python能够在文本和文件处理领域如此流行的原因。

    打开并读取文件

    Python使用open函数打开文件,默认情况下,文件是以只读模式"r"打开的。在结束操作时,关闭文件是非常重要的。如下的伪代码:

    f  = open('readme.txt')
    
    for line in f:
        print(line)
    
    f.close()
    

    另一种更简单的关闭文件的方式是使用with语句,文件会在with代码块结束后自动关闭。

    with open('readme.txt') as f:
        for line in f:
            print(line)
    

    打开并写入文件

    Python操作文件的模式有如下几种:

    • r:只读模式
    • w:只写模式,创建新文件(路径下的同名文件中的数据会被清除)
    • x:只写模式,创建新文件(路径下有同名文件会创建失败)
    • a:添加到已存在的文件(如果不存在就创建)
    • r+:读写模式
    • b:二进制文件模式,与别模式搭配使用(如'rb'或'wb')
    • t:文本文件模式,与别的模式搭配使用(如'rt'或'wt')

    下面的伪代码示例写入文件:

    with open('readme.txt', 'w') as f:
        f.writelines(strings)  # 将字符串序列写入文件
    

    相关文章

      网友评论

          本文标题:第3章 Python数据结构、函数

          本文链接:https://www.haomeiwen.com/subject/lipuyctx.html