
1.2 Python语言基础
1.2.1 入门概念
我们虽然可以使用ArcGIS的Python脚本编写地学数据处理代码,但是仍然需要学习基本的Python语法。Python是一种比较简单的编程语言。
本节介绍Python基本的语法,用户能够初步掌握变量的定义与赋值。使用不同的语句、对象、读写数据文件及导入第三方的Python代码,就可以尝试在ArcGIS中用Python编写代码来处理地学数据了。
每种编程语言都有一系列规则,描述在那种语言中什么样的字符串被认为是有效程序。这些规则定义了语法。Python语言也不例外,其也是通过自身的语法定义的一种编程语言。下面介绍Python的语法基础。
1.标识符
字母、数字、下画线组成了Python的标识符。在Python中,所有标识符可以以英文、下画线开头,但是不能以数字开头;Python的标识符区分字母大小写;以下画线开头的标识符是有特殊含义的,如以单下画线开头的变量(_abc)表示不能直接访问的类属性,需要通过类提供的接口进行访问,不能用from *** import *导入;以双下画线开头的变量(__abc)表示类的私有成员;以下画线开头并以下画线结尾的变量(_abc_)表示Python中特殊用法的专用标识,如“_init_()”标识类的构造函数。
2.注释代码
Python脚本语言具有通用的代码结构。脚本开始时,应说明代码的编写人员姓名、代码的功能描述,代码编写日期和修改日志等基本信息,便于代码的阅读。此外,在代码编写过程中通常可以使用注释的方式来对程序功能、思路等进行描述。多写注释是一种良好的编程风格。
可以用#或##开头的语句表示注释。注释符号后的任何内容都不会被编译执行。
3.导入模板
Python除语言内置的一些函数外,最大的特点就是可以调用外部模块的函数功能。比如,可以通过math模块进行数值计算,可以通过R模块进行数理统计方面的计算。网络上有大量的模块资源,用户可以根据需要进行调用,这是使用Python最大的优点之一。
模块通过import语句导入,如用户要使用ArcGIS提供的地学数据处理模块,必须首先导入ArcPy模块。可以在程序的第一行添加导入该模块的语句(一般写在整个程序注释说明的后面),如下:

1.2.2 变量、函数和类的定义使用
1.变量的定义
在创建变量时,变量名必须符合以下规则:
(1)变量名中可以有数字、字母和下画线;
(2)变量名的第一个字符必须是字母;
(3)除下画线外,变量名中不允许出现其他特殊字符;
(4)变量名不能和Python语言自带的关键词相同。
Python语言自带的关键词常见的有class、if、for、while等。
下面是几个命名合法的变量名:
featureClassParcel
filedPopulation
filed2
my_name
下面是几个命名不合法的变量名:
class(Python关键词)
return(Python关键词)
$featureClass(非法字符、没有以字母开头)
2fileds(没有以字母开头)
parcels&class(非法字符)
需要注意的是,Python语言属于C语言系列,它是一种对大小写敏感的编程语言。使用Python语言编程时,变量的名字必须一致,比较常用的策略是采用驼峰型的变量,也就是第一个字符采用小写,变量后续中有独立词义的第一字母取大写。下面是一个例子:

2.变量的类型
在Python语言中,变量是动态生成的,所谓动态生成是指在编程时可以定义一个变量并给它赋值,而不需要先定义该变量的类型。变量主要包括简单的数据类型和复杂的数据类型。简单的数据类型有字符串型(string)、数值型(number)等;复杂的数据类型有列表型(list)、元组型(tuple)、字典型(dictionary)和对象型(object)等。
在其他语言中,通常在使用变量时必须首先定义变量的名称和类型。但是在Python语言中却不需要这样做。Python中的变量不需要声明,变量的赋值操作就是变量声明和定义的过程。每个变量在内存中被创建,包括变量的标识、名称和数据等信息。在使用前必须对每个变量赋值,变量赋值以后该变量才会被创建。
等号运算符(=)用来给变量赋值。等号运算符左边是变量名,右边是存储在变量中的值,如下:

下面详细介绍Python的数据类型。
1)简单的数据类型
Python有很多简单的数据类型。字符串型数据有很多操作功能,在编程时使用也非常广泛,因此首先介绍字符串数据类型。
字符串型(string)
字符串是字符按照一定顺序排列的集合,主要用于存储和表达文本信息。字符串型数据在赋值时由一对单引号或双引号表示。在地理信息系统(Geographic Information System,GIS)中,字符串型数据常用来表示地址名称、对象名称、where子句的条件或其他可用文本表示的信息。
Python语言对字符串型数据有很多操作方式。字符串连接是用得最多的操作方式之一,可以使用“+”操作符实现两个字符串的连接,连接后产生一个新的字符串,如下:

运行这段代码后,会得到以下结果:

字符串是否相等可用“==”算子判断。这里一定要注意,不要把两个变量是否相同的判断算子和赋值算子混淆,给变量赋值使用一个等号,而判断两个变量是否相同使用两个等号。

运行这段代码后,结果如下:

字符串能够用in算子测试包含关系,如果第一个字符串包含在第二个字符串里,则返回True,如下:

运行这段代码后,结果如下:

还可以通过索引(indexing)取到字符串里的每一个字符,或者通过截取(slicing)取到一个子字符串。字符可以通过“[]”下标取到。例如,想取到上面fcName中的第一个字符,可以使用fcName[0]获得,这里的索引号可以使用负数,表示从字符串的最后一个字符向前提取。需要注意的是,字符串中的最后一个字符如果要用负数提取,下标为“-1”,如上面的例子,提取fcName字符串中最后一个字符可以用fcName[-1]来实现。下面举几个字符串提取的例子。

这里的索引号,下标是从0开始的,这要和VB等语言区别。进一步提取字符串的方法和上面类似,不过要给出偏移字符的数量,如下:

运行这段代码后,结果如下:

字符串操作还有很多算子,如求字符串的长度、大小写转换、删除空格符号、从某个字符串中查找特定字符、替换字符、分割字符及格式化字符输出等,读者可以自行查阅相关的资料。
在使用Python处理ArcGIS地学数据时,经常需要访问存储在本地或者服务器上的文件。通常使用一个字符串来存储这个文件全名。Python将反斜杠“\”用作转义字符。例如,“\n”表示换行符,“\t”表示制表符。在Python语言中反斜杠字符用于表示escape字符和续行字符,因此要表示文件路径时,必须采用双反斜杠(而不是一条)或一个正斜杠“/”或一个反斜杠前加一个r来表示,才能避免语法错误。下面给出几个例子。
非法的路径变量赋值:

合法的路径变量赋值:

数值型(number)
Python数值型主要支持int、long、float和复杂的数据。数值型数据赋值方法和上面类似,只是不需要单引号或者双引号,并且赋的值必须是个数字。
Python支持数值的基本操作,如加、减、乘、除等。内置的数值方法只提供了对数值的基本运算功能,如果想用更加复杂的运算,如绝对值、三角函数、对数、指数等,可以通过math模块完成,math模块中有很多数值计算函数。当然,使用math模块时,必须在程序前面首先导入math模块,语句如下:

可以通过dir命令查看math库中的函数,方法如下:

运行后结果如下:

dir(module)是一个非常有用的命令,可以通过它查看任何模块中所包含的工具。在 math 模块中,可以计算 sin(a)、cos(a)、sqrt(a)等各种常见的数值。
这些计算的方式我们称为函数。模块 math 中提供了各种计算函数,如计算乘方,可以使用 pow函数。但是这些函数怎么用呢?Python提供了help命令,可供使用者查看每个函数的使用方法。如要想查看pow函数的用法,可以输入以下命令:

输出结果如下:

第一行意思是说,这里是 math 模块的内建函数 pow的帮助信息(built-in称为内置函数,代表这个函数是Python默认就有的)。第三行表示这个函数的参数有两个,也是函数的调用方式。第四行是对函数的说明,返回“x**y”的结果,并且在后面解释了“x**y”的含义。读者可以使用这个命令查看不同函数的用法。
2)复杂的数据类型
序列是Python中最基本的数据结构。序列中的每个元素都分配有一个数字,即它的位置或索引,第一个索引是“0”,第二个索引是“1”,以此类推。序列的顺序也可以反过来,反过来最后一个索引是“-1”,倒数第二个索引是“-2”,以此类推。Python有6个序列的内置类型,但最常用的是列表型和元组型。
列表型(list)
列表是最常用的Python数据类型,列表的命名规则就是一个方括号“[]”,数据项以方括号内的逗号分隔出现。列表的数据项不需要具有相同的类型。创建一个列表,只要把逗号分隔的不同的数据项使用方括号括起来即可,如下所示:

要访问列表中的元素,只需按照下标索引操作即可,如下面的例子:

输出:

可以替换列表元素:

就会输出:

可以使用del语句删除列表的元素:

输出:

元组型(tuple)
Python的元组与列表类似,不同之处在于元组的元素不能修改,元组使用圆括号,列表使用方括号。元组创建很简单,只需要在圆括号中添加元素,并使用逗号隔开即可。实例如下:

字典型(dictionary)
字典也是Python语言提供的一种对象集合管理方式,它和列表比较类似,但是字典是非排序的对象集合。和列表通过索引序号访问列表对象不同,字典主要通过关键字(key)存储和获取对象。在字典中,每个关键字都有对应的值。和列表类似,字典通过dictionary类提供了对象集合增加元素和删除元素的操作方法。
下面给出创建和操作字典对象的例子:

对象型(object)
在Python中可以对对象进行赋值等各种操作。在ArcPy中会大量使用对象,读者将在后面的介绍中看到ArcPy中对象操作的具体方法,这里不再赘述。
综上所述,可以给变量赋值的数据类型如表1.2所示。
表1.2 可以给变量赋值的数据类型

3.函数
函数是组织好的、可重复使用的、用来实现单一或相关功能的代码段。函数能提高应用的模块性和代码的重复利用率。Python提供了许多内置函数,如print()等。用户也可以自己创建函数,这被称为用户自定义函数。
用户可以定义一个自己想要功能的函数,以下是简单的规则。
(1)函数代码块以 def 关键词开头,后接函数标识符名称和圆括号。
(2)任何传入参数和自变量必须放在圆括号中间,圆括号可以用于定义参数。
(3)函数的第一行语句可以选择性地使用文档字符串,用于存放函数说明。
(4)函数内容以冒号起始,并且缩进。
(5)以return [表达式] 结束函数时,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
1)函数的定义
以下为一个简单的Python函数,它将一个字符串作为传入参数,再打印到标准显示设备上。

2)函数的调用
定义一个函数需要给定函数名称,指定函数里包含的参数和代码块结构。
这个函数的基本结构完成以后,可以通过另一个函数调用执行,也可以直接从Python提示符执行。以下实例调用了printme()函数:

在Python中,strings、tuples和numbers是不可更改的对象,而list和dictionary等则是可以更改的对象,因此需要注意这些参数在函数传递时是否可更改。Python函数的参数传递有以下两种。
(1)不可变类型:类似C++的值传递,如整数、字符串、元组。例如,fun(a)传递的只是a的值,没有影响a对象本身。在fun(a)内部修改a的值,只是修改另一个复制的对象,不会影响a本身。
(2)可变类型:类似C++的引用传递,如列表、字典。例如,列表变量la作为参数,调用函数fun(la),则是将la真正传递过去,修改后fun外部的la也会受影响。
Python中一切都是对象,严格意义上不能说值传递还是引用传递,应该说传不可变对象和传可变对象。
4.类
类和对象是面向对象编程的基础。Python虽然总体上是一种面向过程的语言,但是也支持面向对象的编程。在面向对象的编程语言中,类主要用于创建对象的实例,一个类可以创建多个对象实例,每个对象都享用类相同的属性和方法,但是每个对象中的数据通常是不一样的。在Python语言中,对象是一种复杂的数据类型,包括属性和方法,也能像其他数据类型的变量一样赋值。
1)面向对象技术的几个基本概念
(1)类(class):用来描述具有相同属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
(2)类变量:在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
(3)数据成员:类变量或者实例变量用于处理类及其实例对象的相关数据。
(4)方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫作方法覆盖(override),也称为方法重写。
(5)实例变量:定义在方法中的变量,只作用于当前实例的类。
(6)继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。
(7)实例化:即创建类的实例、类的具体对象。
(8)方法:类中定义的函数。
(9)对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
2)类的定义
使用class语句来创建一个新类,class之后为类的名称且类以冒号结尾。

以下是一个简单的Python类的例子。

empCount变量是一个类变量,它的值将在这个类的所有实例之间共享,可以在内部类或外部类使用Employee.empCount访问。
第一个__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法。如果开发者没有为该类定义任何构造方法,那么Python会自动为该类定义一个只包含一个self参数的默认构造方法。
self代表类的实例,self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。
3)类的使用
定义好类之后,要使用类的功能时,必须首先将类实例化,并将其赋给一个对象,通过这个对象可以访问类的属性和方法。
(1)创建实例对象。
实例化类在其他编程语言中一般使用关键字new,但是在Python中并没有这个关键字,类的实例化类似函数调用方式。
以下使用类的名称Employee来实例化,并通过__init__()方法接受参数,Python在进行类的实例化时,不需要使用new。

(2)访问属性。
可以使用点“.”来访问对象的属性,如以下使用类的名称访问类变量

1.2.3 语句
在Python中,每一行代码称为一条语句。Python中有许多不同类型的语句,如变量创建和赋值语句、判断语句、循环语句等,它们同样都要遵循Python的语法规定。上面已经介绍了变量的创建和赋值语句,下面介绍判断语句、循环语句、try语句、with语句、break与continue语句、pass语句。
1.判断语句
在Python中主要使用if/elif/else实现判断语句。通过判断条件是否满足(true/false),判断语句可以帮助用户控制程序的流程。需要注意的是,在Python里面是没有switch语句的,多分支判断需要用别的方式来实现。下面举几个判断语句的例子。
1)单分支判断语句(if)

2)双分支判断语句(if else)

3)多分支判断语句(if elif else)

通过对判断语句的简单介绍,相信读者已对Python的判断语句有了一个比较好的认识。读者只需要熟练掌握上面的3个句法,就能写出所需要的控制结构。
2.循环语句
1)while循环
Python编程中while语句用于循环执行程序,即在某种条件下,循环执行某段程序,以处理需要重复处理的相同任务,其基本形式为:

实例:

运行结果是:

while语句还有另外两个重要命令——continue和break,continue用于跳过循环,break则用于退出循环。
2)for循环
Python中的for循环可以遍历任何序列的项目,如一个列表或者一个字符串。for循环的语法格式如下:

实例:

运行结果是:

3)嵌套循环
Python语言允许在一个循环体内嵌入另一个循环。嵌套循环可以是while与while的嵌套、while与for的嵌套,也可以是for与for的嵌套。
另外,while循环、for循环、嵌套循环都有循环控制语句来更改语句执行的顺序。Python提供了break、continue、pass三个循环控制语句。
3.try语句
与其他语言相同,在Python中,try语句主要用于处理程序正常执行过程中出现的一些异常情况,如语法错误(Python作为脚本语言没有编译的环节,在执行过程中对语法进行检测,出错后发出异常消息)、数据除零错误、从未定义的变量上取值等。
默认情况下,在程序段的执行过程中,如果没有提供try/except的处理,脚本文件执行过程中所产生的异常消息会自动发送给程序调用端,如Pythonshell,而Pythonshell对异常消息的默认处理是终止程序的执行并打印具体的出错信息。这也是在Pythonshell中执行程序错误后所出现的出错打印信息的由来。
Python主要提供了两种try语句:try/except/else(处理异常)和try/finally(无论是否发生异常都将执行最后的代码)。
1)try/except/else语法格式

2)try/finally语法格式

下面给出一个简单的例子说明try语句的使用,读者可以修改变量number的值测试不同的运行结果。

4.with语句
有一些任务,可能需要事先设置,事后做内存自动清理工作。对于这种情况,Python的with语句提供了一种非常方便的处理方式。
下面给出一个例子,获取一个文件句柄,从文件中读取数据,然后关闭文件句柄。
不用with语句,代码如下:

这里会出现两个问题:
(1)可能忘记关闭文件句柄;
(2)文件读取数据发生异常,没有进行任何处理。
下面是处理上面两个问题的加强代码:

上面的代码虽然解决了两个问题,但是代码冗长。Python提供的with语句不仅可以“优雅地”解决问题,而且还可以很好地处理上下文环境产生的异常。下面是使用with语句的代码:

5.break与continue语句
在Python中,break和continue语句用于改变普通循环的流程。通常情况下,循环遍历一段代码,直到判断条件为False时结束循环,但有时,可能在一定的需求下应该终止当前迭代甚至整个循环,这种情况下需要使用break和continue语句。
1)break语句
在Python中,break语句终止当前循环中下一条语句的继续执行。break的最常见用途是一些外部条件被触发需要从循环中跳出。break语句可用在while循环和for循环。例如:

运行结果:

2)continue语句
continue语句用于结束当前循环中所有语句的执行,将控制返回到循环的顶部,进行下一次循环。continue语句可用在while循环和for循环语句。例如:

运行结果:

6.pass语句
当编写一个程序时,若执行语句部分思路还没有完成,可以用pass语句来占位,也可以将其理解成一个标记,即要过后来完成的代码。例如:

上述代码的含义:定义一个函数ArcPython(),但是函数整体部分暂时还没有完成,又不能空着不写内容,因此可以用pass来代替,占一个位置。
pass语句也常用于为复合语句编写一个空的主体,比如令一个while语句无限循环,每次迭代时不需要任何操作,可以这样写:

这只是一个例子,现实中最好不要编写这样的代码,因为执行代码块为pass也就是什么也不做,这时Python会进入死循环。
1.2.4 数据文件操作
掌握了Python语言的基本语法后,首先要运用Python语句对数据文件进行读和写的操作。在计算机硬盘上读取或写入文件是最重要的操作,Python内置的对象提供了多种读写文件的功能,本书只介绍其中最基本的部分。读写文件最重要的功能主要包括打开文件、关闭文件、从文件读取数据和向文件写入数据。
Python的open()函数创建了一个file对象,该对象提供了获取计算机上文件的一个链接。读写文件时,必须使用open()函数。open()函数有两个参数,第一个参数是要读写文件的路径,第二参数是操作文件的模式。操作文件的模式主要有3种:read(r)、write(w)和append(a)。如果只有r参数,表示只打开文件读取数据;w参数则表示打开文件后往文件中写入数据,这会覆盖文件中已有的数据内容,所以这种模式要谨慎使用;a参数表示追加模式,允许用户打开文件并往文件中写入数据,但是写入的数据只是追加在文件的最后,而不会覆盖原来的数据内容。下面介绍文件读取的例子:

完成读写操作后,一定要使用close()函数关闭文件。
1.读取文件数据
当一个文件被打开后,就可以采用不同的方法来读取数据了。最典型的数据读取方式是按行读取,这种方法主要针对文本文件。Python提供了readline()函数读取文件的一行数据。readline()函数能够一次读取文本文件的一行数据,并交给一个字符串变量。接下来需要使用一个循环机制,一行一行地读取下面的文件,只到文件结束。如果想要一次直接读取整个文件并赋值给一个变量,可以使用read()函数,它可以直接从文件头读到文件尾。如果想一次读取整个文件,并将文件的内容按照行分割成不同的字符串,则可以使用readlines()函数。
下面给出一个例子,读取气象雨量计的文本文件,统计一个月的下雨量。

2.写入文件数据
Python提供了很多可以往文件中写入数据的方法,write()函数是一种比较简单的写文件的方法,该函数只需要一个参数。

1.2.5 数据库操作
数据库操作是地理专业常用的功能,目前ArcGIS很多地方都涉及地学数据库的概念,这就要求使用者掌握Python读写数据库的基本技能。这里以Python操作Access数据库为例,介绍Python操作数据库的一般功能,其他数据库操作大同小异,读者可以类推。在Python操作Access数据库之前,首先应安装了Python和PythonforWindows extensions。Python操作Access数据库步骤如下。
1.建立数据库连接

2.打开一个记录集

3.对记录集操作

4.关闭记录集和连接句柄

1.2.6 中文字符操作
在GIS操作中会大量使用到中文目录或者中文的文件名,Python2对中文的支持还不太完善,而Python3则基本解决了中文问题。在使用Python2编程时,需要对中文字符进行特殊处理,才能使程序正确运行。若在程序中直接使用了汉字字符串,则必须使用编码转换,如Grouplayer_name=="图层组1".decode('gb2312')。这里decode的作用是将其他编码的字符串转换成unicode编码,表示将gb2312编码的字符串str1转换成unicode编码。encode的作用是将unicode编码转化成其他编码的字符串,表示将unicode编码的字符串str2转换成gb2312编码。因此,转码的时候一定要先明确字符串str是什么编码,然后decode成unicode,最后根据需要再encode成其他编码。
下面假设电脑桌面上有中文文件夹“中文测试文件夹”,该文件夹中有一个中文文件“测试文档.txt”,以Python读取中文路径为例,介绍几种常见的处理方式。
1.路径拆分单独编码

程序运行后输出如下:

需要注意的是,拆分时,第一个部分最后不能是反斜杠,即不能这样拆分

否则会报错,具体可自行测试。
这个程序还用到了中文字符的判断比较,判断文件夹名是否等于“中文测试文件夹”,这个用法在后面的编程中非常有用,如在地理信息系统二次开发中经常要判断图层名是否为所需的图层名,字段名是否为所需字段名,这就涉及中文字符串的比较,读者可以参考这个例子来实现自己想要的判断。
两者的区别是一种用UTF-8编码(也会转化成unicode供内存读取),另一种用unicode直接供内存读取。
2.将路径整体编码为unicode格式
将上述代码中读取中文测试文件夹部分改为以下代码:

程序运行后输出如下:

3.用raw_input方式输入路径
路径中可以含有中文字符,但是需要将终端的输入编码通过decode函数转换成unicode编码,例子如下:
