python中的数据类型(list,tuple,dict,set,None)

该系列文章:python

1.背景知识

1.1.简化版python解释器如何运行源代码

python解释器主要包含了两大部分,一个部分至关于编译器,另外一个部分至关于虚拟机。python解释器的编译器部分首先把程序编译成中间码(字节码),再由python解释器里的虚拟机部分(Python Virtual Machine (PVM))运行字节码。git

1.2.简化版对象(object)与类(class)

咱们知道,给函数提供输入(input),则函数会处理输入(input),返回结果(output)或者不返回。程序就是解决问题的一系列步骤,这被称为面向过程(Procedure Oriented) 的编程方式。后来,编程语言中出现了一种 面向对象(Object Orientend) 的思想,简单来讲,对象借鉴了现实世界由一个个客体组成的概念,用一个个对象间的互动来组织起程序,跟现实世界的客体相似,对象有本身的特征(对象里的各类值),对象也有本身可以作到的事(经过对象里的各类方法)。对象里的各类值被叫作对象的字段(field) ,对象里的各类方法被叫作对象的方法(method) ,对象的字段跟方法统称为对象的属性(attribute)github

对象的字段相似于编程语言里的普通变量,所不一样的是对象的字段是对象独有的。若是一个对象叫a,a有个属性是b,咱们如何访问b呢?答案是经过a.b这种形式的写法访问。a.b的意思就是a的属性b编程

对象的方法相似于编程语言中的函数,所不一样的是对象的方法是对象独有的,若是一个对象叫cc有个方法是d,咱们如何使用(调用)d呢?答案是经过c.d()这种形式的写法来是使用,d方法能够带参数,形如这样:c.d(a)c.d()的意思就是使用(调用)c的方法dapp

如何实现对象呢?有一种方法就是经过类(class)来实现。对象对应于现实中一个个具体的客体,这些客体各不相同,可是很明显,有一些客体是能够归到同一个阵营里去的,好比全部的人,全部的苹果,全部的猫,这里的人、苹果、猫是抽象的通常概念,程序中的类(class) 就是基于像这样的通常概念而抽象出来的某一类客体的模板,能够是人的类,苹果的类,猫的类。从类(模板)中能够构造出这一类客体的对象。从类到对象,至关于从蓝图中实现了一个对象,因此能够说某对象是某个类的一个实例(实现了的例子)。反过来,某个类规定了将要实现的对象的该有的属性跟方法,跟别的类实现的对象有了区别,因此对象的类型(type) 就是它所承袭的类。dom

1.3.简化版调用栈(call stack),堆(heap)

内存(memory):是指计算中的随机存取内存(random access memory (RAM))。能够认为,内存是一张很大的表,其中的每一个表格(1个字节)有两个属性:地址和值。地址表示了某个表格在内存这张大表中的位置,所以咱们能够经过地址找到该表格;一旦找到该表格,就能够对该表格的值进行擦除并重写的操做,或者只是读取该表格的值。具体的细节咱们能够不用去考虑,咱们须要更加抽象的思考地址和值,任意大小的一块内存均可以有一个(标识)地址来让咱们在整个内存中找到它,该内存块中能存储复杂的值。编程语言

程序要运行时,操做系统就会给它分配一块可用的内存,或者由某高级语言虚拟机提供运行时环境。在该内存空间(运行时环境)里,首先会载入程序自己(机器码或者字节码),接下来会载入整个程序能够用的东西(全局(类)变量,模块等),除此以外的内存会划分为两种,一种是程序运行时的调用栈(call stack),另外一种则是堆(heap)。在这里,内存并不是计算机中真实的物理内存,而是由操做系统经过软硬件结合的一种技术分配的一块虚拟内存,该虚拟内存中的虚拟地址跟计算机中真实的物理内存地址之间有着映射的关系。在这样的虚拟内存空间里,地址是连续的,也就是说程序运行在某一块特定的虚拟内存中,能够想象成一个长块儿。
程序运行的内存块ide

当程序运行时,主函数(main函数)或者首先被执行的函数(方法)会被放入到调用栈中,由于调用栈中只有这一帧,因此它处在调用栈的顶层,一旦处在调用栈的顶层,就会被激活,被激活的帧(frame)获得程序的执行权,该帧中的代码被一步步执行,咱们把这一帧姑且叫第0帧吧。第0帧中调用另外一个函数(方法)时,被调用函数(方法)的帧被放入到调用栈的最顶层,咱们把这一帧叫第1帧,如今,被激活的帧就变成了第1帧,程序的执行权转移到第1帧中,它首先会把被调用时传进来的参数(若是有)存储,接着就声明和初始化局部(实例)变量,操做变量……当第1帧调用另外一个函数(方法)时,被调用函数(方法)的帧被放入到调用栈的最顶层,咱们把这一帧叫第2帧,如前所述,第2帧被激活,获得程序执行权,当第2帧执行结束,返回值(若是有),这时候第2帧的内存被销毁,包括其中的局部(实例)变量、参数等等,第2帧在调用栈中不在存在,这时候,第1帧成为调用栈的顶层,程序的执行权又从新回到第1帧第1帧继续执行剩余的代码,当第1帧执行结束,返回值(若是有),第1帧被销毁,调用栈中最顶层的帧从新变成第0帧,若是第0帧执行结束,则调用栈空白……这其中,被调用函数(方法)放入调用栈最顶层被称为推入(push),而在调用栈中被销毁则被称为弹出(pop)
image函数

调用栈(call stack)中的函数(方法)里存储着程序运行过程当中该如何作的行为(动做、指令)和须要处理的局部(实例)变量,这些变量其实是怎么被存储的呢?不一样的编程语言有不一样的存储方式,实际上有两种最为主流的作法。post

咱们从上篇文章《python入门,编程基础概念介绍(变量,条件,函数,循环)》已经知道,变量的值有各类各样的类型。像简单的数字,布尔值,字符串……,对于这些比较简单的值,一种方法就是把它们看作原始(内置)类型,直接在调用栈内存中分配出一块能容纳该类型值的内存,把该值存进去,经过变量名来使用该值。这时候变量仿佛就是这些值了。这种存储类型又被称为值类型(value type)。须要注意的是,静态类型语言中的变量是有类型的,一旦声明成某种类型的,则只能存储该类型的值;动态类型语言中的变量不存在类型,能够存储任何类型的值。
image

对于简单的数字,布尔值,字符串……这些类型的值,另外一种方法就是把它们都存在堆(heap)内存空间里。在调用栈中的变量里,实际存储的是堆(heap)内存空间里的某一块内存的地址。当一个变量被声明而且被赋值的时候发生了什么呢?实际上发生的是首先在堆(heap)内存空间中分配出一块能容纳该类型值的内存,把该值存进去;而后把该内存的地址绑定到变量上。这时候咱们就说该变量引用(reference) 了该值。这种存储类型被称为引用类型(reference type)。有时候也可以看到这种说法:变量里存储的是值的引用,能够看到,值的内存地址跟值的引用的说法能够互换。
image

前面咱们简略介绍了对象,对象能够看作是种复杂类型的值,你能够想一想,对象里的各类属性和各类方法。那么当咱们有一个对象的时候,或者有某个复杂类型的值的时候,不一样的编程语言都不约而同的选择了堆(heap)内存空间。这是为何呢?由于调用栈中的值须要的内存块的大小一旦肯定就不能改变,而堆则没有这个限制,有可能一个对象占用很大的一块内存,而且在程序运行过程当中动态的变大或者变小。

2.数据类型

变量用来存储值,值能够被改变。正如咱们以前好几回说到的那样,值的类型各类各样,但都携带了某种信息,而且这种信息能够被操做(处理),咱们能够认为,它们都是数据(data)

数据的类型(type) 是各类各样的,在1.3小节中咱们已经看到很是广泛的数据类型:数字,布尔值,字符串。对这些简单的数据类型,咱们已经知道广泛存在的两种存储方式,那么在python中呢?在python中,一切数据类型都是对象,因此,咱们存储任何数据类型的方式都是经过引用(reference)

在python中有个内置函数type(),能够经过它来检查一个对象的类型:

>>> type(1)
<class 'int'>
>>> type(1.1)
<class 'float'>
>>> type("int")
<class 'str'>
>>> type(True)
<class 'bool'>
>>>

上面交互命令行中出现了四种简单的数据类型,分别是int,float,str,bool。若是两个变量同时引用了相同的对象,会发生什么呢?在python中有个内置的函数id(),这个函数返回对象的一个id,能够把该id看作该对象的引用(内存地址)。让咱们看看两个变量同时引用相同的对象时发生了什么:

>>> a=1
>>> b=1
>>> id(a)
255681632
>>> id(b)
255681632
>>>

能够看到,两个变量指向了同一个对象,那么当咱们改变了其中某个变量引用的对象,是否是另外一个变量引用的对象也同时改变了呢?从理论上讲会改变,对吧,由于是同一个对象。咱们来看看:

>>> a=1
>>> b=1
>>> id(a)
255681632
>>> id(b)
255681632
>>> b=2 #改变b的值
>>> a
1
>>>

事实并不像咱们认为的那样,另外一个变量的对象并无改变。这是由于,在python中,上面出现的这四种简单的数据类型都是不可变(immutable) 对象。举个数字的例子来理解这种不可变性:数字1是个对象,是个独立的客体,看起来这个对象简单到不能再简单,咱们没法改变它,若是将变量的引用从数字1改变成数字2,那么,已是另外一个对象了,至关因而更新了变量的引用。

2.1.列表(list)

直到如今,咱们处理过的数据类型都很简单,可是当咱们想要存储更为复杂多变的数据,用咱们目前知道的数据类型来存储就会愈来愈繁琐,容易出错了。好比咱们想要在程序里一次性处理包含6个数字的内容(7,9,11,36,74,12),难道咱们要给每一个数字都提供一个变量名,并一一存储吗?咱们还有更多选择,在python中,它提供了一种叫列表(list) 的数据类型,这种数据类型像个容器,能够装进去其余各类数据类型,甚至也能够将其余列表(list)嵌套进去。咱们要把7,9,11,36,74,12放进一个列表(list)中,能够这么作:

#把这几个数字放进列表,并赋值给变量x
x=[7,9,11,36,74,12]  
#能够定义一个空列表
y=[]
#使用内置函数list()建立列表
a=list("abc")
b=list()

如今把数字放进一个列表了,那么咱们怎么拿出某个数字呢?跟字符串相似,列表中的元素组成了一串,每一个元素在列表中都是有顺序的。每一个元素都被分配了一个位置索引(index)。咱们能够经过特定的索引来访问对应的元素,根据惯例,索引老是从0开始。

x=[7,9,11,36,74,12]
z=len(x)
#从列表x中循环取出数字,并打印到命令行
for index in range(z):
    print(str(x[index]))

列表中的元素的值能不能改变呢?能不能增长或者减小元素?答案是能够的。咱们说python中一切数据类型都是对象,列表也是对象,全部它有本身的专属方法。能够经过列表的append()方法来增长元素,增长的元素被追加到列表结尾。删除一个元素呢,能够经过del语句来删除,也能够经过列表的remove()方法或者pop()方法来删除。这里要注意,remove方法经过值来删除,pop方法经过索引来删除,而且remove方法没有返回值,而pop方法则返回要删除的值。若是咱们不仅想删除某一个元素,而是想清空整个列表,则可使用列表的clear()方法。看下面:

a=['change me',['first',1,2],2019,True]
#如下注释都根据惯例,从0开始计数
#改变第0个元素的值
a[0]='changed'
print('列表a: {}'.format(a))
#改变第1个元素(list)中的第0个元素
a[1][0]=0
print('列表a: {}'.format(a))
#增长一个元素
a.append(2019)
print('列表a: {}'.format(a))
#删除一个元素,经过del语句
del a[0]
print('列表a: {}'.format(a))
#删除一个元素,经过remove方法
a.remove(True)
print('列表a: {}'.format(a))
#删除一个元素,经过pop方法,并将返回值赋给变量b
b=a.pop(2)
print("被删除的元素是{}".format(b))
print('列表a: {}'.format(a))
#清空列表
a.clear()
print('列表a: {}'.format(a))

以上代码中,用到了str的format()方法,这种方法经过在字符串保留一对{},来让format方法中的参数填充其中,参数能够是任意多个(须要与前面{}的数量一致),能够是各类数据类型。这个方法大大简化了咱们想把其余数据类型加入到字符串的过程。运行结果以下:

列表a: ['changed', ['first', 1, 2], 2019, True]
列表a: ['changed', [0, 1, 2], 2019, True]
列表a: ['changed', [0, 1, 2], 2019, True, 2019]
列表a: [[0, 1, 2], 2019, True, 2019]
列表a: [[0, 1, 2], 2019, 2019]
被删除的元素是2019
列表a: [[0, 1, 2], 2019]
列表a: []

既然python中的数据类型都是对象,那么咱们如何判断两个对象是不是同一个呢。答案用is操做符。好比咱们想要判断a对象与b对象是否同一,则能够经过a is b来判断,其结果是布尔值。

咱们看到,列表里的元素能够比一个多,字符串里的字符也能够比一个多,因此咱们给这种其中的元素或者属性能够比一个多的对象运用in操做符(查看某元素是否属于该对象,这被称为成员检测),来提供给for循环语句或者别的语句,让这些语句逐个访问其中的元素或者属性,这个行为能够称为迭代。前面说到的内置函数list()可接受的参数就是能够被迭代的对象。上篇文章中讲到的for...in循环就是迭代的一个例子。

#in操做符的例子
>>> x='hello'
>>> 'h' in x
True
>>>

在讲列表(list)以前,咱们说到了不可变对象。那么列表的状况又是如何呢?咱们把那个例子中的数据类型换成列表来看看:

a=['change me',['first',1,2],2019,True]
#把一样的引用赋值给变量b
b=a
#看看引用是否相同,是否对象是同一个
print(a is b)
#经过变量b改变列表
b[0]='changed'
#如今打印出变量a,看是否有变化
print(a)

以上代码运行结果以下:

True
['changed', ['first', 1, 2], 2019, True]

从运行结果来看,列表是能够改变的,因此是可变(mutable)对象。如今咱们已经把可变对象不可变对象的行为差很少摸清楚了。讲不可变对象的时候咱们讲解了一个数字对象的例子来帮助理解不可变对象,如今咱们来经过字符串的例子来进一步说明,假如咱们把一个字符串“string”的引用赋值给变量a,那么咱们是不能对其中的字符来进行修改的,以下图:

>>> a="string"
>>> a[0]="a"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment

可是对于变量a来讲,它是彻底能够更新的,能够把另外一个对象的引用从新赋值给它:

>>> a="string"
>>> a[0]="a"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> a="hello"
>>> a
'hello'
>>>

事实上不可变对象不管是看着没法改变的,仍是直觉上感受能够改变的(好比字符串),都是python中规定好了的。因此没必要纠结于直觉,咱们要作的是记住哪些数据类型是可变的,而哪些数据类型又是不可变的。下面将介绍一种新的数据类型,它跟列表相似,最大的不一样是,它是不可变的。它叫元组(tuple)。

2.2.元组(tuple)

元组(tuple) 中能够装进去其余各类数据类型,甚至也能够将其余元组(tuple)嵌套进去。元组(tuple)的元素有索引(index),能够经过索引访问到。

#空元组
tuple1=()
#一个元素的tuple,写法特殊一点。若是在元素后面不加逗号
#则python解释器会当成元素的数据类型,而不认为是个元组
tuple2=(2,)
#另外一个元组,它里面有个列表做为元素
tuple3=(2,"lalala",True,45,[4,5])
#使用内置函数tuple()建立元组,该函参数接受可迭代的对象
a=tuple()
b=tuple([1,2,3])

由于元组是不可变对象,因此它其中的元素是不能修改的。元素也不能增删。但整个元组是能够经过del语句删除的。可是当元组中的元素是可变对象时,好比元组中的某个元素是列表,那该列表能不能修改?由于该列表是可变对象,因此该列表中的元素是能够天然增删修改的,但该列表由于是不可变对象中的元素,因此没法删除,以下:

>>> d=(2, 'lalala', True, 45, [5, 5])
>>> d[4][0]=88
>>> print(d)
(2, 'lalala', True, 45, [88, 5])
>>> del d[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
>>> del d[4][0]
>>> print(d)
(2, 'lalala', True, 45, [5])
>>>

那么状况反过来呢?当可变对象中的元素是不可变对象时,好比当列表中的某元素是个元组,该元组是否可以被修改?由于该元组是不可变对象,因此该元组中的元素不能被修改,可是该元组自己是可变对象的元素,因此能够被删除,以下:

>>> e=['he!',0,(4,5,'last')]
>>> e[2][0]=5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> del e[2][0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object doesn't support item deletion
>>> del e[2]
>>> print(e)
['he!', 0]
>>>

2.3.序列(sequence)

一个数据集合里面的元素根据放入的前后顺序排成一串,这种形式的数据能够被称为序列。字符串,列表,元组都有着相似序列的结构,因此也就有相似的行为,它们均可以被索引,均可以被迭代,都可以经过索引访问到其中的元素;不只仅是可以访问到其中的某一个元素,还能访问到其中的某几个元素,这种同时访问到好几个元素的行为,称为切片(也能够把被访问的这些元素称为数据的切片),由于这样作至关于从整个数据序列中切下来一部分,以下:

x='string'
y=[1,2,3,4,5,6]
z=(7,8,9,10,11,12)
#如下注释都根据惯例,从0开始计数
#打印出字符串的第3个字符
print(x[3])
#打印出字符串的倒数第2个字符
print(x[-2])
#打印出字符串的一部分,从第1个开始,到第3个字符,不包括第4个字符
print(x[1:4])
#打印出列表的倒数第3个元素
print(str(y[-3]))
#打印出列表的一部分,从第1个元素开始,到第4个元素,不包括第5个元素
print(str(y[1:5]))
#打印出列表的一部分,从第1个元素开始,直到结束
print(str(y[1:]))
#打印出元组的倒数第4个元素
print(str(z[-4]))
#打印出元组的一部分,从第1个元素开始,到第4个元素,不包括第5个元素
print(str(z[1:5]))
#打印出列表的一部分,从第1个元素开始,直到结束
print(str(z[1:]))

运行以下:

i
n
tri
4
[2, 3, 4, 5]
[2, 3, 4, 5, 6]
9
(8, 9, 10, 11)
(8, 9, 10, 11, 12)

切片中能够设置步长,就是说能够设置隔着几个元素的方式访问数据的一部分,默认步长为1,以下:

>>> a=(1,2,3,4,5,6,7,8,9,10)
>>> a[::1]
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> a[::2]
(1, 3, 5, 7, 9)
>>>

当咱们把数据的切片赋值给变量时会发生什么?通常来讲,数据被切片部分会被复制一份副本,而后把副本的引用赋值给变量。可是,有个特殊状况,就是当数据的被切片部分是所有数据自己时,那该数据要看是可变对象仍是不可变对象了。列表(可变对象)则会复制一份副本,而后把副本的引用赋值给变量,而字符串、元组(不可变对象)则会直接将该数据自己的引用直接赋值给变量,以下:

>>> a=[1,2,3,4]
>>> b=a[:]  #省略掉开头跟结尾index则切片结果是数据自己
>>> id(a)
140046385947272
>>> id(b)
140046386409992
>>> c=(1,2,[2,3])
>>> d=c[:]
>>> id(c)
140046385743768
>>> id(d)
140046385743768
>>>

2.4.字典(dict)

前面咱们介绍了序列,咱们知道序列的索引隐含在数据类型里,不须要咱们明确去定义,由于都是0,1,2,3,4……有顺序的排列下去,当可变对象里的元素被删除,保留下来的元素的索引会改变,由于序列里的索引永远都是顺序排列的数字,这些数字没有跟元素绑定,而只是容许咱们经过索引的数字来访问该位置的元素。那若是咱们想要自定义索引,用字符串,数字等来作索引,并但愿这些索引可以跟特定的元素绑定在一块儿,咱们该怎么办?更简单的说,就是咱们但愿索引是独特而且容易记住,有语义,咱们该怎么办?python给咱们提供了一种数据类型字典(dict),能够胜任这样的要求。咱们把这样的索引能够看做是键 (keys),它与要存储的值绑定在一块儿,叫作键值对。

字典里存储键值对儿,索引独特,因此是没有顺序的。顺序已经可有可无,咱们只须要记住独特的键就好了。如何建立字典,如何存储键值对,看下面的例子:

>>> a={}    #建立一个空字典
>>> type(a)
<class 'dict'>
>>> b={"id":4667,"name":"john"}  #建立一个有两对键值对的字典
>>> b["id"]  #经过键"id"来得到对应的值
4667
>>> c=dict(id=4555,name="li")  #经过内置的dict函数建立字典
>>> print(c)
{'id': 4555, 'name': 'li'}
>>>

字典是可变对象。为了经过键来查找值,就须要字典键(keys)保持惟一性,若是键用了可变对象来存储,会出现不可控因素,举个例子,假如两个键都是由列表来存储,则一旦把这两个列表修改相同,那么查找这两个键所对应的值时就会出现矛盾,因此键必定要用不可变对象来存储,包括数字,布尔值,字符串,元组(须要元组中的元素不包含可变对象)。又由于字典是可变对象,因此字典中键值对里的值是能够改变的。以下:

>>> c={("id",):46678,"name":"john_ss"}
>>> c[("id",)]
46678
>>> d=8
>>> e={d:8,"d":"8"}
>>> e[d]
8
>>> e["d"]
'8'
>>> e[d]=123
>>> print(e)
{8: 123, 'd': '8'}
>>>

在字典中,也能够用字典的get()方法经过键获取值。若是要给字典里增长键值对,能够直接用方括号(下标)的方式增长,例如dict["key_word"]="some values"。能够用字典的pop()方法来删除键值对,要注意的是,pop()方法在删除键值对的同时会返回要移除的键值对,把返回值赋给变量,变量就会获得被移除的键值对:pair=dict.pop("id")。若是咱们不仅想删除某一个键值对,而是想清空整个字典,则可使用字典的clear()方法。若是看下面的例子:

>>> a={1:1,2:2,3:3}
>>> a.get(1)
1
>>> a["appended"]="ok,then!"
>>> print(a)
{1: 1, 2: 2, 3: 3, 'appended': 'ok,then!'}
>>> a.pop(2)
2
>>> print(a)
{1: 1, 3: 3, 'appended': 'ok,then!'}
>>> a.clear()
>>> print(a)
{}
>>>

当咱们尝试像迭代序列那样直接迭代字典时,交互命令行显示结果以下,明显只迭代了键值对中的键(key):

>>> a={1: 1, 2: 2, 3: 3, 'appended': 'ok,then!'}
>>> for item in a:
...     print(item)
... 
1
2
3
appended
>>>

这时候字典中有两个内置方法能够协助来完成迭代,分别是items()跟keys()。items()返回字典中无序的键值对,keys()返回字典中无序的键(keys)。以下:

>>> a={1: 1, 2: 2, 3: 3, 'appended': 'ok,then!'}
>>> for item in a.items():
...     print(item)
... 
(1, 1)   #能够看到把键值对装进了元组
(2, 2)
(3, 3)
('appended', 'ok,then!')
>>> for key in a.keys():
...     print(key)
... 
1
2
3
appended
>>>

2.5.集合(set),frozenset

前面咱们介绍了字典,跟字典相似,在python里还有一种无序的数据类型:集合(set)。基本上,这儿的集合跟数学上的集合的概念是同样的。其中的元素是无序的,而且每一个元素都是惟一不可重复的。建立集合跟建立字典的符号同样,都是花括号“{}”,因此当建立空集合时,会跟建立空字典的符号有冲突,因此python里“{}”表示建立空字典,而建立空集合只能用内置函数set()来建立,以下:

>>> a={1,4,7,"string",("lalala",2,3,4)}  #建立集合
>>> print(a)
{1, 4, 7, 'string', ('lalala', 2, 3, 4)}
>>> b={}   #空字典
>>> type(b)
<class 'dict'>
>>> c=set()  #空集合
>>> type(c)
<class 'set'>
>>>

集合是可变对象,可是它的元素则要求必定是不可变对象,根据集合的定义,每一个元素都是惟一不可重复,那么一旦元素是可变对象,则有了可重复的可能,好比元素中有两个列表的话,经过一些操做有可能会让这两个列表成为同样的。以下:

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

既然集合是可变对象,咱们就能够经过add()方法增长元素,经过remove()、discard()方法、pop()方法删除元素,前提是全部元素都要是不一样的,经过pop()方法删除元素时,不带参数,而且集合无序,因此没法预知哪一个元素被丢掉,但pop()方法会返回被丢的元素:

>>> a={1,4,7,("string",55),"hello"}
>>> a.add("hello,world!") #add方法新增元素
>>> print(a)
{1, 4, 7, 'hello,world!', ('string', 55), 'hello'}
>>> a.remove(7) #remove方法删除元素
>>> print(a)
{1, 4, 'hello,world!', ('string', 55), 'hello'}
>>> a.discard(1) #discard方法删除元素
>>> print(a)
{4, 'hello,world!', ('string', 55), 'hello'}
>>> a.pop()   #经过pop方法删除随机某个元素并返回被删除的元素,可使用变量来存储
4
>>> print(a)
{'hello,world!', ('string', 55), 'hello'}

可使用update方法来增长多个元素,使用clear()方法来清空整个集合,接上面的例子:

>>> a.update([1,2,3,4])  #用列表来更新集合
>>> a.update((5,6,7))    #用元组来更新集合
>>> print(a)
{1, 2, 3, 4, 5, 6, 7, 'hello,world!', ('string', 55), 'hello'}
>>> a.clear()
>>> print(a)
set()   #表明空集合

集合能够实现数学上的并集,交集,差集,对称差,有两种方式:操做符跟方法。并集是两个集合中全部的元素组成的新集合,交集是两个集合中都存在的元素组成的新集合。差集是两个集合中在某个集合中存在,而且在另外一个集合中不存在的元素组成的新集合。对称差集是两个集合中全部元素,除去在交集中的元素,由这样的元素组成的新集合。下面这个表显示python的操做方法:

操做名称 操做符 集合(好比A,B)内置方法
| A.union(B)
& A.intersection(B)
- A.difference(B)
对称差 ^ A.symmetric_difference(B)

经过例子来看看:

>>> A={1,2,4,6,7}
>>> B={1,3,4,5,8}
>>> print(A | B)
{1, 2, 3, 4, 5, 6, 7, 8}
>>> print(A.union(B))
{1, 2, 3, 4, 5, 6, 7, 8}
>>> print(A & B)
{1, 4}
>>> print(A.intersection(B))
{1, 4}
>>> print(A - B)
{2, 6, 7}
>>> print(A.difference(B))
{2, 6, 7}
>>> print(B - A)
{8, 3, 5}
>>> print(B.difference(A))
{8, 3, 5}
>>> print(A ^ B)
{2, 3, 5, 6, 7, 8}
>>> print(A.symmetric_difference(B))
{2, 3, 5, 6, 7, 8}
>>>

从上面的例子能够看出来,两个集合的并集,交集,对称差集都是能够互换的,而差集不是,差集须要区分A-BB-A

在python中,还能够建立相似于tuple这样的不可变对象的set,那就是frozenset,frozen在英文中是冻结了的意思,顾名思义,frozenset就是冻结的集合。frozenset不能增长或者更新元素,删除或者清除元素,相似于只读文件。并集,交集,差集,对称差的操做对于frozenset一样适用。以下:

>>> a=frozenset([1,2,3])   #建立一个frozenset
>>> print(a)
frozenset({1, 2, 3})
>>> a.add(4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'add'
>>> a.clear()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'frozenset' object has no attribute 'clear'
>>> b=frozenset((4,5,3))
>>> a.union(b)
frozenset({1, 2, 3, 4, 5})
>>>

2.6.简化内置的help函数,获取python中各数据类型内置的方法及其简要说明

在python中,有个help()内置函数能够获取关于对象的说明文档。但说明文档中有关于该对象实现的细节,当咱们只是想知道各数据类型的经常使用内置方法及其使用时,就会变得很不方便。我写了一个很短的程序,运行该程序能够将help函数的输出简化(删去实现对象的相关细节)并存入当前目录新建的一个文本文件中,而且该程序还有将方法的解释翻译成中文的可选功能。下面是该程序内容:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import re
#若是想将解释翻译成中文,把下面一行的注释取消
#from translate import Translator

def command_line_arguments(usge,len_of_arguments):
    if len(sys.argv)!=len_of_arguments+1:
        print(usge)
    return sys.argv

def stdout_to_file(dir,func,func_arguments):
    #把函数的标准输出存入文件中
    default_stdout = sys.stdout
    with open(dir,"w") as w:
        sys.stdout=w
        func(func_arguments)
    sys.stdout=default_stdout
    
def simpler_help(object):
    
    stdout_to_file("simpler_help_{}.txt".format(object),help,object)
    
    #把文件中的内容分行存入内存
    with open("simpler_help_{}.txt".format(object),"r") as r:
        lines=r.readlines()
    
    #将内存中的内容修改后写入同名文件,覆盖原有文件
    #mark1,为了将带有“__”的特殊函数不要写入,并将其下面的解释一并略过
    mark1=False
    #mark2,为了将“---”下面的内容略过
    mark2=False
    with open("simpler_help_{}.txt".format(object),"w") as w:
        for line in lines:
            
            #将带有“__”的特殊方法不要写入,并将其下面的解释一并略过
            if "__" in line:
                mark1=True
                continue  
            elif mark1==True:
                result=re.search("[A-Za-z]+",line)
                if result==None:
                    mark1=False
                    continue
                

            #将“---”下面的内容略过
            elif "---" in line:
                mark2=True
                continue
            elif mark2==True:
                pass
            
            #将行中的self跟“/”替换成空
            else:
                if ("self," in line) or ("/" in line):
                    if ("self," in line) and ("/" in line):
                        w.write(line.replace("self,","").replace("/","").replace(", )"," )").replace(" , "," "))
                    elif ("self," in line) and ("/" not in line):
                        w.write(line.replace("self,",""))
                    elif ("self," not in line) and ("/" in line):
                        w.write(line.replace("/","").replace(", )"," )").replace(" , "," "))          
                else:
                    w.write(line)

#若是想将解释翻译成中文,能够把下面的函数注释取消                
"""
def translation_of_help(object):
    translator= Translator(to_lang="zh")
    with open("simpler_help_{}_zh.txt".format(object),"r") as r:
        lines=r.readlines()
    with open("simpler_help_{}_zh.txt".format(object),"w") as w:
        line_process=""
        count_line=0
        for line in lines:
            if ("(" in line) and (")"in line) and ("." not in line):
                w.write(line)
            else:
                if count_line<=1:
                    w.write(translator.translate(line)+"\n")
                else:
                    line_process=line.replace("|","")
                    w.write(" |        "+translator.translate(line_process)+"\n")
            count_line=count_line+1
"""

len_of_arguments=1
arguments=command_line_arguments('''You need provide a name of object,for example:
python3 simpler_help.py list''',len_of_arguments)
if len(arguments)==len_of_arguments+1:
    simpler_help(arguments[1])
    #若是想将解释翻译成中文,能够把下面一行的注释取消
    #translation_of_help("list")

将上面内容复制粘贴,并命名为simpler_help.py。举个例子,若是想知道列表中的内置方法,则在命令行中经过相似这样的python3 simpler_help.py list命令来运行,特别注意须要在文件名空一格后写上须要查询的数据类型名称。下面是程序建立的简化版的列表的help函数输出,保存在当前目录的simpler_help_list.txt中:

Help on class list in module builtins:

class list(object)
 |  list(iterable=() )
 |  
 |  Built-in mutable sequence.
 |  
 |  If no argument is given, the constructor creates a new empty list.
 |  The argument must be an iterable if specified.
 |  
 |  Methods defined here:
 |  
 |  append( object )
 |      Append object to the end of the list.
 |  
 |  clear( )
 |      Remove all items from list.
 |  
 |  copy( )
 |      Return a shallow copy of the list.
 |  
 |  count( value )
 |      Return number of occurrences of value.
 |  
 |  extend( iterable )
 |      Extend list by appending elements from the iterable.
 |  
 |  index( value, start=0, stop=9223372036854775807 )
 |      Return first index of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  insert( index, object )
 |      Insert object before index.
 |  
 |  pop( index=-1 )
 |      Remove and return item at index (default last).
 |      
 |      Raises IndexError if list is empty or index is out of range.
 |  
 |  remove( value )
 |      Remove first occurrence of value.
 |      
 |      Raises ValueError if the value is not present.
 |  
 |  reverse( )
 |      Reverse *IN PLACE*.
 |  
 |  sort( *, key=None, reverse=False)
 |      Stable sort *IN PLACE*.
 |

经过程序建立的文件,能够大概了解各数据类型都有哪些内置方法,以后能够从网上经过关键词搜索出更为详细的介绍跟用例。另外,若是想将方法的解释翻译成中文,把simpler_help.py中相关内容的注释取消,而且首先须要在命令行中执行pip install translate

2.7.关于None

>>> type(None)
<class 'NoneType'>
>>>

None属于NoneType这个数据类型(对象),是它惟一的值。而且,NoneType对象并不像别的数据类型同样,能够有不少实例,而是从始至终都只能有一个实例。

>>> None==False
False
>>> None==1
False
>>> None==""
False
>>> None==0
False
>>> None=={}
False
>>> None==None
True
>>>

从上面的例子(作逻辑判断)能够看出来,None不等于其余任何值,None只等于None。None通常用来表明数据的缺失,看下面的例子:

def addTwo(a,b):
    if (type(a)==int or type(a)==float) and ((type(b)==int or type(b)==float)):
        return a+b
    else:
        return None

b=addTwo("ss",4)
print(b)

#OUTPUT应该为None,None表明函数无法合理的处理参数,只能返回None。

当咱们拿到某个数据,咱们想要判断它不是None的时候该如何作呢,这时候应该用is,由于None是数据类型,也是对象,因此咱们通常会想要这样作:if an_object is not None:。此处not的位置跟成员检测if an_object not in an_object:的位置不一样,须要特别注意。以下:

a=1

if a is not None:
    print("a is not None")
else:
    print("a is None")

#OUTPUT应该为"a is not None"。

有些时候咱们会在别人的代码中看到if an_object:这样的写法,这种写法跟上面的判断某数据(对象)是否是None的写法没有什么联系,这种代码暗示了数据(对象)自己在作逻辑判断时会另外呈现出或真或假的布尔值,事实上也的确如此,在各类数据类型中,会有某些特殊的值在逻辑判断的时候布尔值表现为False,其他值表现为True。在python中,规定了一套规则,当咱们拿某个数据自己来作逻辑判断的时候,解释器如何解肯定其布尔值:

  • 布尔型,False表示False,其余为True
  • 整数和浮点数,0表示False,其余为True
  • 字符串和类字符串类型(包括bytes和unicode),空字符串表示False,其余为True
  • 序列类型(包括tuple,list,dict,set等),空表示False,非空表示True
  • None永远表示False

举个例子,当咱们在理论上来讲从某函数(方法)返回了一个列表list1,咱们用if list1:来判断的时候,则会出现三种状况:list1Nonelist1是空列表,list1是非空列表,这时候list1在第一和第二种状况下表现出的布尔值为False,在第三种状况下表现出的布尔值为True。以下:

list1=[]

while True:
    if list1:
        print("true")
    else:
        if list1 != None:
            print("false,empty list")
            list1=None
        else:
            print("false,None")
            list1=[]

#OUTPUT应该永远在None跟空列表之间徘徊。交替打印“false,empty list”和“false,None”。

[欢迎浏览个人我的博客,https://diwugebingren.github.io
](https://diwugebingren.github....
欢迎关注个人公众号

相关文章
相关标签/搜索