Stay awake and pray that you may not come into the time of trial; the spirit indeed is willing, but the flesh is weak.(MATTHEW 26:41)

集合(1)

已经学习了几种对象类型。

温故而知新。它们是:int/float/str/bool/list/dict/tuple

还真的不少了。

不过,Python是一个发展的语言,没准以后还出别的呢。

读者可能有疑问了,出了这么多的类型,我也记不住呀,特别是里面还有不少方法。

不要担心记不住,你只要记住爱因斯坦说的就好了。

爱因斯坦在美国演讲,有人问:“你可记得声音的速度是多少?你如何记下许多东西?”

爱因斯坦轻松答道:“声音的速度是多少,我必须查辞典才能回答。因为我从来不记在辞典上已经印着的东西,我的记忆力是用来记忆书本上没有的东西。”

多么霸气的回答。

这回答不仅仅霸气,更告诉我们一种方法:只要能够通过某种方法查找到的,就不需要记忆。

所以,再多的数据类型及其各种方法,都不需要记忆。因为它们都可以通过下述方法但不限于这些方法查到(这句话的逻辑还是比较严密的,包括但不限于...)

  • 交互模式下用dir()或者help()
  • google(不推荐Xdu,原因自己体会啦)

还有,如果你经常练习,会发现很多东西自然而然就记住了。

在已经学过的不同种类型的对象中:

  • 能够索引的,如list/str,其中的元素可以重复
  • 可变的,如list/dict,即其中的元素/键值对可以原地修改
  • 不可变的,如str/int,即不能进行原地修改
  • 无索引序列的,如dict,即其中的元素(键值对)没有排列顺序

现在要介绍另外一种类型的数据,英文是set,翻译过来叫做“集合”。它的特点是:有的可变,有的不可变;元素无次序,不可重复。

创建集合

元组算是列表和字符串的某些特征的杂合(杂交的都有自己的优势,上一节的末后已经显示了),那么集合则可以堪称是列表和字典的某些特征杂合.

请读者细细品味,这种杂合的特征。

首先要创建集合,其方法是:

>>> s1 = set("qiwsir")  
>>> s1                  
set(['q', 'i', 's', 'r', 'w'])

把字符串中的字符拆解开,形成集合。

特别注意观察,qiwsir中有两个i,但是在集合中,只有一个i,也就是集合中元素不能重复。

>>> s2 = set([123,"google","face","book","facebook","book"])
>>> s2
set(['facebook', 123, 'google', 'book', 'face'])                

在创建集合的时候,如果发现了重复的元素,就会过滤掉,剩下不重复的。

在使用dir()来看看集合的方法,特别从下面找一找有没有index,如果有它,就说明可以索引,否则,集合就没有索引。

>>> dir(set)
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

请用你那双慧眼,寻找一番。

没有index

的确没有。所以,集合没有索引,也就没有顺序而言,它不属序列。当你这样操作的时候,

>>> s1 = set(['q', 'i', 's', 'r', 'w'])
>>> s1[1]
Traceback (most recent call last):
  File "<pyshell#10>", line 1, in <module>
    s1[1]
TypeError: 'set' object does not support indexing

报错。并且明确告知我们,不支持索引。

除了用set()来创建集合。还可以使用{}的方式。

>>> s3 = {"facebook",123}       #通过{}直接创建
>>> s3
set([123, 'facebook'])

但是这种方式不提倡使用。因为我们已经将{}常常用在字典上了,要避免歧义才好。

看看下面的探讨就发现问题了。

>>> s3 = {"facebook", [1,2,'a'], {"name":"python","lang":"english"}, 123}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

>>> s3 = {"facebook", [1,2], 123}
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

认真阅读报错信息,有这样的词汇:“unhashable”,在理解这个词之前,先看它的反义词“hashable”,翻译为“可哈希”。网上搜一下,有不少文章对这个进行诠释。如果我们简单点理解,某数据“不可哈希”(unhashable)就是其可变,如list/dict,都能原地修改,就是unhashable。否则,不可变的,类似字符串那样不能原地修改,就是hashable(可哈希)的。

对于前面已经提到的字典,其键必须是hashable数据,即不可变的。

现在遇到的集合,其元素也要是“可哈希”的。上面例子中,试图将字典、列表作为元素的元素,就报错了。而且报错信息中明确告知列表、字典是不可哈希类型,言外之意,里面的元素都应该是可哈希类型。

特别说明,利用set()建立起来的集合是可变集合,可变集合都是unhashable类型的。

set的方法

从前面的dir(set)结果中,你可以看到不少集合的方法。

为了看的清楚,我把双划线__开始的先删除掉,剩下的就是:

'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update'

然后用help()可以找到每个函数的具体使用方法。读者完全可以用这种方法自己查看了。

下面列几个例子。

add, update

>>> help(set.add)

Help on method_descriptor:

add(...)
Add an element to a set.  
This has no effect if the element is already present.

在交互模式这个最好的实验室里面做实验:

>>> a_set = {}              #我想当然地认为这样也可以建立一个set
>>> a_set.add("qiwsir")     #报错.看看错误信息,居然告诉我dict没有add.我分明建立的是set呀.
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'add'
>>> type(a_set)             #type之后发现,计算机认为我建立的是一个dict     
<type 'dict'>

特别说明一下,{}这个东西,在字典和集合中都用.但是,如上面的方法建立的是字典,不是集合.

这是python规定的.

要建立空集合,不得不使用set()

>>> s = set()
>>> type(s)
<class 'set'>        #Python 2的返回结果略有差异,为<type 'set'>

当然,非空集合,依然可以这样:

>>> a_set = {'a','i'}       #这回就是set了吧
>>> type(a_set)
  <type 'set'>              #Python 3返回: <class 'set'>

然后就开始对这个集合使用add()方法,并看效果。

>>> a_set.add("qiwsir")     #增加一个元素

没有报错,就意味着成功。没有返回值,根据我们经验,这属于“原地修改”。

>>> a_set
set(['i', 'a', 'qiwsir'])

这次经验胜利了。继续洋洋得意地敲代码。

>>> b_set = set("python")
>>> type(b_set)
<type 'set'>
>>> b_set
set(['h', 'o', 'n', 'p', 't', 'y'])
>>> b_set.add("qiwsir")
>>> b_set
set(['h', 'o', 'n', 'p', 't', 'qiwsir', 'y'])

成功继续伴随着。废话!仅仅是刚才的重复罢了。重复是必须的,这样是为了加深印象。

>>> b_set.add([1,2,3])      
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

报错。哪里错了?

遇见错误,不要沮丧。认真阅读报错信息:列表是不可哈希的。洋洋得意中忘记前面强调的:“集合中的元素应该是hashable类型”。

耍一个小聪明吧。

>>> b_set.add('[1,2,3]')  
>>> b_set
set(['[1,2,3]', 'h', 'o', 'n', 'p', 't', 'qiwsir', 'y'])

为什么这么一搞就可以了呢?仔细观察,这回不是增加列表了,本质是字符串。

除了上面的增加元素方法之外,还能够从另外一个集合中合并过来元素,方法是set.update(s2)

>>> help(set.update)
update(...)
    Update a set with the union of itself and others.

>>> s1 = set(['a', 'b'])
>>> s2 = set(['github', 'qiwsir'])
>>> s1.update(s2)       #把s2的元素并入到s1中.
>>> s1                              #s1的引用对象修改
set(['a', 'qiwsir', 'b', 'github'])
>>> s2                             #s2的未变
set(['github', 'qiwsir'])

如果仅仅是这样的操作,容易误以为update方法的参数只能是集合。非也。看文档中的描述,这个方法的作用是用原有的集合自身和其它的什么东西构成的新集合更新原来的集合。这句话有点长,可以多读一遍。分解开来,可以理解为:others是指的作为参数的不可变对象,将它和原来的集合组成新的集合,用这个新集合替代原来的集合。举例:

>>> s2.update("goo")
>>> s2
set(['github', 'o', 'g', 'qiwsir'])
>>> s2.update((2,3))
>>> s2
set([2, 3, 'g', 'o', 'github', 'qiwsir'])

所以,文档的寓意还是比较深刻的。

pop, remove, discard, clear

>>> help(set.pop)
pop(...)
    Remove and return an arbitrary set element.
    Raises KeyError if the set is empty.

一下变量承接前面的操作,

>>> b_set
set(['[1,2,3]', 'h', 'o', 'n', 'p', 't', 'qiwsir', 'y'])
>>> b_set.pop()     #从set中任意选一个删除,并返回该值
'[1,2,3]'
>>> b_set.pop()
'h'
>>> b_set.pop()
'o'
>>> b_set
set(['n', 'p', 't', 'qiwsir', 'y'])

能不能指定删除某个元素?

>>> b_set.pop("n")  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: pop() takes no arguments (1 given)

set.pop()是从set中任意选一个元素,删除并将这个值返回。

但是,不能指定删除某个元素。报错信息中就告诉我们了,pop()不能有参数。

此外,如果集合已经是空的了,再删除,也报错。这条是帮助文档中告诉我们的,读者可以试试。

要删除指定的元素,怎么办?

>>> help(set.remove)

remove(...)
    Remove an element from a set; it must be a member.    

    If the element is not a member, raise a KeyError.

set.remove(obj)中的obj,必须是set中的元素,否则就报错.试一试:

>>> a_set
set(['i', 'a', 'qiwsir'])
>>> a_set.remove("i")
>>> a_set
set(['a', 'qiwsir'])
>>> a_set.remove("w")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'w'

remove(obj)类似的还有一个discard(obj):

>>> help(set.discard)

discard(...)
    Remove an element from a set if it is a member.

    If the element is not a member, do nothing.

help(set.remove)的信息对比,看看有什么不同?

discard(obj)中的obj如果是集合中的元素,就删除;如果不是,就什么也不做,do nothing。

新闻就要对比着看才有意思呢。这里也一样.

>>> a_set.discard('a')
>>> a_set       
set(['qiwsir'])
>>> a_set.discard('b')
>>>

在删除上还有一个绝杀,就是set.clear(),它的功能是:Remove all elements from this set.(自己在交互模式下help(set.clear))

>>> a_set
set(['qiwsir'])
>>> a_set.clear()
>>> a_set
set([])
>>> bool(a_set)     #空了,bool一下返回False.
False

知识

集合,也是一个数学概念(以下定义来自维基百科)

集合(或简称集)是基本的数学概念,它是集合论的研究对象。最简单的说法,即是在最原始的集合论─朴素集合论─中的定义,集合就是“一堆东西”。集合里的“东西”,叫作元素。若然 x 是集合 A 的元素,记作 x ∈ A。

集合是现代数学中一个重要的基本概念。集合论的基本理论直到十九世纪末才被创立,现在已经是数学教育中一个普遍存在的部分,在小学时就开始学习了。这里对被数学家们称为“直观的”或“朴素的”集合论进行一个简短而基本的介绍;更详细的分析可见朴素集合论。对集合进行严格的公理推导可见公理化集合论。

在计算机中,集合是什么呢?同样来自维基百科,这么说的:

在计算机科学中,集合是一组可变数量的数据项(也可能是0个)的组合,这些数据项可能共享某些特征,需要以某种操作方式一起进行操作。一般来讲,这些数据项的类型是相同的,或基类相同(若使用的语言支持继承)。列表(或数组)通常不被认为是集合,因为其大小固定,但事实上它常常在实现中作为某些形式的集合使用。

集合的种类包括列表,集,多重集,树和图。枚举类型可以是列表或集。

不管是否明白,貌似很厉害呀.

是的,所以本讲仅仅是对集合有一个入门.关于集合的更多操作,如运算/比较等,还没有涉及呢.


总目录   |   上节:字典(2)   |   下节:集合(2)

如果你认为有必要打赏我,请通过支付宝:[email protected],不胜感激。

results matching ""

    No results matching ""