购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第2章 Python必须知道的基础语法

Python的语法非常简练,因此用Python编写的程序可读性强、容易理解。本章将向读者介绍Python的基本语法及其概念,并将其与目前流行的开发语言进行比较。Python的语法与其他高级语言有很多不同,Python使用了许多标记作为语法的一部分,例如空格缩进、冒号等。

本章的知识点:

·Python文件的扩展名

·Python的编码规则

·数据类型

·变量和常量的定义和使用

·运算符及表达式

2.1 Python的文件类型

Python文件类型分为3种,分别是源代码、字节代码、优化代码。这些代码可以直接运行,不需要进行编译或者链接。这正是Python这门语言的特性,Python的文件通过Python解释器解释运行。Windows中有python.exe与pythonw.exe,一般安装在路径C:\Python33中,当然也可以改变它的路径,只要保证环境变量设置正确即可。在*nix系统中,Python解释器被安装在目标机器的/usr/local/bin/python目录下,将/usr/local/bin路径放进shell的搜索路径中,即可通过python命令在终端调用。

2.1.1 源代码

Python源代码的扩展名以py结尾,可在控制台下运行。Python语言写的程序不需要编译成二进制代码,可以直接运行源代码。pyw是Windows下开发图形用户接口(Graphical user interface)的源文件,作为桌面应用程序的后缀名。这种文件是专门用于开发图形界面的,由pythonw.exe解释运行。以py和pyw为后缀名的文件可以用文本工具打开,并修改文件的内容。

2.1.2 字节代码

Python源文件编译后生成pyc后缀的文件,pyc是编译过的字节文件,这种文件不能使用文本编辑工具打开或修改。pyc文件是与平台无关的,因此Python的程序可以运行在Windows、UNIX、Linux等操作系统上。py文件直接运行后即可得到pyc类型的文件,或通过脚本生成该类型的文件。下面这段脚本可以把hello.py编译为hello.pyc。


import py_compile
py_compile.compile('hello.py')

保存此脚本,运行后即可得到hello.pyc文件。

2.1.3 优化代码

扩展名为pyo的文件是优化过的源文件,pyo类型的文件需要用命令行工具生成。pyo文件也不能使用文本编辑工具打开或修改。下面把hello.py编译成hello.pyo。

(1)启动命令行窗口,进入hello.py文件所在的目录。例如:


cd /D D:\developer\python\example\02\2.1

D:\developer\python\example\02\2.1是笔者设置的hello.py文件所在的目录,读者可根据自己的环境进行修改。

(2)在命令行中输入python-O-m py_compile hello.py,并按回车键。


python -O –m py_compile hello.py

【代码说明】

·参数“-O”表示生成优化代码。

·参数“-m”表示把导入的py_compile模块作为脚本运行。编译hello.pyo需要调用py_compile模块的compile()方法。

·参数“hello.py”是待编译的文件名。

最后,查看hello.py文件所在的目录,此时目录中生成了一个名为hello.pyo的文件。

2.2 Python的编码规范

Python语言有自己独特的编码规则,包括命名规则、代码书写规则等。本节将详细介绍Python中常用的规则,并解释这些规则的原理和由来。

2.2.1 命名规则

Python语言有一套自己的命名规则,用户也可以借鉴Java语言的命名规则形成自己命名的规则。命名规则并不是规定,只是一种习惯用法。下面介绍几个常见规范。

1.变量名、包名、模块名

变量名、包名、模块名通常采用小写,可使用下划线,示例如下。


01    # 变量、模块名的命名规则
02      # Filename: ruleModule.py
03      
04      _rule = "rule information"

【代码说明】

·第2行代码声明模块的名称,模块名采用小写。也可以不指定模块名,以py后缀的文件就是一个模块。模块名就是文件名。

·第4行代码定义了一个全局变量_rule。

2.类名、对象名

类名首字母采用大写,对象名采用小写。类的属性和方法名以对象作为前缀。类的私有变量、私有方法以两个下划线作为前缀。下面这段代码演示了类的定义和实例化的规范写法。


01       class Student:                         # 类名大写
02           __name = ""                        # 私有实例变量前必须有两个下划线
03           def __init__(self, name):
04               self.__name = name             # self相当于Java中的this
05           def getName(self):                 # 方法名首字母小写,其后每个单词的首字母大写
06               return self.__name
07      
08      if __name__ == "__main__":
09           student = Student("borphi")        # 对象名小写
10           print(student.getName())

【代码说明】

·第1行代码定义了一个名为Student的类,类名首字母大写。

·第2行代码定义了一个私有的实例变量,变量名前有两个下划线。

·第4行代码使用self前缀说明__name变量属于Student类。

·第5行代码定义了一个公有的方法,方法名首字母小写,其后的单词Name首字母大写。函数的命名规则和方法名相同。

·第9行代码创建了一个student对象,对象名小写。

说明 关于面向对象的知识会在第9章详细介绍,这里读者只需要知道类、对象、属性以及方法的书写方式即可。

3.函数名

函数名通常采用小写,并用下划线或单词首字母大写增加名称的可读性,导入的函数以模块名作前缀。下例中,为了演示导入函数前缀写法,使用了生成随机数的模块random。该模块有一个函数randrange()。该函数可以根据给定的数字范围生成随机数。randrange()声明如下所示:


randrange(start, stop[, step])

【代码说明】

·参数start表示生成随机数所在范围的开始数字。

·参数stop表示生成随机数所在范围的结束数字,但不包括数字stop。

·参数step表示从start开始往后的步数。生成的随机数在[start,stop-1]的范围内,取值等于start+step。

例如:


randrange(1, 9, 2)

随机数的范围在1、3、5、7之间选取。下面这段代码演示了函数的规范写法,其中定义了一个compareNum(),该函数用于比较两个数字的大小,并返回对应的结果。


01      # 函数中的命名规则
02      import random
03      
04      def compareNum(num1, num2):
05          if(num1 > num2):
06              return 1
07          elif(num1 == num2):
08              return 0
09          else:
10              return -1
11      num1 = random.randrange(1, 9)
12      num2 = random.randrange(1, 9)
13      print( "num1 =", num1)
14      print ("num2 =", num2)
15      print (compareNum(num1, num2))

【代码说明】

·第2行代码导入了random模块。

·第4行代码定义了一个函数compareNum(),参数num1、num2为待比较的两个变量。

·第5行到第10行代码比较两个数的大小,返回不同的结果。

·第11、12行代码调用random模块的randrange()函数,返回两个随机数。

·第13、14行代码输出随机数,不同的机器、不同的执行时间得到的随机数均不相同。

·第15行代码调用compareNum(),并把产生的两个随机数作为参数传入。

良好命名可以提高编程效率,可以使代码阅读者在不了解文档的情况下,也能理解代码的内容。下面以变量的命名为例说明如何定义有价值的名称。许多程序员对变量的命名带有随意性,如使用i、j、k等单个字母。代码阅读者并不知道这些变量的真实含义,需要阅读文档或仔细查看源代码才能了解其含义。下面是一个命名不规范的例子。


01      # 不规范的变量命名
02      sum = 0
03      i = 2000
04      j = 1200
05      sum = i + 12 * j

【代码说明】这段代码定义了一个求和变量sum,以及两个变量i、j。如果只看代码片段,并不知道运算的含义是什么,需要通读整个函数或功能模块才能理解此处表达式的含义。

下面是一个良好命名的例子。


01      # 规范的变量命名
02      sumPay = 0
03      bonusOfYear = 2000
04      monthPay = 1200
05      sumPay = bonusOfYear + 12 * monthPay

【代码说明】bonusOfYear表示年终奖金、monthPay表示月薪,因此sumPay表示全年的薪水。命名良好的变量可以节省阅读程序的时间,更快地理解程序的含义。

注意 变量的命名应尽可能地表达此变量的作用,尽量避免使用缩写,以至于任何人都能理解变量名的含义。不用担心变量名的长度,长的变量名往往能更清楚地表达意思。

以上讨论的命名方式同样适用于模块名、类名、方法名、属性名等。命名规则会带来很多益处。统一命名规则便于开发团队合作开发同一个项目;便于统一代码的风格,理解不同程序员编写的代码;命名规范的变量名使函数的内容更容易被理解;避免项目中随意命名变量的情况,促进程序员之间的交流。规则并不是绝对的,统一规则、表达清楚名称的含义才是制定规则的原因。

2.2.2 代码缩进与冒号

代码缩进是指通过在每行代码前输入空格或制表符的方式,表示每行代码之间的层次关系。任何编程语言都需要用代码缩进清晰程序的结构,采用代码缩进的编程风格有利于代码的阅读和理解。对于C、C++、Java等语言,代码缩进只是作为编程的一种良好习惯而延承下来。对于Python而言,代码缩进是一种语法,Python语言中没有采用花括号或begin…end…分隔代码块,而是使用冒号和代码缩进区分代码之间的层次。

使用IDE开发工具或EditPlus等编辑器书写代码时,编辑器会自动缩进代码、补齐冒号,提高编码效率。下面这段代码中的条件语句采用了代码缩进的语法。


01      x = 1
02      if x == 1:
03          print( "x =", x)                    # 代码缩进
04      else:
05          print( "x =", x)                    # 代码缩进
06          x = x + 1                           # 代码缩进
07      print ("x =", x)

【代码说明】

·第1行代码创建了变量x,并赋值为1。在赋值运算符的两侧各添加一个空格,这是一种良好的书写习惯,提高了程序的可读性。

·第2行代码使用了条件语句if,判断x的值是否等于1。if表达式后输入了一个冒号,冒号后面的代码块需要缩进编写。本行代码与第1行代码处于同一个层次,直接从最左端书写代码。

·第3行代码表示x的值等于1时输出的结果。当if条件成立时,程序才能执行到第3行,所以第3行代码位于第2行代码的下一个层次。在编码时,首先在最左端输入4个空格或制表键(不建议),然后再书写print语句。输出结果:


x = 1

·第4行代码的else关键字后是一段新的代码块。当x的值不等于1时,程序将执行第5、6行代码。

·第5、6行代码采用缩进式的代码风格。

·第7行代码输出结果:


x = 1

Python对代码缩进要求非常严格。如果程序中不采用代码缩进的编码风格,将抛出一个IndentationError。下面这段代码是错误的编码方式。


01      x = 0
02      if x == 1:
03      print( "x =", x)
04      else:
05          print( "x =", x)            # 代码缩进
06          x = x + 1                   # 代码缩进
07      print ("x =", x)

【代码说明】第3行没有缩进代码,python不能识别出代码的层次关系,python误认为if x==1:语句后没有代码块。代码运行后输出如下错误:


IndentationError: expected an indented block

注意 如果缩进的代码前只有一个空格或几个制表符也是符合语法的,但是这种写法并不推荐。最佳的方式是编码前统一代码的书写规则,所有代码前的空格数保持一致,最好使用4个空格缩进。

每行代码缩进的情况不同,代码执行的结果也不同。例如,前文提到的hello.py,else子句包含的两条语句都采用了代码缩进的格式,这两条语句组成了一个代码块。如果把hello.py的代码修改为如下的内容,则变量x的值将有所变化。


01      x = 1
02      if x == 1:
03          print( "x =", x)            # 代码缩进
04      else:
05          print( "x =", x)            # 代码缩进
06      x = x + 1
07      print( "x =", x)

【代码说明】

·第6行代码与第1行代码处于同一个层次,是主程序必须执行的代码,因此变量x的值为2。

·第7行代码的输出结果如下。


x = 2

注意 当程序出现问题时,程序员首先要检查代码的书写格式,是否因为代码缩进的问题导致了不期望的计算结果。

2.2.3 模块导入的规范

模块是类或函数的集合,用于处理一类问题。模块的导入和Java中包的导入的概念很相似,都使用import语句。在Python中,如果需要在程序中调用标准库或其他第三方库的类,需要先使用import或from…import…语句导入相关的模块。

1.import语句

下面这段代码使用import语句导入sys模块,并打印相关内容。


01      # 规范导入方式
02      import sys
03      
04      print (sys.path)
05      print (sys.argv)

【代码说明】

·第2行代码使用import语句导入了sys模块,sys模块是处理系统环境的函数的集合。

·第4行代码输出Python环境下的查找路径的集合,Python默认情况下会查找sys.path返回的目录列表。列表是Python内置的数据结构,定义了一组相同意义的数据,通常用来作为参数或返回值。关于列表的知识请参考第4章的内容。本行代码的输出结果如下。


['D:\\developer\\python\\example\\02\\2.2\\2.2.3','C:\\WINDOWS\\system32\\python25.zip',
'D:\\developer\\python\\DLLs', 'D:\\developer\\python\\lib',
'D:\\developer\\python\\lib\\plat-win',
'D:\\developer\\python\\lib\\lib-tk','D:\\developer\\python',
'D:\\developer\\python\\lib\\site-packages','D:\\developer\\python\\lib\\site-packages\\win32',
'D:\\developer\\python\\lib\\site-packages\\win32\\lib',
'D:\\developer\\python\\lib\\site-packages\\Pythonwin',
'D:\\developer\\python\\lib\\site-packages\\wx-2.8-msw-unicode']

·第5行代码中的sys.argv是存储输入参数的列表。默认情况下,argv自带的参数是文件名。输出结果如下。


['import.py']

2.from…import…语句

使用from…import…语句导入与使用import语句导入有所不同,区别是前者只导入模块中的一部分内容,并在当前的命名空间中创建导入对象的引用;而后者在当前程序的命名空间中创建导入模块的引用,从而可以使用“sys.path”的方式调用sys模块中的内容。

下面这段代码实现了与上面代码相同的功能,不同的是使用了from…import…语句导入指定内容。


01      #不规范导入方式
02      from sys import path
03      from sys import argv
04      
05      print (path)
06      print (argv)

【代码说明】第5、6行代码直接调用path、argv列表的内容,没有模块名的限定,但这种写法不够规范。如果程序比较复杂,导入了很多模块,阅读程序的人并不了解path、argv来自哪个模块。而通过sys.path、sys.argv的写法可以清楚地知道path、argv来自sys模块。

2.2.4 使用空行分隔代码

函数之间或类的方法之间用空行分隔,表示一段新的代码的开始。类和函数入口之间也用一行空行分隔,突出函数入口的开始。下面这段代码创建了一个类A,类A中定义了两个方法funX()和funY()。


01      class A:
02          def funX(self):
03              print( "funY()")
04      
05          def funY(self):
06              print ("funY()")
07      
08      if __name__ == "__main__":
09          a = A()
10          a.funX()
11          a.funY()

【代码说明】

·第4行代码插入了一个空行,便于程序员阅读代码,表示区分方法之间的间隔。

·第7行代码也是一个空行。因为下面的if语句是主程序的入口,用于创建类A的对象,并调用其方法。

空行与代码缩进不同,空行并不是Python语法的一部分。书写时不插入空行,Python解释器运行也不会出错。但是空行的作用在于分隔了两段不同功能或含义的代码,便于日后代码的维护或重构。记住,空行也是程序代码的一部分。

2.2.5 正确的注释

注释是用于说明代码实现的功能、采用的算法、代码的编写者以及代码创建和修改的时间等信息。注释是代码的一部分,注释起到了对代码补充说明的作用。C、C++、Java均采用“//”或“/**/”作为注释的标记,Python的注释方式有所不同。如果只对一行代码注释,使用“#”加若干空格开始,后面是注释的内容。如果对一段代码进行注释,也使用“#”,段落之间以一个“#”行分隔。Python会忽略“#”行的内容,跳过“#”行执行后面的内容。下面的代码演示了Python的注释。


01      # note - show Python's note
02      # Copyright (C) 2008 bigmarten
03      #
04      # This program is free software. you can redistribute it and/or modify
05      # it under the terms of the GNU General Public License as published by
06      # the Free Software Foundation
07      #
08      ########################################################################
09      #
10      # Version is 1.0
11      #
12      # Its contents are calculate payment
13      #
14      ########################################################################
15      
16      # 规范的变量命名
17      sumPay = 0                                   # 年薪
18      bonusOfYear = 2000                           # 年终奖金
19      monthPay = 1200                              # 月薪
20      sumPay = bonusOfYear + 12 * monthPay         # 年薪 = 年终奖金 + 12 * 月薪

【代码说明】

·第1行代码对本文件进行摘要说明。

·第2行代码声明了版权信息。

·第3行代码是空行,说明下面将另起一段,注释其他的内容。

·第4行到第6行代码说明程序的许可信息。

·第8行到第14行代码说明程序的版本和实现的功能。

·第16行开始的代码实现程序的功能,并在行末对每行代码进行单行注释。

Python还有一些特殊的注释,完成一些特别的功能,如中文注释、程序的跨平台。

(1)中文注释:如果需要在代码中使用中文注释,必须在Python文件的最前面加上如下注释说明。


# -*- coding: UTF-8 -*-

注意 Python3中默认的编码是Unicode,所以不需在每个Python文件中再加以上注释,但在Python2中若使用中文则必须加上。

(2)跨平台注释:如果需要使Python程序运行在*nix系统中,最好在Python文件的最前面加上如下注释说明。


#!/usr/bin/python

此外,注释也用于调试程序。由于文本编辑器不具备调试功能,因此,可以使用注释辅助调试。如果一个程序很长,可以把程序分成若干个部分编写。为了更好地调试目前正在编写的程序模块,可以将那些已经编译通过的部分注释掉;或者把多余代码注释掉,把主要精力集中在当前编写的逻辑上。

例如,编写一个比较两个数字大小的函数。


01      def compareNum(num1, num2):
02          if(num1 > num2):
03              return str(num1)+" > "+str(num2)
04          elif(num1 < num2):
05              return str(num1)+" = "+str(num2)
06          elif(num1 == num2):
07              return str(num1)+" = "+str(num2)
08          else:
09              return ""

【代码说明】本段代码中的str()函数实现了数字类型到字符串类型的转换。

编译后显示如下内容:


---------- python ----------
输出完成 (耗时: 1 秒) - 正常终止

说明这个函数编译通过,至少在语法上没有任何错误。下面就可以编写这个函数的调用程序,证明这个程序的逻辑是否正确。在前面的代码段后面添加如下代码。


01      num1 = 2
02      num2 = 1
03      print (compareNum(num1, num2))
04      num1 = 2
05      num2 = 2
06      print (compareNum(num1, num2))
07      num1 = 1
08      num2 = 2
09      print (compareNum(num1, num2))

运行程序,发现第3行的输出结果有误。


2 > 1
2 = 2
1 = 2

第1行和第2行的输出证明函数compareNum()中num1>num2和num1==num2的判断是正确的,于是想到程序可能是在num1<num2的条件判断的逻辑上出现了错误。为了证明这个观点,注释掉num1<num2的分支判断语句。注释后的compareNum()的代码如下。


01       def compareNum(num1, num2):
02          if(num1 > num2):
03              return str(num1)+" > "+str(num2)
04          #elif(num1 < num2):
05          #    return str(num1)+" = "+str(num2)
06          elif(num1 == num2):
07              return str(num1)+" = "+str(num2)
08          else:
09              return ""

此时,程序编译通过,证明逻辑错误就在num1<num2的分支语句中。仔细检查注释的语句,发现return语句的返回值表达式写错了,其中的“>”误写为“=”。compareNum()正确的写法如下所示。


01      def compareNum(num1, num2):
02          if(num1 > num2):
03              return str(num1)+" > "+str(num2)
04          elif(num1 < num2):
05              return str(num1)+" < "+str(num2)
06          elif(num1 == num2):
07              return str(num1)+" = "+str(num2)
08          else:
09              return ""

再次运行整个程序,输出结果正确显示。


2 > 1
2 = 2
1 < 2

合理地使用注释可以检查程序中的错误。这段代码演示了注释问题语句的做法,并检查这些语句错误的过程。另一种用法是,注释正确语句,单独运行问题语句,检查语句错误。这两种用法可以根据实际情况分别应用,后者更适合于代码比较长的情况。注释对于程序非常重要,表2-1说明了注释的用法和作用。

表2-1 注释的用法

2.2.6 语句的分隔

分号是C、Java等语言中标识语句结束的标志。Python也支持分号,同样可以用分号作为一行语句的结束标识。但在Python中分号的作用已经不像在C、Java中那么重要了,在C、Java中分号是必需的;而Python中的分号可以省略,主要通过换行来识别语句的结束。例如,以下两行代码是等价的。


01      # 下面两条语句是等价的
02      print( "hello world!")
03      print( "hello world!");

【代码说明】

·第1行代码的输出结果:


hello world!

·第2行代码的输出结果:


hello world!

如果要在一行中书写多个语句,就必须使用分号分隔了,否则Python无法识别语句之间的间隔。


01      # 使用分号分隔语句
02      x = 1; y = 1 ; z = 1

【代码说明】第2行代码中有3条赋值语句,语句之间需要用分号隔开。如果不隔开语句,则Python解释器不能正确解释,会提示语法错误。


SyntaxError: invalid syntax

注意 分号并不是Python推荐使用的符号,Python倾向于使用换行作为每条语句的分隔。简单直白是Python语法的特点,通常一行只写一条语句,这样便于阅读和理解程序。一行写多条语句的方式是不好的实践。

Python同样支持多行写一条语句,Python使用“\”作为换行符。在实践中,一条语句写在多行也是很常见的。例如,把SQL语句作为参数传递给函数,由于SQL非常长,因此需要换行书写,提高阅读的方便性。


01      # 字符串的换行
02      # 写法一
03      sql = "select id,name \
04      from dept \
05      where name = 'A'"
06      print (sql)
07      # 写法二
08      sql = "select id,name " \
09            "from dept " \
10            "where name = 'A'"
11      print (sql)

【代码说明】

·写法一只使用了一对双引号,把SQL语句分为select、from、where 3部分分别书写。

·第6行代码输出结果如下。


select id,name from dept where name = 'A'

·写法二使用了3对双引号,select、from、where分别对应一对双引号。

·第11行代码输出结果如下。


select id,name from dept where name = 'A'

第二种写法比第一种写法的可读性更强,可以使用空格和制表符对齐语句,使代码显得更工整。对于简短的语句不推荐使用换行的写法,这种写法只会造成阅读的复杂性。下面这段程序是不合理的换行写法。


01      # 一条语句写在多行
02      print (\
03      "hello world!")

【代码说明】第2、3行代码是一个整体,调用print输出“hello world!”,这种情况不适合分行书写。

2.3 变量和常量

变量是计算机内存中的一块区域,变量可以存储任何值,而且值可以改变。常量是一块只读的内存区域,常量一旦初始化就不能修改。

2.3.1 变量的命名

变量由字母、数字或下划线组成。变量的第1个字符必须是字母或下划线,其他字符可以由字母、数字或下划线组成。例如:


01      # 正确的变量命名
02      var_1 = 1
03      print (var_1)
04      _var1 = 2
05      print( _var1)

【代码说明】

·第2行代码定义了一个名为var_1的变量,该变量的初始值为1。这个变量以字母开头,后面的字符由字母、下划线和数字组成。

·第3行代码输出结果如下。


1

·第4行代码定义了一个名为_var1的变量,该变量的初始值为2。这个变量以下划线开头,后面的字符由字母和数字组成。

·第5行代码输出结果如下。


2

下面这段代码演示了错误的变量命名方式。


01      # 错误的变量命名
02      1_var = 3
03      print (1_var)
04      $var = 4
05      print ($var)

【代码说明】

·第2行代码定义了一个名为1_var的变量,该变量以数字开头,后面的字符由字母、下划线组成。

·第3行代码,变量以数字开头,不符合变量命名的规则。提示如下错误:


SyntaxError: invalid syntax

·第4行代码定义了一个名为$var的变量,该变量以$符号开头。

·第5行代码,变量以$符号开头,不符合变量命名的规则。提示如下错误:


SyntaxError: invalid syntax

2.3.2 变量的赋值

Python中的变量不需要声明,变量的赋值操作即是变量声明和定义的过程。每个变量在内存中创建,都包括变量的标识、名称和数据这些信息。例如:


x = 1

上面的代码创建了一个变量x,并且赋值为1,如图2-1所示。

图2-1 变量的内部结构

Python中一次新的赋值,将创建一个新的变量。即使变量的名称相同,变量的标识并不相同。下面的代码演示了Python的变量声明以及赋值操作。


01      # 一次新的赋值操作,将创建一个新的变量
02      x = 1   
03      print (id(x))
04      x = 2
05      print( id(x))

【代码说明】

·第2行代码定义了一个名为x的变量,该变量的初始值为1。

·第3行代码,输出变量x的标识。输出结果如下。


11229424

·第4行代码再次定义了一个x的变量,该变量的初始值为2。该变量与前面的变量x并不是同一变量。

·第5行代码,输出变量x的标识。输出结果如下。


11229412

如果变量没有赋值,Python将认为该变量不存在。例如:


print y

运行后,解释器提示:


NameError: name 'y' is not defined

在变量y没有赋值的前提下,不能直接输出y的值。每个变量在使用前都必须赋值,这样可以避免由于变量的空值引起的一些异常。Python支持对一些变量同时赋值的操作,例如:


01      # 给多个变量赋值
02      a = (1, 2, 3)
03      (x, y, z) = a
04      print( "x =", x)
05      print( "y =", y)
06      print( "z =", z)

【代码说明】

·第2行代码定义了一个序列a,这个序列有3个值:1、2、3。

·第3行代码,把序列a的值分别赋值给序列(x,y,z)中的变量x、y、z。

·第4行代码输出变量x的值。输出结果:


x = 1

·第5行代码输出变量y的值。输出结果:


y = 2

·第6行代码输出变量z的值。输出结果:


z = 3

通过序列的装包和拆包操作,实现了同时给多个变量赋值。关于序列的概念参见第4章的内容。

2.3.3 局部变量

局部变量是只能在函数或代码段内使用的变量。函数或代码段一旦结束,局部变量的生命周期也就结束。局部变量的作用范围只在其被创建的函数内有效。例如,文件1的fun()中定义了一个局部变量,则该局部变量只能被fun()访问,而不能被fun2()访问,也不能被文件2访问,如图2-2所示。

下面定义了一个函数fun(),该函数中定义了一个局部变量。


01      # 局部变量
02      def fun():
03          local = 1
04          print(local)
05      fun()

【代码说明】

·第2行代码定义了一个函数fun()。

·第3行代码定义了一个局部变量local。

·第4行代码输出local的值。输出结果如下。


1

·第5行代码调用函数fun()。此时已超出local变量的作用范围。

注意 Python创建的变量就是一个对象,Python会管理变量的生命周期。Python对变量的回收采用的是垃圾回收机制。

2.3.4 全局变量

全局变量是能够被不同的函数、类或文件共享的变量,在函数之外定义的变量都可以称为全局变量。全局变量可以被文件内部的任何函数和外部文件访问。例如,如果文件1中定义了一个全局变量,文件1中的函数fun()可以访问该全局变量。此外,该全局变量也能被文件1、文件2访问,如图2-3所示。

图2-2 局部变量的作用范围

图2-3 全局变量的作用范围

全局变量通常在文件的开始处定义。下面定义了两个全局变量_a、_b和两个函数add()、sub(),这两个函数将调用全局变量执行加法和减法计算。


01      # 在文件的开头定义全局变量
02      _a = 1
03      _b = 2
04      def add():
05          global _a
06          _a = 3
07          return "_a + _b =", _a + _b
08      def sub():
09          global  _b
10          _b = 4
11          return "_a - _b =", _a - _b
12      print (add())
13      print( sub())

【代码说明】

·第2行代码定义了一个名为_a的全局变量,这个变量的作用范围从定义处到文件的结尾。之所以使用下划线是为了区分于其他变量,引起程序员对全局变量出现的重视。

·第3行代码定义了一个名为_b的全局变量。同样,变量_b的作用范围从定义处到文件的结尾。

·第4行代码定义了一个函数add(),用于执行加法计算。

·第5行代码引用全局变量_a。这里使用了global关键字,global用于引用全局变量。

·第6行代码对全局变量_a重新赋值。

·第7行代码返回_a+_b的值。

·第8行代码定义了一个函数sub(),用于执行减法运算。函数内的实现方式和add()相同。

·第12代码调用函数add()。输出结果如下。


('_a + _b =', 5)

·第13行代码调用函数sub()。输出结果如下。


('_a - _b =', -1)

如果不使用global关键字引用全局变量,而直接对_a、_b赋值,将得到不正确的结果。


01      # 错误地使用全局变量
02      _a = 1
03      _b = 2
04      def add():
05          _a = 3
06          return "_a + _b =", _a + _b
07      def sub():
08          _b = 4
09          return "_a - _b =", _a - _b
10      print (add())
11      print (sub())

【代码说明】

·第5行代码中的_a并不是前面定义的全局变量,而是函数add()中的局部变量。虽然输出的结果相同,但是运算的对象并不相同。

·第6行代码中的_b还是前面定义的全局变量_b。

·第8行代码中的_b是局部变量。

·第10行代码的输出结果如下。


('_a + _b =', 5)

·第11行代码的输出结果如下。


('_a - _b =', -3)

注意 变量名相同的两个变量可能并不是同一个变量,变量的名称只是起标识的作用。变量出现的位置不同,变量的含义也不同。

同样可以把全局变量放到一个专门的文件中,便于统一管理和修改。创建一个名为gl.py的文件。


01      # 全局变量
02      _a = 1
03      _b = 2

【代码说明】pl.py创建了两个全局变量_a和_b。

再创建一个调用全局变量的文件use_global.py。


01      # 调用全局变量
02      import gl
03      def fun():
04          print(gl._a)
05          print(gl._b)
06      fun()

【代码说明】

·第2行代码导入前面创建的文件gl.py,即模块gl。

·第3行代码定义了一个函数fun(),该函数调用全局变量_a和_b。这里不需要使用global引用gl.py中的全局变量,因为前导符可以定位全局变量_a和_b。

·第4行代码输出_a的值,使用前导符gl定位。输出结果:


1

·第5行代码输出_b的值,使用前导符gl定位。输出结果:


2

·第6行代码调用fun()。

应该尽量避免使用全局变量。因为不同的模块都可以自由地访问全局变量,可能会导致全局变量的不可预知性。对于gl.py中的全局变量,如果程序员甲修改了_a的值,程序员乙同时也要使用_a,这时可能导致程序中的错误。这种错误是很难发现和更正的。

全局变量降低了函数或模块之间的通用性,不同的函数或模块都要依赖于全局变量。同样,全局变量降低了代码的可读性,阅读者可能并不知道调用的某个变量是全局变量。

2.3.5 常量

常量是指一旦初始化后就不能改变的变量。例如,数字5、字符串“abc”都是常量。C++中使用const关键字指定常量,Java使用static和final关键字指定常量,而Python并没有提供定义常量的关键字。Python是一门功能强大的语言,可以自己定义一个常量类来实现常量的功能。在《Python Cookbook》一书中定义了一个常量模块const。


01    class _const:                                 # 定义常量类_const
02        class ConstError(TypeError): pass         # 继承自TypeError
03        def __setattr__(self,name,value):
04            if self.__dict__.has_key(name):       # 如果__dict__中不包含对应的key则抛出错误
05                raise self.ConstError, "Can't rebind const(%s)"%name
06            self.__dict__[name]=value
07    import sys
08    sys.modules[__name__]=_const()                # 将const注册进sys.modules的全局dict中

【代码说明】

·这个类定义了一个方法__setattr__()和一个异常类型ConstError,ConstError类继承自TypeError。通过调用类自带的字典__dict__,判断定义的常量是否包含在字典中。如果字典中包含此常量,将抛出异常。否则,给新创建的常量赋值。

·最后两行代码的作用是把const类注册到sys.modules这个全局字典中。

以下代码在use_const.py中调用const,定义常量。


01      import const
02      const.magic = 23
03      const.magic = 33

【代码说明】

·第1行代码导入const模块。

·第2行代码定义了一个常量magic。

·第3行代码修改常量magic的值,抛出异常。


const.ConstError: Can't rebind const(magic)

2.4 数据类型

数据类型是构成编程语言语法的基础。不同的编程语言有不同的数据类型,但都具有常用的几种数据类型。Python有几种内置的数据类型——数字、字符串、元组、列表和字典。本节将重点介绍数字和字符串。

2.4.1 数字

Python3的数字类型分为整型、浮点型、布尔型、分数类型、复数类型。使用Python编写程序时,不需要声明变量的类型。由Python内置的基本数据类型来管理变量,在程序的后台实现数值与类型的关联,以及类型转换等操作。Python与其他高级语言定义变量的方式及内部原理有很大的不同。在C或Java中,定义一个整型的变量,可以采用如下方式表示:


int i = 1;

在Python中,定义整型变量的表达方式更简练。


i = 1

Python根据变量的值自动判断变量的类型,程序员不需要关心变量究竟是什么类型,只要知道创建的变量中存放了一个数,以后的工作只是对这个数值进行操作,Python会对这个数的生命周期负责。

更重要的一点是,C或Java只是创建了一个int型的普通变量;而Python创建的是一个整型对象,并且Python自动完成了整型对象的创建工作,不再需要通过构造函数创建。Python内部没有普通类型,任何类型都是对象。如果C或Java需要修改变量i的值,只要重新赋值即可;而Python并不能修改对象i的值。例如:


01      #下面的两个i并不是同一个对象
02      i = 1
03      print(id(i))
04      i = 2
05      print (id(i))

如果需要查看变量的类型,可以使用Python定义的type类。type是__builtin__模块的一个类,该类能返回变量的类型或创建一个新的类型。__builtin__模块是Python的内联模块,内联模块不需要import语句,由Python解释器自动导入。后面还会接触到更多内联模块的类和函数。

下面这段代码返回了各种变量的类型。


01      #整型
02      i = 1
03      print( type(i))
04      #长整型
05      l = 999999999999999999990       # 什么时候python将int转为float跟操作系统位数相关
06      print type(l)
07      #浮点型
08      f = 1.2
09      print( type(f))
10      #布尔型
11      b = True
12      print (type(b))

【代码说明】

·第3行代码输出结果:

·第6行代码输出结果:

·第9行代码输出结果:

·第12行代码输出结果:

用Python来进行科学计算也很方便,因为Python内置了复数类型。Java、C#等高级语言则没有提供复数类型。


01      #复数类型
02      c = 7 + 8j
03      print (type(c))

第3行代码输出结果:<class'complex'>

注意 复数类型的写法与数学中的写法相同,如果写为c=7+8i,Python不能识别其中的“i”,将提示语法错误。

2.4.2 字符串

在Python中有3种表示字符串的方式——单引号、双引号、三引号。单引号和双引号的作用是一样的,对于不同的程序员可以根据自己的习惯使用单引号或双引号。PHP程序员可能更习惯使用单引号表示字符串,C、Java程序员则习惯使用双引号表示字符串。下面这段代码中单引号和双引号的使用是等价的。


01      # 单引号和双引号的使用是等价的
02      str = "hello world!"
03      print (str)
04      str = 'hello world!'
05      print (str)

【代码说明】第3行代码输出结果:


hello world! 

第5行代码输出结果:


hello world! 

三引号的用法是Python特别的语法,三引号中可以输入单引号、双引号或换行等字符。


01      # 三引号的用法
02      str = '''he say "hello world!"'''
03      print( str)

【代码说明】第3行代码的三引号中带有双引号,双引号也会被输出。输出结果:


he say "hello world!"

三引号的另一种用法是制作文档字符串。Python的每个对象都有一个属性__doc__,这个属性用于描述该对象的作用。


01      # 三引号制作doc文档
02      class Hello:
03          '''hello class'''
04          def printHello():
05              '''print hello world'''
06              print ("hello world!")
07      print( Hello.__doc__)
08      print (Hello.printHello.__doc__)

【代码说明】

·第2行代码定义了一个名为Hello的类。

·第3行是对Hello类的描述,该字符串将被存放在类的__doc__属性中。

·第4行代码定义了一个方法printHello()。

·第5行代码描述了printHello(),并把字符串存放在该函数的__doc__属性中。

·第6行代码输出结果:


hello world!

·第7行代码输出Hello的__doc__属性的内容。输出结果:


hello class

·第8行代码输出printHello()的__doc__属性的内容。输出结果:


print hello world

如果要输出含有特殊字符(单引号、双引号等)的字符串,需要使用转义字符。Python中转义字符为“\”,和C、Java中的转义字符相同。转义操作只要在特殊字符的前面加上“\”即可。下面这段代码说明了特殊字符的转义用法。


01      # 转义字符
02      str = 'he say:\'hello world!\''
03      print (str)

【代码说明】第2行代码中的单引号是特殊字符,需要在“'”前加上转义字符。第3行代码的输出结果:


he say:'hello world!'

使用双引号或三引号可以直接输出含有特殊字符的字符串,不需要使用转义字符。


01      # 直接输出特殊字符
02      str = "he say:'hello world!'"
03      print (str)
04      str = '''he say:'hello world!' '''
05      print (str)

【代码说明】

·第2行代码中使用了双引号表示字符串变量str,因此Python能够识别出双引号内部的单引号只是作为输出的字符。

·第3行代码的输出结果:


he say:'hello world!'

·第4行代码使用三引号表示字符串变量str,注意最后一个单引号后面留有一个空格,这个空格是为了让Python识别出三引号留下的。如果不留下这个空格,4个单引号连在一起,Python解释器不能正确识别三引号。提示如下错误:


SyntaxError: EOL while scanning single-quoted string

·第5行代码的输出结果:


he say:'hello world!'

注意 输出的字符串中含有单引号,使用双引号表示字符串即可。相反,输出的字符串中含有双引号时,可使用单引号表示字符串。

2.5 运算符与表达式

Python的运算符号包括算术运算符、关系运算符和逻辑运算符。表达式是由数字或字符串和运算符组成的式子。表达式通常用于判断语句和循环语句的条件使用,表达式是学习控制语句编写的基础。本节将介绍Python中的各种表达式的使用。

2.5.1 算术运算符和算术表达式

算术运算符包括四则运算符、求模运算符和求幂运算符。Python中的算术运算符和表达式如表2-2所示。

表2-2 Python中的算术运算符和表达式

注意 与C、Java语言不同,Python不支持自增运算符和自减运算符。例如,i++、i--是错误的语法。

下面这段代码演示了Python中算术运算符的使用方法。


01      print( "1 + 1 =", 1 + 1)
02      print ("2 - 1 =", 2 - 1)
03      print( "2 * 3 =", 2 * 3)
04      print ("4 / 2 =", 4 / 2)
05      print( "1 / 2 =", 1 / 2)
06      print ("1 / 2 =", 1.0 / 2.0)
07      print ("3 % 2 =", 3 % 2)
08      print ("2 ** 3 =", 2 ** 3)

【代码说明】

·第1行代码的输出结果:1+1=2

·第2行代码的输出结果:2-1=1

·第3行代码的输出结果:2*3=6

·第4行代码的输出结果:4/2=2

·第5行代码的输出结果:1/2=0.5

·第6行代码中的被除数是1.0,除数是2.0。Python把这两个数作为浮点型处理,因此相除后可以得到正确的结果。输出结果:1.0/2.0=0.5

·第7行代码中,求模的值为3除以2后的余数。输出结果:3%2=1

·第8行代码的输出结果:2**3=8

注意 Python2中执行“1/2”算术表达式的结果略有不同,Python2认为1和2是整型,相除后的结果会被截断,因此得到的值为0。

Python的算术表达式具有结合性和优先性。结合性是指表达式按照从左往右、先乘除后加减的原则。即从表达式的左边开始计算,先执行乘法和除法运算,再执行加法和减法运算。例如:


a + b * c % d

以上表达式先执行b*c,然后执行b*c%d,最后执行加法运算。

优先性是指先执行圆括号内的表达式,再按照结合性的原则进行计算。例如:


(a + b) * (c % d)

以上表达式先计算a+b的值,然后计算c%d的值,最后把两个值相乘。下面这段代码演示了算术运算的优先级。


01      # 算术运算的优先级
02      a = 1
03      b = 2
04      c = 3
05      d = 4
06      print ("a + b * c % d =", a + b * c % d) 
07      print ("(a + b) * (c % d) =", (a + b) * (c % d))

第6行代码的输出结果:


a + b * c % d = 3

第7行代码的输出结果:


(a + b) * (c % d) = 9

2.5.2 关系运算符和关系表达式

关系运算符即对两个对象进行比较的符号。Python中的关系运算符和表达式如表2-3所示。

表2-3 Python中的关系运算符和表达式

下面这段代码演示了关系表达式的逻辑输出。


01      # 关系表达式
02      print (2 > 1)
03      print (1 <= 2)
04      print (1 == 2)
05      print (1 != 2)

【代码说明】

·第2行代码,2>1的逻辑正确。输出结果:True

·第3行代码,1<=2的逻辑正确。输出结果:True

·第4行代码,1==2的逻辑错误。输出结果:False

·第5行代码,1!=2的逻辑正确。输出结果:True

不同的关系运算符优先级别不同。其中<、<=、>、>=4个运算符的优先级别相等,==、!=的优先级别相等。而<、<=、>、>=的优先级别大于==、!=的优先级别。例如:a>=b==c等价于(a>=b)==c。

关系运算符的优先级低于算术运算符。下面的代码演示了关系运算符的优先级别。


01      # 关系表达式的优先级别
02      print( "1 + 2 < 3 - 1 =>", 1 + 2, "<", 3 - 1, "=>", 1 + 2 < 3 - 1)
03      print ("1 + 2 <= 3 > 5 % 2 =>", 1 + 2, "<=", 3, ">", 5 % 2, "=>", 1 + 2 <= 3 > 5 % 2)

【代码说明】

·第2行代码,先执行1+2=3,然后执行3–1=2,最后比较3<2。输出结果:


1 + 2 < 3 - 1 => 3 < 2 => False

·第3行代码,先执行1+2=3,然后执行5%2=1,最后比较3<=3>1。输出结果:


1 + 2 <= 3 > 5 % 2 => 3 <= 3 > 1 => True

2.5.3 逻辑运算符和逻辑表达式

逻辑表达式是用逻辑运算符和变量连接起来的式子。任何语言的逻辑运算符都只有3种——逻辑与、逻辑或和逻辑非。C、Java语言的逻辑运算符用&&、||、!表示,Python采用and、or、not表示。表2-4列出了Python中的逻辑运算符和表达式。

表2-4 Python中的逻辑运算符和表达式

下面的代码演示了逻辑表达式的运算。


01      # 逻辑运算符
02      print( not True)
03      print( False and True)
04      print (True and False)
05      print (True or False)

【代码说明】

·第2行代码,True的逻辑非为False。输出结果:False

·第3行代码,检测到and运算符左侧的False,就直接返回False。输出结果:False

·第4行代码,检测到and运算符左侧为True,然后继续检测右侧,右侧的值为False,于是返回False。输出结果:False

·第5行代码,or运算符的左侧为True,于是返回True。输出结果:True

逻辑非的优先级大于逻辑与和逻辑或的优先级,而逻辑与和逻辑或的优先级相等。逻辑运算符的优先级低于关系运算符,必须先计算关系运算符,然后再计算逻辑运算符。下面这段代码演示了逻辑运算符、关系运算符和算术运算符的优先级别。


01      # 逻辑表达式的优先级别
02      print( "not 1 and 0 =>", not 1 and 0)
03      print( "not (1 and 0) =>", not (1 and 0))
04      print ("(1 <= 2) and False or True =>", (1 <= 2) and False or True)
05      print ("(1 <= 2) or 1 > 1 + 2 =>", 1 <= 2, "or", 1 > 2, "=>", (1 <= 2) or (1 < 2))

【代码说明】

·第2行代码,先执行not 1,再执行and运算。输出结果:


not 1 and 0 => False

·第3行代码,先执行括号内的1 and 0,再执行not运算。输出结果:


not (1 and 0) => True

·第4行代码,先执行1<=2的关系运算表达式,再执行and运算,最后执行or运算。输出结果:


(1 <= 2) and False or True => True

·第5行代码,先执行1<=2的关系运算表达式,再执行表达式1>1+2,最后执行or运算。输出结果:


(1 <= 2) or 1 > 1 + 2 => True or False => True

2.6 小结

本章讲解了Python的基本语法和基本概念,包括Python的文件类型、编码规则、变量、数据类型以及运算符和表达式等内容。重点讲解了Python的编码规则、命名规则、缩进式的写法、注释等。Python中的变量与C、Java有很大的不同,本章通过比较的方式阐述Python变量的分类和特性。最后讲解了运算符和表达式的知识,这些知识是编写控制语句的基础。下一章将会学习到Python控制语句的编写,控制语句将用到本章介绍的变量和表达式。条件语句和循环语句都是通过表达式返回的布尔值控制程序流程的,这些内容是编写程序的基本要素。

2.7 习题

1.以下变量命名不正确的是( )。

A.foo=the_value

B.foo=1_value

C.foo=_value

D.foo=value_&

2.计算2的38次方的值。

3.以下逻辑运算的结果:

a)True and False

b)False and True

c)True or False

d)False or True

e)True or False and True

f)True and False or False

4.编写程序计算1+2+3+…+100的结果。 63T3qtLyWZqJC9LphTEz72ppeEYd2xFLgZEW/ca56q8dxEfova41Ele2bGFfkLDn

点击中间区域
呼出菜单
上一章
目录
下一章
×