Python对象类型

Python程序可以分解为模块、语句、表达式以及对象,如下所示:

程序由模块构成

模块包含语句

语句包含表达式

表达式建立并出口对象

一、Python类型的介绍:

1.1 为什么使用内置类型

Python提供了强大的对象类型作为语言的组成部分,事实上,除非有内置类型无法提供的特殊对象要处理,最好总是使用内置对象而不是使用自己的实现。下面是其原因:

内置对象使程序更容易编写:

       对于简单的任务,内置类型往往能够表现问题领域的所有结构。例如,集合(列表)和搜索表(字典)可以马上使用它们,仅使用python内置对象类型就能完成很多工作。

内置对象使扩展的组建:

      对于较为复杂的任务,或许仍需要提供自己的对象,使用python的类或C语言的结构。例如,堆栈数据结构也许会实现为管理和定制内置列表的类。

内置对象往往比定制的数据结构更有效率:

    在速度方面,python内置类型优化了用C实现数据结构算法。尽管可以实现属于自己的类似的数据类型,但往往很难达到内置数据类型所提供的性能水平。

内置对象是语言的标准的一部分:

    python不但借鉴了依靠内置工具的语言(例如LISP),而且吸取了那些依靠程序员去提供自己实现的工具或框架的语言(例如C++)的优点。尽管在Python中可以实现独一无二的对象类型,但在开始阶段并没有必要这样做。此外,因为Python的内置工具是标准的,他们一般都是一致的。

1.2 Python的核心数据类型

python的内置对象类型和一些编写其常量(literal)所使用到的语法,也就是能够生成这些对象的表达式。

内置对象:

数字、字符串、列表、字典、元组、文件、集合、其他类型、编程单元类型、与实现相关的类型

上面所列内容并不完整,因为在python程序中处理的每样东西都是一种对象。例如,在python中进行文本模式匹配时,创建了模式对象,还有进行网络脚本编程时,使用了套接字对象,它们由def、class、import和lamba这样的语句和表达式创建,并且可以在脚本间自由地传递,存储在其他对象中等。

1.3数字

python的核心对象集合包括常规的类型:整数、浮点数以及更为少见的类型(有虚部的复数、固定精度的十进制数、带分子和分母的有理分数以及集合等)。

python的基本数字类型还是相当基本的。python中数字支持一般的数学运算。其中**表示乘方。

>>> 12+33
45
>>> 1.5*4
6.0
>>> 2 ** 100
1267650600228229401496703205376

#注意这里的最后一个记过:当需要的时候,python 3.0的整数类型会自动提供额外的精度,以用于较大的数值。
1.4 字符串

就像任意字符的集合一样,字符串是用来记录文本信息的。从严格意义上来说,字符串是单个字符的字符串的序列,其他类型的序列还包括列表和元组。

作为序列,字符串支持假设其中各个元素包含位置顺序的操作。例如,如果我们有四个字符的字符串,我们通过内置的len函数验证其长度并索引操作得到其各个元素。

>>> S = 'Spam'
>>> len(S)
4
>>> S[0]
'S'
>>> S[1]
'p'

在Python中,索引是按照最前面的偏移量进行编码的,也就是从0开始,第一项索引为0,第二项索引为1,以此类推。

>>> S[-1]
'm'
>>> S[-2]
'a'
>>>

#在Python中,我们能够反向索引,从最后一个开始(反向索引是从左边开始计算,反向索引是从右边开始计算)

>>> S[len(S)-1]
'm'

#一般来说,负的索引号会简单地与字符串的长度相加,因此,上面操作和S[-1]是等效的。

>>> S[1:3]
'pa'

#除了简单的从位置进行索引,序列也支持一种所谓分片(slice)的操作,这是一种一步就能够提取整个分片(splice)的方法。

>>> S[1:]
'pam'
>>> S[0:3]
'Spa'
>>> S[:3]
'Spa'
>>> S[:-1]
'Spa'
>>> S[:]
'Spam'

#在一个分片中,左边界默认为0,并且右边界默认为分片序列的长度。

>>> S+'xyz'
'Spamxyz'
>>> S*8
'SpamSpamSpamSpamSpamSpamSpamSpam'

#作为一个序列,字符串也支持使用加号进行合并(将两个字符串合成为一个新的字符串),或者重复(通过再重复一次创建一个新的字符串)。
#注意加号(+)对于不同的对象有不同的意义:对于数字为加法,对于字符串为合并。这是Python的一般特性,也就是我们将会在本书后面提到的多态。简而言之,一个操作的意义取决于被操作的对象。Python编写的操作通常自动地适用于不同类型的对象。

#同时字符串还具有不可变性。如上面的例子

>>> S.find('pa')
1
>>> S.find('pd')
-1
>>> S.replace('pa','XYZ')
'SXYZm'
>>> S
'Spam'

#字符串的find方法是一个基本的字符串查找的操作(它将返回一个传入字符串的偏移量,或者没有找到的情况下返回-1),而字符串的replace方法将会对全局进行搜索和替换。
#尽管这些字符串方法的命名有改变的含义,但在这里我们都不会改变原始的字符串,而是会创建一个新的字符串作为结果------因为字符串具有不可变性。

>>> line='aaa,bbb,ccccc,dd'
>>> line.split(',')#以逗号为分隔符对字符串进行切片
[
'aaa', 'bbb', 'ccccc', 'dd'] >>> S='spam' >>> S.upper()#将小写转换成大写
'SPAM' >>> S.isalpha()#检测是否是字母组成,布尔值,不是的话返回false。
True
>>> line='aaa,bbb,ccccc,dd\n' >>> line=line.rstrip()#去掉两边的空格
>>> line 'aaa,bbb,ccccc,dd'

#还能实现通过分隔符将字符串拆分为子字符串(作为一种解析的简单形式),大小写变换,测试字符串的内容(数字、字母或其他),去掉字符串后的空格字符。

>>> 'name:%s,age:%s' %('chaishao','27')#这种就类似于位置参数,按位置将值传给前面的%s
'name:chaishao,age:27' >>> 'chaishao:{1},age:{0}'.format(27,'chaishao')#这种还是定义位置,前面{1}和{0}表示要第二个位置的值和第一个位置的值
'chaishao:chaishao,age:27' >>> 'chaishao:{name},age:{age}'.format(name='chaishao',age=27)#这种就是前面定义变量,后面给变量赋值
'chaishao:chaishao,age:27'

#字符串还支持一个叫做格式化的高级替代操作,可以以一个表达式的形式和一个字符串方法调用。如上面的例子.

>>> dir(S)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '_
_le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize',
'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'iss
pace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'sw
apcase', 'title', 'translate', 'upper', 'zfill']

#对于更多的细节,可以通过调用内置的dir函数,将会返回一个列表,其中包含了对象的所有属性。由于方法是函数属性,他们也会在这个列表中出现。上面那些变量名中有下划线的内容,它们代表了字符串对象的实现方式,并支持定制。一般来说,以双下划线开头并结尾的变量名是用来表示Python实现细节的命名模式。而这个列表中没有下划线的属性是字符串对象能够调用的方法。

>>> help(S.replace)
Help on built-in function replace:

replace(...) method of builtins.str instance
    S.replace(old, new[, count]) -> str

    Return a copy of S with all occurrences of substring
    old replaced by new.  If the optional argument count is
    given, only the first count occurrences are replaced.

#dir函数简单地给出了方法的名称。要查询它们是做什么的,可以将其传递给help函数。就像PyDoc一样(一个从对象中提取文档的工具),help是一个随Python一起分发的面向系统代码的接口。

>>> help(S)
No Python documentation found for 'spam'.
Use help() to get the interactive help utility.
Use help(str) for help on the str class.

>>> help(str)#可以查看str的所有help帮助说明
Help on
class str in module builtins:

#如上面可以直接查看所有的帮助说明就像查看man帮助一样,不过一般还是针对某个特定的方法进行查看。

>>> S='A\nB\tC'
>>> len(S)#查看字符串的长度
5 >>> ord('\n')#ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
10 >>> S='A\0B\0C' >>> len(S) 5

#上面的反斜线转义序列表示特殊的字符。

>>> msg=""" abc
... def'''ABC""BCD'HAHA"""
>>> msg
' abc\ndef\'\'\'ABC""BCD\'HAHA'
>>> print(msg)
 abc
def'''ABC""BCD'HAHA

#Python允许字符串包括在单引号或双引号中(他们代表着相同的东西)。它也允许在三个引号(单引号或双引号)中包括多行字符串常量。当采用这种形式的时候,所有的行都合并在一起,并在每一行的末尾增加换行符。在Python脚本中嵌入像HTML或XML这样的内容时,是很方便的。

1.5 列表 

Python的列表对象是这个语言提供的最通用的序列。列表是一个任意类型的对象的位置相关的有序集合,它没有固定的大小。不像字符串,其大小是可变的,通过对偏移量进行赋值以及其他各种列表的方法进行调用,确实能够修改列表的大小。

>>> L=[123,'spam',1.23]
>>> len(L)
3
>>> L[0]#查看列表的索引,下标从0开始。
123 >>> L[:-1]#也可以对列表进行分片,这里就是[0:2]
[
123, 'spam'] >>> L + [4,5,6]#两个列表可以合并
[
123, 'spam', 1.23, 4, 5, 6] >>> L [123, 'spam', 1.23]

#由于列表是序列的一种,列表支持所有的我们队字符串所讨论过得序列操作。唯一的区别就是其结果往往是列表而不是字符串。
#列表和其他语言中的数组有些类似,但是列表要强大很多,其中一点就是列表没有固定类型的约束。例如,上面的列表就包含了三个完全不同类型的对象(一个整数、一个字符串、以及一个浮点数)。此外,列表没有固定大小,也就是说能够按照需要增加或减少列表大小,来响应其特定的操作。

>>> L.append('NI')  #在列表尾部添加一个NI
>>> L
[123, 'spam', 1.23, 'NI']  
>>> L.pop(2)  #删除索引2也就是从左往右第三个元素
1.23
>>> L
[123, 'spam', 'NI']
>>> L.insert(1,'haha') #在索引1的位置插入haha对象
>>> L
[123, 'haha', 'spam', 'NI']
>>> L.remove(123) #删除123
>>> L
['haha', 'spam', 'NI']

#列表的append方法扩充了列表的大小并在列表的尾部插入一项;pop方法(或者等效的del语句)移除给定偏移量的一项,从而让列表减小。其他的列表方法可以在任意位置插入(insert)元素,按照值移除(remove)元素等。因为列表是可变的,大多数列表的方法都会就地改变列表对象,而不是创建一个新的列表。

>>> list1=['bb','aa','cc']
>>> list1.sort()  #对列表进行排序默认是按照升序排序
>>> list1 ['aa', 'bb', 'cc'] >>> list1.reverse()#对列表进行翻转
>>> list1 ['cc', 'bb', 'aa']

#列表sort方法是对列表排序的意思,这些方法都会直接对列表进行改变。

>>> list1[1]  #查找list1列表偏移量是1也就是第二个元素
'bb'
>>> list1[3]  #超出边界直接会报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> list1[1]='BB' #对列表第二个元素重新赋值
>>> list1[3]='CC' #超出列表末尾范围之外赋值会报错
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
>>> list1
['cc', 'BB', 'aa']

#尽管列表没有固定的大小,Python扔不允许引用不存在的元素。超出列表末尾之外的索引总是会导致错误,对列表末尾范围之外赋值也是如此。这是有意而为之的,由于去给一个列表边界外的元素赋值,往往会得到一个错误(而在C语言中情况比较糟糕,因为它不会像Python这样进行错误检查)。在Python中,并不是默默的增大列表作为响应,而是会提示错误。为了让一个列表增大,可以调用类似于append这样的列表方法。

>>> M=[[1,2,3],[4,5,6],[7,8,9]]
>>> M
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> M[1] #获取第二个元素的值
[4, 5, 6]
>>> M[1][2] #获取第二个元素里面嵌套的列表的第三个元素的值
6

#Python核心数据类型的一个优秀的特性就是他们支持任意的嵌套。能够以任意的组合对其进行嵌套,并可以多个层次进行嵌套(例如,能够让一个列表包含一个字典,并在这个字典中包含另一个列表等)。这种特性的一个直接的应用就是实现矩阵,或者Python中的“多维数组”,一个嵌套列表的列表就能完成这个基本的操作。上面我们编写了一个包含3个其他列表的列表。其效果就是表现了一个3*3的数字矩阵。

>>> col2 = [row for row in M] #for row in M是一个for循环,那么row久代表了M里面的三个元素,所以col2的值就是M的值
>>> col2 [[1, 2, 3], [4, 5, 6], [7, 8, 9]] >>> col2=[row[1] for row in M] #这里row[1]就相当于取M[1]
>>> col2 [2, 5, 8]

#列表解析,处理序列的操作和列表的方法中,Python还包括了一个更高级的操作,成为列表解析表达式,从而提供了一个处理像矩阵这样结构的强大工具。列表解析源自集合的概念。它是一种通过对序列中的每一项运行一个表达式来创建一个新列表的方法,每次一个,从左至右。列表解析是编写在方括号中的,并且由使用了同一个变量名的(这里是row)表达式和循环结构组成。上面的这个列表解析表达基本上就是它字面上所讲的,把矩阵M的每个row中的row[1]放到一个新的列表中。

>>> col2
[2, 5, 8]
>>> [row[1]+1 for row in M] #把它搜集到的每一个元素都加1
[3, 6, 9] >>> [row[1] for row in M if row[1] % 2 == 0] #使用if条件语句,通过使用%求余表达式(取余数)过滤了结果中的奇数。
[2, 8]
>>> M
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> diag = [M[i][i] for i in [0,1,2]] #M[i][i]就相当于M[0][0],M[1][1],M[2][2]
>>> diag
[1, 5, 9]
>>> doubles = [c*2 for c in 'spam'] #取出来的值*2
>>> doubles
['ss', 'pp', 'aa', 'mm']
>>> G = (sum(row) for row in M) #让row求和,因为是for循环嘛,这里的意思就相当于让M[0],M[1],M[2]分别求和
>>> next(G) #next() 返回迭代器的下一个项目
6
>>> next(G)
15
>>> next(G)
24
>>> list(map(sum,M)) #内置map可以做上面类似的事情,产生对各项运行一个函数的结果,在Python 3.0中,将其包装到列表中,会使其返回所有值。
[6, 15, 24]
>>> {sum(row) for row in M} #可以用解析语法来创建一个集合
{24, 6, 15}
>>> {i : sum(M[i]) for i in range(len(M))} #可以用解析语法来创建一个字典
{0: 6, 1: 15, 2: 24}

#实际应用中的列表解析可以更复杂,如上面。列表解析创建了新的列表作为结果,但是能够在任何可迭代的对象上进行迭代。
#列表解析以及相关的内容函数map和filter比较复杂,这里记录一下先。实际上,在python 3.0中,列表、集合和字典都可以用解析来创建。

1.6 字典

Python中的字典是完全不同的东西:它们不是序列,而是一种映射(mapping)。映射是一个其他对象的集合,但是它们是通过键而不是相对位置来存储的。实际上,映射并没有任何可靠的从左至右的顺序。它们简单地将键映射到值。字典是Python核心对象集合中的唯一的一种映射类型,也具有可变性------可以就地改变,并可以随需求增大或减小、就像列表那样。

>>> D={'food':'Spam','quantity':4,'color':'pink'}#定义一个字典D
>>> D['food']#字典food键的值
'Spam' >>> D['quantity'] += 1#字典quantity的值加1
>>> D {'color': 'pink', 'food': 'Spam', 'quantity': 5}

#上面是映射操作,作为常量编写时候,字典编写在大括号中,并包含一系列的“键:值”对。在我们需要将键与一系列值相关联(例如,为了表述某物的某属性)的时候,字典是很有用的。上面的例子是一个包含了三个元素的字典(键分别为“food”、“quantity”和“color”)。通过键对字典进行索引来读取或改变键所关联的值。字典的索引操作使用的是和序列相同的语法,但是在方括号中的元素是键,而不是相对位置。上面就是使用大括号这种常量形式。

>>> D={}#先定义一个空字典
>>> D['name']='chaishao'#通过给这种定义键值对的形式直接给字典赋值
>>> D['job']='dev' >>> D['age']=27 >>> D {'age': 27, 'name': 'chaishao', 'job': 'dev'} >>> print(D['name']) chaishao

#上面就是一种创建空字典,然后每次以一个键来填写它的方式。与列表中禁止边界外的赋值不同,对一个新的字典的键赋值会创建该键。

#上面最后一个例子,实际是使用字典的键来搜索对应的值,在别的应用中,字典可以用来执行搜索,通过键索引一个字典往往是Python中编写搜索的最快方法。

>>> rec = {'name':{'first':'chai','last':'shao'},#记录了姓和名,这就属于字典嵌套字典
... 'job':['dev','mgr'],#记录了职位和角色,这就是嵌套了列表
... 'age':27}
>>> rec
{'age': 27, 'name': {'last': 'shao', 'first': 'chai'}, 'job': ['dev', 'mgr']}
>>> rec['name'] #查找字典rec关于name键的值
{'last': 'shao', 'first': 'chai'}
>>> rec['name']['last'] #查找字典rec关于name键中的last键的值
'shao'
>>> rec['job']
['dev', 'mgr']
>>> rec['job'][-1]#查找rec字典中job键中列表最后一个元素的值
'mgr'
>>> rec['job'].append('CTO') #给rec字典中的job键中的列表加入一个CTO的值
>>> rec
{'age': 27, 'name': {'last': 'shao', 'first': 'chai'}, 'job': ['dev', 'mgr', 'CTO']}

#上面在顶层使用了三个键的字典(键分别是“name”、“job”和“age”),但是值的情况变得复杂得多:一个嵌套的字典作为name的值,支持了多个部分,并用一个嵌套的列表作为job的值从而支持多个角色和未来的扩展。能够获取这个结构的组件,就像之前在矩阵中所作的那样,但是这次索引的是字典的键,而不是列表的偏移量。

#注意这里的最后一个操作是如何扩展嵌入job列表的。因为job列表是字典所包含的一部分独立的内存,它可以自由的增加和减少。

>>> rec=0
>>> rec
0

#在Python中,当最后一次引用对象后(例如,将这个变量用其他的值进行赋值),这个对象所占用的内存空间将会自动清理掉,类似于上面那种。从技术上来说,Python具有一种叫做垃圾收集的特性,在程序运行时可以清理不再使用的内存,并将你从必须管理代码中这样的细节中解放出来。在Python中,一旦一个对象的最后一次引用被移除,空间将会立即回收。

>>> D = {'a':1,'d':2,'c':3}
>>> Ks = list(D.keys()) #将字典D的键做成列表赋值给Ks
>>> Ks
['a', 'd', 'c'] >>> Ks.sort() #进行一下排序
>>> Ks ['a', 'c', 'd'] >>> D = {'a':1,'c':2,'b':3} >>> D ##字典不是序列,它们并不包含任何可靠的从左至右的顺序,这意味着如果我们建立一个字典,并将它打印出来,它的键也许会以与我们输入时不同的顺序出现。
{'a': 1, 'b': 3, 'c': 2} >>> Ks = list(D.keys())
>>> Ks.sort()

>>> for key in Ks: #写个for循环
... print(key,'=>',D[key]) #循环打印key的名称和D字典里key的值
... #按一次回车是继续写,我们要出结果所以需要按两次回车
a => 1 b => 3 c => 2

#上面的例子如果一个字典的元素中,需要强调键的某种顺序的话,常用的解决办法就是通过字典的keys方法收集一个键的列表,然后使用列表的sort方法进行排序,然后使用Python的for循环逐个进行显示结果。

>>> for key in sorted(D):
...  print(key,'=>',D[key])#注意这里,要按个空格再打print啊,不然按回车要报错的。
... a
=> 1 c => 3 d => 2

#通过使用最新的sorted内置函数就可以一步完成上面对key的排序。sorted调用返回结果并对各种对象类型进行排序,上面的例子就是自动对字典的键排序。

>>> squares = [x ** 2 for x in [1,2,3,4,5]]
>>> squares
[1, 4, 9, 16, 25]
>>> squares1=[]
>>> for x in [1,2,3,4,5]:#编写了一个等效的for循环,通过在运行时手动增加列表来创建最终的列表。
... squares1.append(x
** 2) ... >>> squares [1, 4, 9, 16, 25]

#迭代和优化,for循环是真正通过的迭代工具。事实上,它们都能够工作于遵守迭代协议(这是Python中无处不在的一个概念,表示在内存中物理存储的序列,或一个在迭代操作情况下每次产生一个元素的概念,表示在内存中物理存储的序列,或一个在迭代操作情况下每次产生一个元素的对象)的任意对象。如果一个对象在响应next之前先用一个对象对iter内置函数做出响应,那么它属于后一种情况。我们在前面所见到生成器解析表达式就是这样的一个对象。

#从左到右扫描一个对象的每个Python工具都使用迭代协议。这就是上上个例子介绍的sorted调用直接工作于字典之上,我们不必调用keys方法来得到一个序列,因为字典是可迭代的对象,可以用一个next返回后续的键。这也就一意味着像上面任何列表解析表达式都可以计算一列数字的平方。

#尽管这样,列表解析和相关的函数编程工具,如map和filter,通常运行得比for循环快:这是对有大数据集合的程序有重大影响的特性之一。在Python中性能测试是一个很难应付的任务,因为它在反复地优化。

#Python中的一个主要原则就是,首先为了简单和可读性去编写代码,在程序可以工作,并证明了确实有必要考虑性能后,再考虑该问题。更多的情况是代码本身就已经足够快了。如果确实需要提高代码的性能,那么Python提供了帮助你实现的工具,包括time以及timeit模块和profile模块。

>>> D
{'a': 1, 'd': 2, 'c': 3}
>>> D['e'] = 99  #虽然能够通过给新的键值来扩展字典
>>> D {'a': 1, 'e': 99, 'd': 2, 'c': 3} >>> D['f'] #但是获取一个不存在的键值仍然是一个错误
Traceback (most recent call last): File "<stdin>", line 1, in <module> KeyError: 'f' >>> 'f' in D #编写程序时并不是总是知道当前存在什么键,可以通过in关系表达式,来查询字典中的一个键是否存在。
False >>> if not 'f' in D: #也可以通过使用Python的If语句对结果进行分支处理
... print('missing') ... missing
>>> value = D.get('x',0) #这是另外一种方法,如果获取键失败就给赋值0
>>> value
0
>>> value = D['f'] if 'f' in D else 0 #这是跟if结合,如果f键存在就将f的值赋给value,否则赋值0
>>> value
0

#默认情况下,if语句也可以有else分句,以及一个或多个elif(else if)分句进行其他的测试。它是Python主要的选择工具,并且是脚本中编写逻辑的方法。

1.7 元组

元组对象(tuple)基本上就像一个不可以改变的列表。就像列表一样,元组是序列,但是它具有不可变性,和字符串类似。从语法上讲,它们编写在圆括号中而不是方括号中,它们支持任意类型、任意嵌套以及常见的序列操作。

>>> T = (1,2,3,4)  #定义一个元组
>>> len(T) 4 >>> T + (5,6) #两个元组合并
(1, 2, 3, 4, 5, 6) >>> T[1] #查找元组偏移量是1的也就是第二个元素
2 >>> T.index(4) #查找元素4所在的索引位置
3 >>> T.count(4) #查找4这个元素在元组中出现的次数
1 >>> T[0] = 2 #元组是不可变的,如果想改变元组的值直接就报错了
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'tuple' object does not support item assignment

#上面就是元组的常用操作以及不可变的演示。

>>> T = ('spam',3.0,[1,2,3])  #元组也支持混合的类型和嵌套
>>> T[1] #查找偏移量是1的值
3.0 >>> T[2][2] #查找偏移2也就是最后的列表中的第三个元素
3 >>> T.append(4) #元素不能增长或缩短,因为它们是不可变的
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'tuple' object has no attribute 'append'

#为什么要用元组呢,它的关键是不可变性。如果在程序中以列表的形式传递一个对象的集合,它可能在任何地方改变;如果使用元组的话,则不能。

 1.8 文件

文件对象是Python代码对电脑上外部文件的主要接口。虽然文件是核心类型,但是它有些特殊:没有特定的常量语法创建文件。要创建一个文件对象,需调用内置的open函数以字符串的形式传递给它一个外部的文件名以及一个处理模式的字符串。

>>> f = open('data.txt','w')  #以写的方式打开文件
>>> f.write('Hello\n') #写入Hello并换行
6 >>> f.write('world\n') #写入world并换行
6 >>> f.close() #关闭文件,就会把写的内容写入到文件

#例如上面,创建一个文本输出文件,可以传递其文件名以及‘w’处理模式字符串以写数据,这样就在当前文件夹下创建了一个文件,并向它写入文本(文件名可以是完整的路径)。

>>> f=open('data.txt')
>>> text = f.read()
>>> text
'HelloHello\nworld\n'
>>> print(text)
HelloHello
world

>>> text.split()
['HelloHello', 'world']

#上面的例子是打开文件,默认就是r类型,然后将文件的内容读至一个字符串,并显示它。对脚本而言,文件的内容总是字符串,无论文件包含的数据是什么类型。
#文化提供了多种读和写的方法(read可以接受一个字节大小的选项,readline每次读一行等),以及其他的工具(seek移动到一个新的文件位置)。读取一个文件的最佳方式就是根本不读它,文件提供了一个迭代器(iterator),它在for循环或其他环境中自动地一行一行地读取。

#其他文件类工具:open函数能够实现在Python中编写的绝大多数文件处理。尽管这样,对于更高级的任务,Python还有额外的类文件工具:管道、先进先出队列(FIFO)、套接字、通过键访问文件、对象持久、基于描述符的文件、关系数据库和面向对象数据库接口等。例如,描述符文件(descriptor file)支持文件锁定和其他的底层工具,而套接字提供网络和进程间通信的接口。

1.9 其他核心类型

集合,它不是映射也不是序列,相反,它是唯一的不可变的对象的无序集合。集合可以通过调用内置set函数而创建,或者使用Python 3.0中新的集合常量和表达式创建,并且它支持一般的数学集合操作。

http://www.cnblogs.com/chaishao/p/5759436.html  #开头部分对集合的介绍和文件的介绍。

二、数字

2.1 数字类型介绍

Python的数字类型是相当典型的,在Python中,数字并不是一个真正的对象类型,而是一组类似类型的分类。Python不仅支持通常的数字类型(整数和浮点数),而且能够通过常量直接创建数字以及处理数字的表达式。此外,Python为更高级的工作提供了很多高级数字编程支持和对象。Python数字类型的完整工具包括:

整形和浮点数、复数、固定精度的十进制数、有理分数、集合、布尔类型、无穷的整数精度、各种数字内置函数和模块。

数字常量:在基本类型中,Python提供了:整数(正整数和负整数)和浮点数(带小数部分的数字)。Python还允许我们使用十六进制、八进制常量来表示整数,提供一个复数类型,并且允许整数具有无穷的精度(只要内存空间允许,它可以增长成任意位数的数字)。下面展示了Python数字类型在程序中的显示方式(作为常量):

#一般来说,Python的数字类型是很容易使用的,除了内置数字常量之外,Python还提供了一系列处理数字对象的工具:

表达式操作符:+、-、*、\、>>、**、&等

内置数学函数:pow、abs、round、int、hex、bin等

公用模块:random、math等

2.2 Python表达式操作符

表达式式处理数字的最基本的工具。当一个数字(或其他对象)与操作符想结合时,Python执行时将计算得到一个值。在Python中,表达式式使用通常的数学符号和操作符号写出来的。除了常用的是加减乘数,%是计算余数操作符、<<执行左移位、&计算位与的结果等。其他的则更Python化一些,并且不全都具备数值特征。例如,is操作符测试对象身份(也就是内存地址,严格意义上的相等),lambda创建匿名函数。

Python表达式操作符及程序:

下面是Python2.6和Python3中操作符的版本差异和最新添加:

1. 在python 2.6版中,值不相等可以写成X != Y或X <> Y.在python 3.0之中,后者会被移除,因为它是多余的。值不相等测试使用X != Y就行了。

2. 在Python 2.6中,一个后引号表达式‘X’和repr(X)的作用相同,转换对象以显示字符串。由于其不好理解,Python 3.0删除了这个表达式,使用更容易理解的str和repr内置函数。

3. 在Python 2.6和Python 3.0中,floor除法表达式(X // Y)总是会把余数小数部分去掉。在Python 3.0中,X / Y表达式执行真正的除法(保留余数)和Python 2.6中的传统除法(截除为整数)。

4. 列表语法([...])用于表示列表常量或列表解析表达式。后者是执行隐形循环,把表达式的结果收集到新的列表中。

5. (...)语法用于表示云组和表达式,以及生成器表达式,后者是产生所需结果的列表解析的一种形式,而不是构建一个最终的列表。

6. {...}语法表示字典常量,并且在Python 3.0中可以表示集合常量以及字典和集合解析。

7. yield和三元选择表达式和Python 2.5及其以后的版本中可用。前者返回生成器中的send(...)参数,后者是一个多行if语句的缩写形式。如果yield不是单独地位于一条赋值语句的右边的话,需要用圆括号。

8. 比较操作符可以连续使用:X <Y <z的结果与X < Y < X相同。

9. 在最近的Python中,分片表达式X[I:J:K]等同于同一个分片对象索引:X[slice(I,J,K)]。

10. 在Python 3.0中,对字典的大小比较也不再支持(尽管支持相等性测试);比较sorted(dict.items())是一种可能的替代。

 2.3 十六进制、八进制和二进制记数

Python证书能够以十六进制、八进制和二进制记数法来编写,作为一般的以10位基数的十进制记数法的补充。这些常量只是指定一个整数对象的值的一种替代方法,例如,Python 3.0和Python 2.6中的如下常量编码会产生具有3种进制的指定值的常规整数:

>>> 0o1,0o20,0o377 #八进制
(
1, 16, 255) >>> 0x01,0x10,0xFF #十六进制
(1, 16, 255) >>> 0b1,0b100000,0b111111111 #二进制
(
1, 32, 511) >>> 0b1,0b10000,0b111111111 (1, 16, 511) #都表示十进制的数
>>> oct(255),hex(255),bin(255) #将十进制数字转变成对应的进制数
('0o377', '0xff', '0b11111111')
>>> int('255'),int('377',8),int('ff',16),int('11111111',2)
(255, 255, 255, 255)
>>> int('255'),int('0o377',8),int('0xff',16),int('0b11111111',2)
(255, 255, 255, 255)

#oct函数会将十进制数转换为八进制数,hex函数会将十进制转换为十六进制数、而bin会将十进制数转换为二进制。另一种方式,内置的Int函数会将一个数字的字符串变换为一个整数,并可以通过定义的第二个参数来确定变换后的数字的进制。
2.4 位操作

除了一般的数学运算(加法、减法等),Python也支持C语言中大多数数学表达式。这包括哪些把整数当做二进制位串对待的操作。

>>> x = 1  #换算成二进制这就相当于0001
>>> x << 2 #二进制逢2进位,0001左移动两位,变成了0100,就表示十进制的4了
4 >>> x | 2 #这是二进制的或运算,前面的是0001后面的是0010,所以就是0001|0010=0011,换算成十进制就是3了
3 >>> x & 1 #这个就是与的关系,就是0001&0001=0001,也就是1
1
>>> x & 2 #这个就是0001&0010=0000,也就是0
0

#像上面这样按位进行掩码的运算,使我们可以对一个整数进行多个标志位和值进行编码。在这个领域里,Python 2.6 和Python 3.0中都支持的二进制和十六进制变得特别有用,它们允许我们按照位字符串来编写和查看数字。如果Python代码必须与由C程序生成的网络包或封装了的二进制数打交道的话,它是很实用的。尽管这样,注意位操作在Python这样的高级语言中并不像在C这样的底层语言中那么重要。
2.5 其他的内置数学工具

除了核心对象类型以外,Python还支持用于数字处理的内置函数和内置模块。例如,内置函数pow和abs,分别计算幂和绝对值,这里有一些内置match模块(包含在C语言中math库中的绝大多数工具)的例子并有一些实际中的内置函数。

>>> import math
>>> math.pi,math.e
(3.141592653589793, 2.718281828459045)
>>> math.sin(2 * math.pi / 180)
0.03489949670250097
>>> math.sqrt(144),math.sqrt(2)
(12.0, 1.4142135623730951)
>>> pow(2,4),2 ** 4
(16, 16)
>>> abs(-42.0),sum((1,2,3,4))
(42.0, 10)
>>> min(3,1,2,4),max(3,1,2,4)
(1, 4)
>>> math.floor(2.567),math.floor(-2.567)
(2, -3)
>>> math.trunc(2.567),math.trunc(-2.567)
(2, -2)
>>> int(2.567),int(-2.567)
(2, -2)
>>> round(2.567),round(2.467),round(2.567,2)
(3, 2, 2.57)
>>> (1/3),round(1/3),('%.2f'%(1/3))
(0.3333333333333333, 0, '0.33')
>>> (1/3),round(1/3,2),('%.2f'%(1/3))
(0.3333333333333333, 0.33, '0.33')

>>> '%.1f' % 2.567, '{0:.2f}'.format(2.567)
('2.6', '2.57')

#这里展示的的sum函数作用于数字的一个序列,min和max函数接受一个参数序列或者单个的参数。有各种各样的方法可以删除一个浮点数的小数位。
#上线也介绍了截断和floor方法,也可以用round,不管是为了求值还是为了显示。

#最后一个例子产生了我们通常会打印出的字符串,并且它支持各种格式化选项。

>>> import math
>>> math.sqrt(144)
12.0
>>> 144 ** .5
12.0
>>> pow(144, .5)
12.0
>>> math.sqrt(1234567890)
35136.41828644462
>>> 1234567890 ** .5
35136.41828644462
>>> pow(1234567890, .5)
35136.41828644462

#在Python中有3种方法可以计算平方根:使用一个模块函数、一个表达式或者一个内置函数。注意内置math这样的模块必须先导入,但是abs这样的内置函数不需要导入就可以直接使用,换句话说,模块是外部的组件,而内置函数位于一个隐性的命名空间内,Python自动搜索程序的变量名。这个命名空间对应于Python 3.0中名为builtins的模块。

>>> import random
>>> random.random() #选出一个0到1之间的任意浮点数
0.7293494111854583 >>> random.random() 0.7408299666488919 >>> random.randint(1,10) #选出1到10之间的随机整数
6 >>> random.randint(11,100) 37 >>> random.choice(['A','B','C']) #在一个序列中任意挑选一项
'A' >>> random.choice(['A','B','C']) 'C'

#一般模块都是导入才能使用的,上面使用标准库中的random模块时必须导入。
2.6 其他数字类型

Python的核心数字类型:整数、浮点数和复数。对于绝大多数程序员来说,需要进行的绝大多数数字处理都满足了。不过,Python还自带了一些更少见的数字类型。

小数数字:

比其他数据类型复杂一些,小数是通过一个导入的模块调用函数后创建的,而不是通过运行常量表达式创建的。从功能上来说,小数对象就像浮点数,只不过它们由固定的位数和小数点,因此小数是有固定的精度的浮点值。例如,使用小数对象,我们能够使用一个只保留两位小数位精度的浮点数。此外,能够定义如何省略和截断额外的小数数字,尽管它对平常的浮点数类型来说带来了微小的性能损失,小数类型对表现固定精度的特性(例如,钱的总和)以及实现更好的数字精度是一个理想的工具。

>>> 0.1 + 0.1 + 0.1 - 0.3  #得出来的非0确实一个浮点数
5.551115123125783e-17
>>> print(0.1 + 0.1 + 0.1 - 0.3)
5.551115123125783e-17
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3') 
Decimal('0.0')

#从上面的例子可以看出,浮点数缺乏精确性,因为用来存储数值的空间有限。结果接近0但是却没有足够的位数去实现这样的精度。正如上面显示的,能够通过调用在decimal模块中的Decimal的构造函数创建一个小数对象,并传入一个字符串,这个字符串有我们希望在结果中显示的小数位数。当不同的精度的小数在表达式中混编时,python自动升级为小数位数最多的。

>>> import decimal
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.1428571428571428571428571429')
>>> decimal.getcontext().prec=4  #设置输出为小数点后4位
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.1429')
>>> 1999 + 1.33
2000.33
>>> decimal.getcontext().prec=2
>>> pay = decimal.Decimal(str(1999 + 1.33))
>>> pay
Decimal('2000.33')

#可以设置全局精度,decimal模块中的其他工具可以用来设置所有小数数值的精度、设置错误处理等。例如,这个模块中的一个上下文对象允许指定精度(小数位数)和舍入模式(舍去、进位等)。该精度全局性地适用于调用线程中创建的所有小数。
另外还有分数,可以用Fraction模块。

2.7 集合

 Python 2.4引入了一种新的类型--集合(set),这是一些唯一的、不可变的对象的一个无序集合(collection),这些对象支持与数学集合理论相应的操作。根据定义,一个项在集合中只能出现一次,不管将它添加了多少次。同样,集合有着广泛的应用,尤其是在涉及数字和数据库的工作中。

因为它是其他对象的集合,因此,它具有列表和字典这样的对象的某些共同行为。例如集合是可以迭代的,可以根据需要增长或缩短,并且能够包含各种对象类型。将会看到,一个集合的行为很像一个无值的字典的键,但是,它还支持额外的操作。

然而由于集合是无序的,并且不会把键匹配到值,他们既不是序列也不是映射类型;它们是自成一体的类型。

>>> x = set('abcde')  #通过用set的方式来创建集合
>>> y = set('bdxyz')
>>> type(x) #可以查看一下x的类型
<class 'set'> >>> x #输出一下可以看出集合是无序的
{'a', 'd', 'e', 'c', 'b'} >>> 'e' in x #如果e在集合x中就为真
True >>> x - y #求差集,在x中但没有在y中的元素
{'a', 'c', 'e'} >>> x | y #x和y的并集
{'a', 'b', 'd', 'c', 'e', 'x', 'z', 'y'} >>> x & y #x和y的交集
{'d', 'b'} >>> x ^ y #对称差集,项目出现在x或者y中,但是不会同时出现在两者之中
{'a', 'c', 'e', 'x', 'z', 'y'} >>> x > y, x < y #测试y中的元素是否都在x中,或者x的元素都在y中
(False, False)
>>> x=set('abcd')
>>> y=set('abc')
>>> x > y, x < y
(True, False)

#集合通过表达式操作符支持一般的数学集合运算。注意,不能再一般序列上应用这些表达式,必须通过序列创建集合后才能使用这些工具。

>>> z = x.intersection(y)  #返回一个新的集合,包含x和y中的公有的元素
>>> z {'a', 'c', 'b'} >>> z.add('SPAM') #将SPAM添加到z集合中
>>> z {'a', 'SPAM', 'c', 'b'}
>>> z.update(set(['X','Y'])) #在z集合中添加一个集合,不加set也可以里面添加列表什么的,也就是添加多个元素
>>> z {'a', 'X', 'SPAM', 'Y', 'c', 'b'} >>> z.remove('b') #删除z集合中的b元素

#除了表达式,集合对象还提供了对应这些操作的方法,以及更多的支持改变集合的方法,集合add方法插入一个项目、update是按位置求并集,remove根据值删除一个项目(在任何集合实例或集合类型名上运行dir来查看所有可用的方法)。

>>> for item in set('abc'): print(item *3)  #支持for循环
... aaa ccc bbb >>> S = set([1,2,3])
>>> S | set([3,4])
{1, 2, 3, 4} >>> S | [3,4] #集合和列表是不能进行上面的那些差集之类的
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'set' and 'list' >>> S.union([3,4]) #返回一个新的集合,返回S和【3,4】中的相同元素以外的所有元素
{1, 2, 3, 4} >>> S.intersection((1,3,5)) #这就是求S和(1,3,5)的交集
{1, 3} >>> S.issubset(range(-5,5)) #判断S中的每一个元素是否都在(-5,5)中
True

#上面的例子可以看出,作为可迭代的容器,集合也可以用于len、for循环和列表解析这样的操作中。然而,由于它们都是无序的,所以不支持像索引和分片这样的操作,尽管上上面介绍的集合表达式通常需要两个集合,但是上面的例子可以看出,他们基于方法的对应形式往往对任何可迭代类型也有效。

#我这里用的是Python 3.0,可以看出集合输出的是一个字典的花括号的形式,这个语法是有意义的,因为集合基本上就像是无值的字典,集合的项是无序的、唯一的、不可改变的,因此,他们的行为和字典的键很像。由于字典键列表在Python 3.0中是视图对象,它支持像交集和并集这样的类似集合的行为这种相似性更加惊人。

#注意,在Python中{}仍然是一个字典。空的集合必须通过内置函数set来创建。

#不可变限制和冻结集合。集合是强大而灵活的对象,但是,它们只能包含不可变(即可散列)对象类型。因此,列表和字典不能嵌入到集合中,但是,如果需要存储复合值的话,元组是可以嵌入的。例如,集合中的元组可以用来表示日期、记录、IP地址等。集合本身也是不可改变的,因此,不能直接嵌入到其他集合中;如果需要在另一个集合中存储一个集合,可以像调用set一样来调用frozenset,但是,它创建一个不可变的集合,该集合不可修改并且可以嵌套到其他集合中。

#为什么使用集合?集合操作有各种各样常见的用途,其中一些比数字更加实用。例如,由于项在集合中只能存储一次,集合(set)可以用来把重复项从其他集合(collection)中过滤掉。直接把集合(collection)转换为一个集合(set),然后再转换回来即可(因为集合是可迭代的,这里的list调用对其有效):如下面的例子

>>> L = [1,2,1,3,2,4,5]
>>> set(L)
{1, 2, 3, 4, 5}
>>> L = list(set(L))
>>> L
[1, 2, 3, 4, 5]
>>> type(L)
<class 'list'>

#当遍历图形或其他的回环结构的时候,集合可以用来记录已经访问过的位置。当传递性模块重载和继承树列表程序实例,必须确保访问过的项不再循环。尽管把访问状态作为键记录到字典中很搞笑,但集合提供了几乎等同的一种替代方式。
#最后在处理较大的数据集合的时候(例如,数据库查询结果),两个集合的交集包含了两个领域中共有的对象,并集包含了两个集合中的所有项目。

 2.8 布尔型

对于Python的布尔类型有一些争论,bool原本是一个数字,因为它有两个值True和False,不过是整数1和0以不同的形式显示后的定制版本而已。

Python如今正式地有了一种明确的布尔型数据类型,叫做bool,其值为True和False,并且其值True和False是预先定义的内置的变量名。在内部在新的变量名True和False是bool的实例,实际上仅仅是内置的整数类型int的子类(以面向对象的观点来看)。True和False的行为和整数1和0是一样的,除了它们有特定的显示逻辑:它们是作为关键字True和False显示的,而不是数字1和0(从技术上来讲,bool为它的两个对象重新定义了str和repr的字符串格式)。

由于这个定制,布尔表达式和交互提示模式的输出就作为关键字True和False来显示,而不是曾经的1和0.此外,布尔型让真值更精确。例如,一个无限循环现在能够编写成while True:而不是while 1:。类似地,通过使用flag = False,可以更清楚地设置标志位。

还有对于其他实际的用途,你能够将True和False看做是预定义的设置为整数1和0的变量。大多数程序员都曾把True和False预先赋值为1和0:因为True仅仅是定制了显示格式的整数1,在Python中True+4得到了5!

>>> type(True)
<class 'bool'>
>>> isinstance(True,int)
True
>>> True == 1
True
>>> True is 1
False
>>> True or False
True
>>> True + 4
5

#上面的例子就是对上面介绍的一种展示.

三、动态类型

3.1 变量、对象和引用

当在Python中运行赋值语句a = 3时就属于:

变量创建:一个变量(也就是变量名),就像a,当代码第一次给它赋值时就创建了它。之后的赋值将会改变已创建的变量名的值。

变量类型:变量永远不会有任何的和它有关联的类型信息或约束。类型的概念是存在对象中而不是变量名中,变量原本是通用的,它只是在一个特定的时间点,简单的引用一个特定的对象而已。

变量使用:当变量出现在表达式中时,它会马上被当前引用的对象所代替,无论这个对象是什么错误。此外,所有的变量必须在使用前明确地赋值,使用未赋值的变量会产生错误。

总而言之,变量在赋值的时候才创建,它可以引用任何类型的对象,并且必须在引用之前赋值。

在Python中,每当一个变量名被赋予了一个新的对象,之前的那个对象站应用的空间就会被回收(如果它没有被其他的变量名或对象所引用的话)。这种自动回收对象的空间的技术叫做垃圾收集。

在内部,Python它在每个对象中保持了一个计数器,计数器记录了当前指向该对象的引用数目。一旦(并精确在同一时间)这个计数器被设置为零,这个对象的内存空间就会自动回收。在前面的介绍中,假设每次x都被赋值给一个新的对象,而前一个对象的引用计数器为零,就会导致它的空间被回收。

3.2 共享引用

在交互模式下,引入另一个变量,并看一下变量名和对象的变化:

>>> a = 3
>>> b = a
>>> b
3

#像上面那样,实际的效果是变量a和b都引用了相同的对象(也就是说指向了相同的内存空间)。这在Python中叫做共享引用--多个变量名引用了同一个对象。

>>> a = 3
>>> b = a
>>> a = 'spam'
>>> b
3

#从上面的例子可以看出,虽然a指向了新的对象,但是b还是使用原来的变量也就是使用原来的内存空间。要想b跟着变化还得再来下b=a。

>>> L1 = [2,3,4]
>>> L2 = L1
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[24, 3, 4]

#上面的例子是共享引用和在原处修改的例子,上面的例子,没有改变L1,改变了L1所引用的对象的一个元素。这类修改会覆盖列表对象中的某部分。因为这个列表对象是与其他对象共享的(被其他对象引用),那么一个像这样在原处的改变不仅仅会对L1有影响。也就是说,必须意识到当做了这样的修改,它会影响程序的其他部分。这里也对L2产生影响,因为它与L1都引用了相同的对象。另外,我们实际上并没有改变L2,但是它的值将发生变化,因为它已经被修改了。

>>> L1 = [2,3,4]
>>> L2 = L1[:]
>>> L1[0] = 24
>>> L1
[24, 3, 4]
>>> L2
[2, 3, 4]

#如果不想L1变化了L2也要跟着变化,需要Python拷贝对象,而不是创建引用。有很多种拷贝一个列表的办法,包括内置列表函数以及标准库的copy模块。上面的方式是一种从头到尾的分片方法。这里L1的修改不会影响L2,因为L2引用的是L1所引用对象的一个拷贝。也就是说,两个变量指向了不同的内存区域。
#注意这种分片技术不会应用在其他的可变的核心类型(字典和集合,因为它们不是序列)上,复制一个字典或集合应该使用X.copy()方法调用。而且,注意标准库中的copy模块有一个通用的复制任意对象类型的调用,也有一个拷贝嵌套对象结构(例如,嵌套了一个列表的字典)的调用:

>>> L1=[1,2,3]
>>> import copy
>>> X = copy.copy(L1)    #浅copy
>>> X = copy.deepcopy(L1)   #深copy

 

posted @ 2017-08-20 10:37  chaishao  阅读(3596)  评论(0编辑  收藏  举报