美文网首页Python3 学习笔记
Python_5_Python的封装与解构-集合

Python_5_Python的封装与解构-集合

作者: 静堂先生 | 来源:发表于2019-05-28 16:28 被阅读0次

    1. 封装与解构

      封装与解构属于 Python 语言的一种特性,它使用起来很像其他语言中的 "逗号表达式",但内部原理是不同的,在某些场景下:比如变量交换、复制时使用,显得非常优雅。

    1.1. 封装

      封装顾名思义就是装箱,把多个值使用逗号分隔,组合在一起,本质上来看,其返回的是一个元组,只是省略了小括号。(一定要区别于 C 语言的逗号表达式)

    In : t1 =(1,2)  # 定义一个元组
    
    In : t2 = 1,2    # 省略括号,其内部还是会封装成元组
    
    In : t1
    Out:(1, 2)
    
    In : t2
    Out:(1, 2)
    
    In : type(t1)
    Out: tuple
    
    In : type(t2)
    Out: tuple
    

    1.2. 解构

      解构,就是把箱子解开,在 Python 中表示从线性结构中把元素解开,并且顺序的赋值给其他变量,需要注意的是,解构时接受元素的变量,需要放在等式的左边,并且数量要和右边待解开的元素的个数一致。

    In : t1
    Out:(1, 2)
    
    In : a,b = t1    # 表示把 1 赋给 a,把 2 赋给 b
    
    In : a 
    Out: 1
    
    In : b
    Out: 2
    
    In : a,b,c = t1   # 当接受元素的变量多于解构的元素时,会提示 ValueError,反之相同
    ---------------------------------------------------------------------------
    ValueError                                Traceback(most recent call last)
    <ipython-input-101-2e8ad53e5fc7> in <module>
    ----> 1 a,b,c = t1
    
    ValueError: not enough values to unpack(expected 3, got 2)
    

    1.3. Python3 的解构

      了解了封装与解构,那么回想一下当我们需要进行变量交换的时候,是否可以通过封装与解构进行优化呢?当我们在其他语言中进行 a,b 变量的值的交换,我们需要一个中间变量 temp,即:temp = a ; a = b ; b = temp,在 Python 中我们可以省略它。

    >>> a = 1
    >>> b = 2
    >>> a,b = b,a    # 等号右边使用了封装,而左边就使用了解构,这样就完成了变量的交换了,是不是很方便
    >>> a
    2
    >>> b
    1
    >>> 
    

      为什么可以使用这种操作,是因为 Python 在进行变量赋值时,会先计算等式右边的表达式,封装起来,然后再进行解构,赋值给对应位置上的变量。

    • *号: 使用方式为: *变量名,贪婪吸收解构的元素并形成一个列表,无论能否吸收,都会返回一个列表
    • _号:表示丢弃一个变量(实际上是使用 _ 做变量名接受不想要的值,但不使用它,相当于把它丢弃,这是一个惯例,是一个不成文的约定,不是标准)
    In : t = list(range(5))
    
    In : t  
    Out: [0, 1, 2, 3, 4]
    
    In : head,*mid,tail = t
    
    In : head     
    Out: 0
    
    In : mid
    Out: [1, 2, 3]
    
    In : tail     
    Out: 4
    

      需要注意的是:

    1. *变量名 这种格式不能单独使用
    2. 也不能多个 *变量名 连续使用(因为从原理上看,两个 *变量名 连起来使用,会引起歧义,所以 Python 禁止了这种写法)
    3. *_ 这种格式,可以收集足够多的元素并丢弃之
    # 从 [1,(2,3,4),5] 中取出 4 来
    >>> _,(*_,a),_ = [1,(2,3,4),5]
    >>> a
    >>> 4
    
    # 环境变量 JAVA_HOME=/usr/bin/java,返回环境变量名和路径
    >>> env, path = 'JAVA_HOME=/usr/bin/java'.split('=')
    >>> env, path
    >>>('JAVA_HOME','/usr/bin/java')
    
    # 或者
    
    In : env, _, path = 'JAVA_HOME=/usr/bin/java'.partition('=')
    
    In : env, path
    Out:('JAVA_HOME', '/usr/bin/java')
    

      解构是 Python 提供的很好的功能,可以方便的提取复杂的数据结构的值,配合*_ 使用时,会更加便捷。

    2. set 类型

      集合 set 在 Python 中是一个非常重要的 非线性结构,它使用 {} 表示,用三个词总结集合的特点就是:可变的无序的不重复。它的官方解释如下:

    • set 是一个无序的,不重复的 可 hash 对象 组成的集。
    • 常用来进行成员测试,在一个序列中去掉重复的对象,和进行数学上的计算,比如 交集(intersection)并集(union)差集(difference)对称差集(symmetric difference) 等。
    • 和其他容器类型相似,在一个无序的集合中支持 x in set,len(set),for x in set 等操作。
    • set 不会记录元素的位置以及元素加入集合的顺序,所以 set 不支持 索引切片 或者其他的类序列的操作。

      什么是可 hash 对象,可以简单的理解为可以被 hash() 计算的对象,在 Python 中,可 hash 对象是不可变类型的,比如 tuple, str, int 等等。

    2.1. set 的定义

      Python 提供了两种定义一个集合的方式,set()set(iterable),他们的用法如下:

    set() --> new empty set object     # 返回一个空的 set 对象
    set(iterable) --> new set object   # 返回一个 set 对象,元素由 iterable 填充
    

    例如:

    In : s1 = set()
    
    In : s2 = set(range(5))          # {0, 1, 2, 3, 4}
    
    In : s3 = set(list(range(10)))   # 外面使用 list 进行转换,多此一举
    
    In : s3
    Out: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    
    In : s4 = {}                     # 这种方式实际上是创建了一个空字典,而不是集合
    
    In : s4        
    Out: {}
    
    In : type(s4)  
    Out: dict
    
    In : s5 = {(1,3),3,'a'}
    
    In : s6 = {[1,2],(1,2,),1,2}       # list 属于不可 hash 对象,所以无法添加到 set 中去
    ---------------------------------------------------------------------------
    TypeError                                 Traceback(most recent call last)
    <ipython-input-52-237c72203405> in <module>
    ----> 1 s6={[1,2],(1,2,),1,2}
    
    TypeError: unhashable type: 'list'
    

    2.2. set 的基本操作

      set 是可变的类型,那就意味着,我们可以对 set 进行增加、删除、修改等操作。

    2.2.1. 增加元素

      set 提供了两种定义一个集合的方式,addupdate,他们的用法如下:

    s.add(elem) --> None            # 在集合 s 中添加一个元素 elem,如果元素存在,则什么都不做(去重特性)。(就地修改)
    
    s.update(*others) --> None   # 把 *others 个可迭代可 hash 对象,和 s 进行并集,然后赋给 s。(就地修改)
    

    例如:

    In : s = {1, 2, 3}        
    
    In : s.add('abc')       # 把字符串 'abc' 当作一个元素添加进去
    
    In : s
    Out: {1, 2, 3, 'abc'}  
    
    In : s.add((1,2,3))     # 把元组(1,2,3) 当作一个元素添加进去
    
    In : s
    Out: {(1, 2, 3), 1, 2, 3, 'abc'}
    
    In : s.update(range(5),'abcdef',[5,6,7,8])     # 合并多个可迭代可 hash 对象到 s 集合中来
    
    In : s
    Out: {(1, 2, 3), 0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    

    2.2.2. 删除元素

      set 提供了多种删除元素的方式,比如 remove ,pop,他们的用法如下:

    s.remove(elem)  --> None   # 在集合 s 中删除一个元素,这个元素必须存在集合 s 中,否则会报 KeyError 异常
    
    s.discard(elem) --> None   # 在集合 s 中删除一个元素,如果元素不存在集合中,那么什么也不做
    
    s.pop()  --> item     # 在集合 s 中随便弹出一个元素,并返回元素的本身,如果集合本身为空,那么会提示 KeyError 异常
    
    s.clear()  --> None   # 清空集合
    

    例如:

    In : s
    Out: {(1, 2, 3), 0, 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s.remove(0)  
    
    In : s
    Out: {(1, 2, 3), 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s.remove(1000)       # 不存在集合内的元素,删除会报异常   
    ---------------------------------------------------------------------------
    KeyError                                  Traceback(most recent call last)
    <ipython-input-74-9d9da17ab719> in <module>
    ----> 1 s.remove(1000)
    
    KeyError: 1000
    
    In : s = {(1, 2, 3), 1, 2, 3, 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s.pop()   
    Out: 1
    
    In : s
    Out: {(1, 2, 3), 2, 3, 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s1 = set()
    
    In : s1.pop()      # 空集合会报异常        
    ---------------------------------------------------------------------------
    KeyError                                  Traceback(most recent call last)
    <ipython-input-84-095118de218b> in <module>
    ----> 1 s1.pop()
    
    KeyError: 'pop from an empty set'
    
    In : s = {(1, 2, 3), 4, 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s.discard(1000)     # 不会报异常   
    
    In : s.discard(4) 
    
    In : s
    Out: {(1, 2, 3), 5, 6, 7, 8, 'a', 'abc', 'b', 'c', 'd', 'e', 'f'}
    
    In : s.clear() 
    
    In : s
    Out: set()
    

    2.2.3. 修改元素

      上来我们需要先想一个问题,为什么要修改 set 呢?修改的本质是什么?

    • 修改的本质其实就是找到这个元素,删除,然后再加入新的元素
    • 由于集合是非线性结构,所以无法被索引
    • 但是 set 是容器,可以被迭代

      所以 set 不能像 list 那样,通过索引修改元素,因为它无序的特性,修改其实等同于删除后再添加元素。

    2.2.4. 成员判断

      我们说既然 set 是容器,那么我们就可以对容器内的元素进行判断,那么就需要使用成员判断符 innot in 了。

    • inx in s, 判断元素 x 是否是在集合 s 中,返回 bool 类型
    • not inx not in s, 判断元素 x 不在集合 s 中,返回 bool 类型
    In : s = set(range(20))    
    
    In : s
    Out: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
    
    In : 1 in s    
    Out: True
    
    In : 10000 not in s        
    Out: True
    

    2.3. set 和线性结构

      在 list,str 这种 线性结构 中进行 成员判断 时,因为需要遍历,线性结构的查询时间复杂度是 O(n),即随着数据规模的增大而增加耗时。
      而 set 等非线性结构,内部使用的是 hash 值作为 key,查询时只需把要判断的元素进行 hash,找到 set 中对应的门牌号,把里面的数据拽出来,看看是不是相同就可以,时间复杂度可以做到 O(1),查询时间和数据规模无关,效率很高。
      在 Python 中可 hash 对象 都属于不可变类型 ,Python 中的可 hash 对象如下:

    • 数值类型 int、float、complex
    • 布尔值 True、False
    • 字符串 String、Bytes
    • 元组 tuple
    • None

    3. 集合

      简单来说,所谓的一个集合,就是将数个对象归类而分成一个或数个形态各异的大小整体。 一般来讲,集合 是具有某种特性的事物的整体,或是一些确认对象的汇集。构成集合的事物或对象称作 元素 或是 成员。集合的元素可以是任何事物,可以是人,可以是物,也可以是字母或数字等。 (此解释来自于维基百科)

    • 全集:所有元素的结合。例如实数集,所有实数组成的集合就是实数集
    • 子集 subset 和超集 superset:一个集合 A 所有的元素都在另一个集合 B 内,A 是 B 的子集,B 是 A 的超集
    • 真子集和真超集:A 是 B 的子集,且 A 不等于 B,A 就是 B 的真子集,B 就是 A 的真超集
    • 并集:多个集合合并的结果
    • 交集:多个集合的公共部分
    • 差集:集合中除去和其他集合共有的部分

      这些是小学数学基础概念。

    3.1. 集合运算

      通过集合运算,我们可以方便地求出集合的差集、并集等,Python 的集合除了提供了大量的集合运算方法,还提供了不少的特殊符号用来表示集合运算。

    3.2. 并集

      将集合 A 和集合 B 所有元素合并在一起,组成的集合称为集合 A 和集合 B 的 并集

    union.png
    s.union(*others) --> new set object   # 把多个集合和集合 s 进行合并,返回一个新的集合对象,使用 | 表示
    
    s.update(*others) --> None            # 把 *others 个可迭代可 hash 对象,和 s 进行并集,然后赋给 s。(就地修改), 使用 |= 表示
    

    3.3. 交集

      集合 A 和集合 B,由所有属于 A 且属于 B 的元素组成的集合称为 交集

    intersection.png
    s.intersection(*others) --> new set object   # 返回多个集合的交集,使用 & 表示
    
    s.intersection_update(*others) --> None      # 获取多个集合的交集,就地进行修改,使用 &= 表示
    

    3.4. 差集

      集合 A 和 B,由所有属于 A 且不属于 B 的元素组成的集合称为 差集

    difference.png
    s.difference(*others) --> new set object    # 返回集合 s 和其他多个集合的差集,使用 - 表示
    
    s.difference_update(*others) --> None       # 返回集合 s 和其他多个集合的差集,就地进行修改,使用 -= 表示
    

    3.5. 对称差集

      不属于集合 A 和集合 B 交集的其他元素组成的集合,数学表达式为:(A-B) U(B-A)

    symmetric_differece.png
    s.symmetric_difference(other) --> new set object   # 返回和另一个集合的对称差集,使用 ^ 表示
    
    s.symmetric_difference_update(other) --> None      # 返回和另一个集合的对称差集,就地修改,使用 ^= 表示
    

    3.6. 集合的其他运算

    s.issubset(other) --> bool      # 判断当前集合是否是另一个集合的子集,使用 <= 表示
    
    set1 < set2                      # 判断 set1 是否是 set2 的真子集
    
    s.issuperset(other) --> bool    # 判断当前集合是否是 other 的超集,使用 >= 表示
    
    set1 > set2                      # 判断 set1 是否是 set2 的真超集
    
    s.isdisjoint(other) --> bool    # 判断当前集合和另一个集合有没有交集,没有交集返回 True
    

      以上集合运算举例说明之:

    >>> s1 = set(range(1, 10))
    
    >>> s2 = set(range(8, 15))
    
    >>> s1, s2
    ({1, 2, 3, 4, 5, 6, 7, 8, 9}, {8, 9, 10, 11, 12, 13, 14})
    
    >>> s1 | s2         # s1 和 s2 的并集
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
    
    >>> s1 |= s2 ; s1   # 就地修改,结果赋值给 s1
    {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}
    
    >>> s1 = set(range(1, 10)) ; s2 = set(range(8, 15)) ; s1, s2
    ({1, 2, 3, 4, 5, 6, 7, 8, 9}, {8, 9, 10, 11, 12, 13, 14})
    
    >>> s1 & s2         # s1 和 s2 的交集
    {8, 9}
    
    >>> s1 &= s2 ; s1   # 就地修改,结果赋值给 s1
    {8, 9}
    
    >>> s1 = set(range(1, 10)) ; s2 = set(range(8, 15)) ; s1, s2
    ({1, 2, 3, 4, 5, 6, 7, 8, 9}, {8, 9, 10, 11, 12, 13, 14})
    
    >>> s1 - s2         # s1 和 s2 的差集,从 s1 中排除与 s2 的交集中的元素,得到所有属于 s1 但是不属于 s2 的元素组成的集合
    {1, 2, 3, 4, 5, 6, 7}
    
    >>> s1 -= s2 ; s1    # 就地修改,结果赋值给 s1
    {1, 2, 3, 4, 5, 6, 7}
    
    >>> s1 = set(range(1, 10)) ; s2 = set(range(8, 15)) ; s1, s2
    ({1, 2, 3, 4, 5, 6, 7, 8, 9}, {8, 9, 10, 11, 12, 13, 14})
    
    >>> s1 ^ s2  # s1 和 s2 的对称差集,从 s1 和 s2 的并集中排除 s1 的 s2 交集
    {1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14}
    
    >>> s1 ^= s2 ; s1    # 就地修改,结果赋值给 s1
    {1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14}
    
    >>> s1,s2
    ({1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14}, {8, 9, 10, 11, 12, 13, 14})
    
    >>> s2 < s1  # 判断 s2 是否是 s1 的真子集
    False
    
    >>> s1 = set(range(6)) ; s2 = set(range(5, -1, -1)) ; s1, s2
    ({0, 1, 2, 3, 4, 5}, {0, 1, 2, 3, 4, 5})
    
    >>> s1 <= s2   # 判断 s1 是否是 s2 的子集
    True
    
    >>> s1 < s2   # 判断 s1 是否是 s2 的真子集
    False
    
    >>> s2 >= s1   # 判断 s2 是否是 s1 的超集
    True
    
    >>> s2 > s1   # 判断 s2 是否是 s1 的真超集
    False
    
    >>> s1.isdisjoint(s2)   # 判断 s2 是否与 s1 没有交集,没有交集返回 True
    False
    

    相关文章

      网友评论

        本文标题:Python_5_Python的封装与解构-集合

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