从非正式角度讲,在Python中,我们使用一些东西在做事情。"事情"采用的是像加法以及连接这样的操作形式,而"东西"指的就是我们操作的对象。接下来本文将介绍"东西"及用这些东西可以做的"事情"

从更正式的角度讲,在Python中,数据以对象的形式出现,无论是Python提供的内置对象还是使用Python或像C扩展库这样的扩展语言工具创建的对象。现在可能对对象的概念还是模糊的,但对象无非是内存的一部分,包含数值和相关操作的集合。

由于对象是Python中最基本的概念,下文将全面地体验Python的内置对象类型。

从更具体的视角来看,Python程序可以分解成模块、语句、表达式以及对象:

  • 程序由模块构成
  • 模块包含语句
  • 语句包含表达式
  • 表达式建立并处理对象

为什么使用内置类型

底层语言C中,很大一部分工作集中于用对象(或者叫数据结构)去实现应用领域的组件。需要部署内存结构、管理内存分配、实现搜索和读取例程等。这些工作听起来非常乏味(且容易出错),并且往往背离程序的真正目标。

在典型的Python程序中,这些令人头痛的大部分工作都消失了。因为Python提供了强大的对象类型作为语言的组成部分,在你开始解决问题之前往往没有必要编写对象的实现。事实上,除非你有内置类型无法提供的特殊对象要处理,最好总是使用内置对象而不是使用自己的实现,原因如下:

  • 内置对象使程序更容易编写。对于简单的任务,内置类型往往能够表现问题领域的所有结构。免费得到了如此强大的工具,如,集合(列表)和搜索表(字典),可以马上使用它们。仅使用Python内置对象类型就可以完成很多工作
  • 内置对象是扩展的组件。对于较为复杂的任务,或许仍需要提供你自己的对象,使用Python的类或C语言的接口。人工实现的对象往往建立在像列表和字典这样的内置类型的基础上
  • 内置对象往往比定制的数据结构更有效率。在速度方面,Python的内置类型优化了用C实现数据结构算法。尽管可以实现属于自己的类似的数据类型,但往往很难达到内置数据类型提供的水平
  • 内置对象是语言的标准的一部分。从某种程度上来说,Python不但借鉴了依靠内置工具的语言,而且汲取了那些依靠程序员去提供自己实现的工作或框架的语言的优点。此外,因为Python的内置工具是标准的,它们一般都是一致的。另一方面,独创的框架则在不同的环境都有所不同

内置对象类型不仅仅让编程变得更简单,而且它们也更强大和更高效。无论是否实现新的对象类型,内置对象都构成了每个Python程序的核心部分。

Python的核心数据类型

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

**注意,常量(literal)是指语法会生成对象的表达式,有时也叫做常数(constant)。并不是指不可变的对象或变量,区别于Java、C++等语言中的常量含义

对象类型        例子,常量/创建

数字          12,3.1415,3+4j,Decimal,Fraction

字符串         'hello'

列表          [1,2,'aaa','3']

字典          {'k1':'v1','k2':'v2'}

元组          (1,2,3,'a')

文件          f = open('log','r')

集合          set('aa')

其他类型        None,布尔

编程单元类型      函数、模块、类

与实现相关的类型    编译的代码堆栈跟踪

上面所列的内容并不完整,因为在Python程序中处理的每一样东西都是对象(一切皆对象)。

后面我们会了解到,像函数、模块和类这样的编程单元在Python中也是对象,它们由def、class、import和lambda这样的语句和表达式创建,并且可以在脚本间自由的传递,存储在其他对象中等。Python还提供了一组与实现相关的类型,例如编译过得代码对象,它们通常更多的关系到工具生成器而不是应用程序开发者,后面再仔细研究

我们通常把上表中的对象类型称作是核心数据类型,因为它们是在Python语言内部高效创建的,也就是说,有一些特定的语法可以生成它们。我们运行下面一行代码:

查看对象的值 python python如何查询对象的类型_字符串

从技术上讲,正在运行一个常量表达式,这个表达式生成并返回一个新的字符串对象。这是Python用来生成这个对象的一个特定语法。类似的,一个方括号中的表达式会生成一个列表,大括号中的表达式会创建一个字典等。Python中没有类型声明,运行的表达式的语法决定了创建和使用的对象的类型。

一旦创建了一个对象,它就和操作集合绑定了,只可以对字符串进行字符串相关的操作,对列表进行列表相关的操作。Python是动态类型的(它自动跟踪你的类型而不是要求声明代码),它也是强类型语言(只能对一个对象进行适合该类型的有效的操作)

数字

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

Python的数字支持一般的数学运算。除了表达式外,和Python分发的还有一些常用的数学模块,模块只不过是我们导入以供使用的一些额外工具包

查看对象的值 python python如何查询对象的类型_Python_02

math模块包括更高级的数学工具,如函数,而random模块可以作为随机数字的生成器和随机选择器

查看对象的值 python python如何查询对象的类型_Python_03

Python还包括一些较为少见的数字对象,例如复数、固定精度十进制数、有理数、集合和布尔值,第三方开源扩展领域甚至包含了更多。

字符串

就像任意字符的集合一样,字符串是用来记录文本信息的。它们是在Python中作为序列(也就是说,一个包含其他对象的有序集合)提到的第一个例子。序列中的元素包含了一个从左到右的顺序,序列中的元素根据它们的相对位置进行存储和读取。从严格意义上来说,字符串是单个字符的字符串的序列,其他类型的序列还包括列表和元组

序列的操作

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

查看对象的值 python python如何查询对象的类型_查看对象的值 python_04

在Python中,索引是按照从最前面的偏移量进行编码的,也就是从0开始,第一项索引为0,第二为1,一次类推,最后一项为len(s) - 1 所以上例中,s[5]时会报错:IndexError:string index out of range

我们也能够反向索引,从最后一个开始(正向索引是从左边开始,反向索引是从右边开始)

查看对象的值 python python如何查询对象的类型_Python_05

我们能够在方括号中使用任意表达式,而不仅仅是使用数字常量,只要Python需要一个值,我们可以使用一个常量、一个变量或任意表达式。

序列还支持一种分片(slice)操作,这是一种一步就能提取整个分片(slice)的方法

查看对象的值 python python如何查询对象的类型_元组_06

认识分片最简单的办法就是把它们看做是从一个字符串中一步就提取出一部分的方法。一般形式为s[i:j],表示"取出在s中从偏移量为i,直到但不包括偏移量为j的内容"。结果就是返回一个新的对象。

在一个分片中,左边界默认为0,并且右边界默认为分片序列的长度,这引入了一些常用法的变体:

查看对象的值 python python如何查询对象的类型_元组_07

最后作为一个序列,字符串也支持使用加号进行合并(将两个字符串拼接成一个新的字符串),或者重复(通过重复来创建一个新的字符串):

查看对象的值 python python如何查询对象的类型_字符串_08

加号对于不同的对象由不同的意义:对于数字为加法,对于字符串为拼接。这是Python的一般特性。简而言之,一个操作的意义取决于被操作的对象。这种多态的特性给Python代码带来了很大的简洁性和灵活性。由于类型不收约束,Python编写的操作通常可以自动的适用于不同类型的对象,只要它们支持一种兼容的接口

不可变性

注意:在上面的例子中,没有通过任何操作对原始的字符串进行改变。每个字符串都被定义为生成新的字符串作为其结果,因为字符串在Python中具有不可变性---在创建后就不能改变。但是,你可以创建一个新的字符串将其赋值给相同的变量名。Python会在运行过程中自动清理旧的对象

查看对象的值 python python如何查询对象的类型_查看对象的值 python_09

在Python中的每一个对象都可以分为不可变性或可变性。在核心类型中,数字、字符串和元组是不可变的,列表和字典是可变的。

类型特定的方法

前面我们了解了序列操作。对于所有的序列都适用,对弈具体的某个对象,还有一些特定的操作方法。

如,字符串的find方法是一个基本的子字符串查找的操作(返回一个传入子字符串的偏移量,没有找到返回-1),字符串的replace方法将会对全局进行搜索和替换

查看对象的值 python python如何查询对象的类型_字符串_10

寻求帮助

内置函数dir会返回一个列表,其中包含了对象的所有属性。方法是函数属性,方法也会出现在这个列表中。

查看对象的值 python python如何查询对象的类型_Python_11

一般来说,以双下划线开头并结尾的变量名是用来表示Python实现细节的命名模式,没有下划线的属性是对象能够调用的方法

dir函数简单地给出了方法的名称。要查询它们是做什么的,可以将其传递给help函数

查看对象的值 python python如何查询对象的类型_Python_12

编写字符串的其他方法

前面我们知道了字符串对象的序列操作方法和类型特定的方法。Python还提供了各种编写字符串的方法,下面我们来详细学习。如,反斜线转义序列表示特殊的字符

查看对象的值 python python如何查询对象的类型_字符串_13

Python允许字符串包括在单引号或双引号中(它们代表着相同的东西)。它也允许在三个引号中包括多行字符串常量。当采用这种形式的时候,所有的行都合并在一起,并在没一行的末尾增加换行符。

Python也支持原始(raw)字符串常量,即去掉反斜线转义机制(这样的字符串常量通常是以字母'r'开头的)。Python还支持Unicode字符串形式从而支持国际化。在Python3.0中,基本的str字符串类型也处理Unicode(这是有意义的,因为ASCII文本是一种简单的Unicode),并且用bytes类型表示原始字节字符串;在Python2.6中,Unicode是一种单独的类型,str处理8位字符串和二进制数据。在Python3.0中,文件也改变为返回和接受str,从而处理二进制数据的文本和字节

模式匹配

字符串对象的方法能够支持基于模式的文本处理。需要导入re模块,后面详细介绍

列表 

Python的列表对象是最通用的序列。列表是一个任意类型的对象的位置相关的有序集合,它没有固定的大小,是可变的,通过对偏移量进行赋值以及其他方法进行操作

序列操作 

由于列表是序列的一种,列表支持所有的序列操作,跟字符串一样,只是返回的结果往往是列表

类型特定的操作

Python的列表与其他语言中的数组有些类似,但是列表要强大的多。其中一方面是,列表没有固定类型的约束。列表没有固定大小,能够按照需要增加或减少列表元素

查看对象的值 python python如何查询对象的类型_查看对象的值 python_14

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

查看对象的值 python python如何查询对象的类型_Python_15

列表sort方法默认按照升序对列表进行排序,而reverse对列表进行翻转。这些方法都直接对列表进行了改变

边界检查

尽管列表没有固定的大小,Python仍不允许引用不存在的元素。超出列表末尾之外的索引总是会导致错误,对列表末尾范围之外赋值也是如此

查看对象的值 python python如何查询对象的类型_查看对象的值 python_16

这是有意为之,由于去给一个列表边界外的元素赋值,往往会得到一个错误(而在C语言中,情况比较糟糕,因为它不会像Python这样进行错误检查)。在Python中并不是默默地增大列表作为响应,而是会报错。

嵌套

Python核心数据类型的一个优秀的特性就是它们支持任意的嵌套。能够以任意的组合对其进行嵌套,并可以多个层次进行嵌套。这种特性的一个直接的应用就是实现矩阵,或者Python中的"多维数组"。一个嵌套列表的列表能完成这个基本操作:

查看对象的值 python python如何查询对象的类型_查看对象的值 python_17

列表解析

处理序列的操作和列表的方法中,Python还包括了一个更高级的操作,称作列表解析表达式(list comprehension expression),从而提供了一种处理想矩阵这样结构的强大工具。例如,假设我们需要从矩阵中提取第二列。因为矩阵是按照行进行存储的,所以通过简单的索引即可获取行,使用列表解析简单获取列

查看对象的值 python python如何查询对象的类型_元组_18

列表解析源自集合的概念。它是一种通过对序列中的每一项运行一个表达式来创建一个新列表的方法,每次一个,从左至右。列表解析是编写在方括号中的(提醒你在创建列表这个事实)并且由使用了同一个变量名的(这里是row)表达式和循环结构组成。

实际应用中的列表解析可能更复杂

查看对象的值 python python如何查询对象的类型_查看对象的值 python_19

第一个操作,把他搜集到的每一个元素都加1,第二个操作使用了一个if条件语句,通过取余过滤掉了结果中的奇数。列表解析创建了新的列表作为结果,但是能够在任何可迭代的对象上进行迭代。

字典

Python中的字典是完全不同的东西:它们不是序列,而是一种映射(mapping)。映射是一个其他对象的集合,但是它们是通过键而不是相对位置来存储的。实际上,映射并没有任何可靠的从左至右的顺序。它们简单的将键映射到值。字典是Python核心对象集合中的唯一的一种映射类型,是可变的

映射操作

作为常量编写时,字典编写在大括号中,并包含一系列的"键:值"对。在我们需要将键与一系列值相关联的时候,字典是很有用的

查看对象的值 python python如何查询对象的类型_查看对象的值 python_20

我们可以通过键对这个字典进行索引来读取或改变所关联的值。字典的索引操作使用的是和序列相同的语法,但是在方括号中的元素是键

查看对象的值 python python如何查询对象的类型_Python_21

尽管可以使用大括号这种常量形式,最好还是见识一下不同的创建字典的方法。如,下面开始一个空的字典,然后每次以一个键来填写它。与列表中禁止边界外的赋值不同,对于一个新的字典的键赋值会创建该键

查看对象的值 python python如何查询对象的类型_Python_22

重访嵌套

上面的例子中,我们使用字典去描述一个人物,用了四个键。假设信息更复杂一些,我们需要去记录名(first name)和姓(last name),并有多个工作的头衔。事实上这产生了另一个Python对象嵌套的应用。下面的这个字典,一次将所有内容编写进一个常量,将可以记录更多的结构化信息

查看对象的值 python python如何查询对象的类型_Python_23

顶层使用了四个键(分别是name、age、sex、job),值比较复杂:一个嵌套的字典作为name的值,一个嵌套的列表作为job的值。能够获取这个结构的组件,就像之前在矩阵中所做的那样,但是这次索引的是字典的键

查看对象的值 python python如何查询对象的类型_元组_24

键的排序:for循环

作为映射,字典仅支持通过键获取元素。尽管这样,在各种常见的应用场合,通过调用方法,它们也支持类型特定的操作

我们前面提过,字典不是序列,它们是无序的。这以为着我们创建一个字典,将它输出,它的键也许会以与我们输入时不同的顺序出现。那么,在一个字典中,如果我们确实需要强调某种顺序的时候,应该怎么做呢?一个常用的解决办法就是通过字典的keys方法收集一个键的列表,使用列表的sort方法进行排序,然后使用Python的for循环逐个显示结果

查看对象的值 python python如何查询对象的类型_字符串_25

我们可以使用sorted内置函数直接完成。sorted调用返回结果并对各种对象类型进行排序,本例中自动对字典的键进行排序:

查看对象的值 python python如何查询对象的类型_元组_26

迭代和优化

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

这也就意味着像下面这样的任何列表解析表达式都可以计算一列数字的平方:

查看对象的值 python python如何查询对象的类型_字符串_27

能够编写成一个等效的for循环,通过在运行时手动增加列表来创建最终的列表:

查看对象的值 python python如何查询对象的类型_查看对象的值 python_28

列表解析和相关的函数编程工具,如map和filter,通常运行的比for循环快(也许快了两倍):这是对有大数据集合的程序有重大影响的特性之一。在Python中性能测试是一个很难应付的任务,因为它在反复的优化,版本与版本之间差别很大

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

不存在的键:if测试

尽管我们能够通过给新的键赋值来扩展字典,但是获取一个不存在的键值仍然是一个错误。

查看对象的值 python python如何查询对象的类型_查看对象的值 python_29

 我们该怎样处理并避免这种错误发生呢?一个技巧就是首先进行测试。in关系表达式允许我们查询字典中一个键是否存在,并可以通过使用Python的if语句对结果进行分支处理

 

查看对象的值 python python如何查询对象的类型_字符串_30

这里有其他的方法创建字典并避免获取不存在的键:get方法(带有一个默认值的条件索引)、Python 2.X的has_key方法(3.0不可用)、try语句(捕获异常工具),以及if/else表达式

查看对象的值 python python如何查询对象的类型_元组_31

元组

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

查看对象的值 python python如何查询对象的类型_Python_32

在Python 3.0中,元组还有两个专有的可调用方法

查看对象的值 python python如何查询对象的类型_字符串_33

元组不可变

查看对象的值 python python如何查询对象的类型_字符串_34

元组支持混合的类型和嵌套

查看对象的值 python python如何查询对象的类型_元组_35

为什么要用元组

元组支持的操作如此少,我们为什么要用它呢?其实,用它的原因是它不可变。在程序中以列表的形式传递一个集合对象,它可能在任何地方改变;如果使用元组的话,则不能。也就是说,元组提供了一种完整性的约束

文件

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

查看对象的值 python python如何查询对象的类型_Python_36

 

查看对象的值 python python如何查询对象的类型_查看对象的值 python_37

两种方式打开文件结果是一样的,我常用第二种,第二种不用我们手动去关闭文件流

关于更多文件操作的相关知识后面详细介绍

其他文件类工具

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

其他核心类型

目前为止除了我们看到的核心类型外,还有其他的或许能够称得上核心类型的类型,这取决于我们定义的分类有多大。如,集合,它不是序列也不是映射,相反,它是唯一的不可变的对象的无序集合。集合可以通过调用内置函数set创建,或者使用Python 3.0中对的集合常量和表达式创建,并且它支持一般的数学集合操作

查看对象的值 python python如何查询对象的类型_Python_38

Python添加了一些新的数值类型:十进制数(固定精度浮点数)和分数(有一个分子和一个分母的有理数)。它们都是用来解决浮点数学的局限性和内在的不精确性

查看对象的值 python python如何查询对象的类型_元组_39

Python添加了布尔值(预定义的True和False对象实际上是定制后以逻辑结果显示的整数1和0),以及长期以来一直支持的特殊的占位符对象None(它通常用来初始化名字和对象)

查看对象的值 python python如何查询对象的类型_Python_40

如何破坏代码的灵活性

内置函数type返回的类型对象是赋给该类型的另一个对象的对象,其结果在Python 3.0中略有不同,因为该类型已经完全和类结合起来了

#In Python 2.7

查看对象的值 python python如何查询对象的类型_查看对象的值 python_41

#In Python 3.6

查看对象的值 python python如何查询对象的类型_Python_42

除了允许交互的研究对象,这个函数的实际应用是,允许编写代码来检查它所处理的对象类型。实际上,在Python脚本中至少有三种方法可以做到这点:

查看对象的值 python python如何查询对象的类型_字符串_43

其实,在Python中这样做基本上都是错误的(这也是一个有经验的C程序员刚开始使用Python时的一个标志)。后面,开始编写函数这样较大的代码单元的时候,再澄清其原因,但这是一个(可能是唯一的)核心Python的概念。在代码中检验了特定的类型,实际上破坏了它的灵活性,即限制它只能使用一种类型工作。

这个与前面提到的多态思想有些关联,它是由Python没有类型声明而发展出来的。在Python中,我们编写对象接口(所支持的操作)而不是类型。不关注与特定的类型意味着代码可以自动地适应它们中的很多类型:任何具有兼容接口的对象均能够工作,而不管它是什么对象类型。

用户定义的类

类是面向对象编程中的内容,此处简单提一下,后面会系统介绍。用抽象的属于来说,类定义了新的对象类型,扩展了核心类型。假如希望有一个对象类型对职员进行建模。Python中没有这样特定的核心类型,我们自己来自定义一个类型来满足这个要求

查看对象的值 python python如何查询对象的类型_查看对象的值 python_44

这个类定义了一个新的对象类型,有name和pay两个属性(有时候叫做状态信息),也有两个小的行为编写为函数(通常叫方法)的形式。就像函数那样去调用类,会生成我们新类型的实例,并且的类的方法调用时,类的方法自动获取被处理的实例(其中的self参数)

查看对象的值 python python如何查询对象的类型_元组_45

隐含的"self"对象是我们把这叫做面向对象模型的原因,即一个类中的函数总有一个隐含的对象。