Python核心编程--学习笔记--3--Python基础
本章介绍基本的Python语法、编程风格;并简要介绍标识符、变量和关键字,以及变量占用内存的分配和回收;最后给出一个较大的Python样例程序来体验这些特性。
1 语句和语法
1.1 注释
可以在一行的任何位置,以 # 开头,直至行尾都是注释。
1.2 反斜线继续上一行
一行过长的语句,可以用反斜线 \ 分解成几行。\ 必须是该行的最后一个字符(其后不能有空格等其他任何字符)。
闭合符号:小括号()、中括号[]、花括号{}、三引号""" """/''' ''',其内的语句可以跨行书写,不需使用 \ 。
1.3 多个语句构成代码组
缩进相同的一组语句构成一个代码块,称为代码组。例如:if、while等关键字开头的首行,之后的一行或多行就是一个代码组。首行及之后的代码组称为一个子句(clause)。
1.4 代码组由不同的缩进分隔
代码的层次关系通过同样深度的空格或制表符缩进来体现,同一代码组的代码行必须严格左对齐。
没有缩进的代码块是最高层次的,称为脚本的main部分。
1.5 同一行书写多个语句
多个语句写在同一行的话,之间用分号(;)隔开,这些语句同属一个代码块(子句),中间不能开始新的代码块。
1.6 模块
每个Python脚本文件都可以当成一个模块,模块以磁盘文件形式存在。如果一个模块太大,功能太多,应考虑拆分到多个模块中。
2 变量赋值
Python通过等号=、增量赋值运算符+=(-=、*=等)来赋值。
Python中,对象是通过引用传递的。在赋值时,不管这个对象是新创建的,还是一个已经存在的,都是将该对象的引用(并不是值)赋值给变量。(要有对象的概念,名字只是一个符号/指针,指向被赋值的对象)
Python中的链式赋值(多重赋值),只是对象的同一个引用被赋给了多个变量。Python的赋值语句不会返回值(与C不同)。
>>> x = y = z = 1
>>> x
1
>>> y
1
>>> z
1
>>> x = ( y = 13 )
File "<stdin>", line 1
x = ( y = 13 )
^
SyntaxError: invalid syntax
增量赋值,同样适合与列表/字符串;++n、--n被解析为+(+n)、-(-n),n++、n--是语法错误。
+= -= *= /= %= **= #Python支持的增量赋值符 <<= >>= &= ^= |=
>>> aList = [123, 'xyz'] >>> aList += [45.6e7] #列表加 >>> aList [123, 'xyz', 456000000.0] >>> n=3 >>> --n #与C语言不同 3 >>> n 3 >>> n-- #语法错误 File "<stdin>", line 1 n-- ^ SyntaxError: invalid syntax
多元赋值,即同时给多个变量赋值:
>>> x, y, z = 1, 2, 'a string' #等号两边实质是元组,建议总是加上括号,即 (x, y, z) = (1, 2, 'string') >>> x 1 >>> y 2 >>> z 'a string' >>> x, y = 1, 2 >>> x 1 >>> y 2 >>> x, y = y, x #不需要中间变量,即可交换两个变量值 >>> x 2 >>> y 1
3 标识符
合法标识符:同C语言,字母或下划线_开头,后面可以是字母、数字或下划线;大小写敏感。
关键字:关键字列表kwlist和iskeyword()函数都放入了keyword模块以便查阅。
and |
as | assert | break |
class | continue | def | del |
elif | else | except | exec |
finally | for | from | global |
if | import | in | is |
lambda | not | or | pass |
raise | return | try | |
while | with | yield | None |
>>> import keyword
>>> keyword.iskeyword('and')
True
内建:除了关键字之外,Python还有可以在任何一级代码使用的“内建”的名字集合,这些名字可以由解释器设置或使用,应该把它当作“系统保留字”,不做他用。Python不支持重载标识符,任何时刻都只有一个名字绑定。内建built-in是__builtins__模块的成员,在程序开始或在交互解释器中给出>>>提示之前,由解释器自动导入的。把它们看成适用在任何一级Python代码的全局变量。
类变量的专用下划线标识符:
_xxx #不用'from module import *'导入,外部可以访问,但是应视其为 私有变量
__xxx__ #系统定义名字,特殊变量,可以直接访问,自己不要使用这种变量名
__xxx #类中的私有变量名,外部无法访问(实质上,一般的编译器,都将其改名为 _所属类名__xxx)
4 基本风格指南
添加注释,写字串文档,采用合适缩进,尽量使用简短的变量名。
import this #可以看到Python之禅
4.1 模块结构和布局
编写脚本文件pytest.py,并执行:
#/usr/bin/env python #1.起始行(Unix) / 模块注释
# -*- coding: utf-8 -*- #如果代码包含中文,需要添加该行(由于出现在该注释之前,上一行的中文注释会被报错)
"this is a test module" #2.模块文档,可通过 模块名.__doc__ 直接访问
import sys #3.导入所需的模块
import os
debug = True #4.全局变量。尽量少使用全局变量,有利于代码维护,提高性能,节省内存
class FooClass(object): #5.类定义,模块被导入时类就会被定义
"Foo class" #类文档,可通过 模块名.类名.__doc__ 来访问
pass
def test(): #6.函数定义,模块被导入时类就会被定义,可通过 模块名.函数名() 来访问
"test function" #函数文档,可通过 模块名.函数名.__doc__ 来访问
foo = FooClass() #类对象
if debug:
print 'ran test()'
if __name__ == '__main__': #7.主程序部分,通常只包含函数调用,无论是被导入还是作为脚本执行,都会执行这部分代码
test() #如果模块直接执行,则__name__值为'__main__',该句执行;如果被导入,则__name__值为模块名
$ python pytest.py #作为脚本直接执行
ran test()
>>> import pytest #交互解释器中,导入模块,主程序部分的if条件不成立。模块名即去掉后缀的文件名
>>> pytest.__doc__ #模块文档
'this is a test module'
>>> pytest.FooClass.__doc__ #模块内的类文档
'Foo class'
>>> pytest.test() #执行模块内函数
ran test()
没有缩进的代码,在模块被导入时就会被执行(所以导入模块时,类、函数就会被定义),所以安全的写代码方式:除了真正要执行的代码,把其他功能代码都写在函数当中。
绝大部分的模块,其创建的目的就是被导入,所以应该尽量只包括类、函数的定义。很多项目都只有一个主程序部分,由它导入模块、调用函数。
4.2 在主程序中书写测试代码
测试代码应该在模块直接执行时被执行,模块被导入时不执行。(即用到上面例子的最后if语句)
测试代码应随着测试条件、结果变更时及时更新,且每次代码更新后都应执行测试代码,以确认没有新问题。
5 内存管理
5.1 变量定义
变量无需声明,第一次被赋值时自动声明。
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x=4
>>> x
4
5.2 动态类型
变量的类型和占用的内存,都是运行时确定的。赋值时,解释器根据语法和右操作数来决定新对象类型。对象创建后,将其引用赋给左边变量。
5.3 内存分配
Python解释器负责内存管理,程序员无需关心。
5.4 引用计数
Python内部记录着使用中的对象各有多少引用,对象的引用数目简称引用计数。当对象的引用计数为0时,它就被垃圾回收。
增加引用计数:
x = 3.14 #对象被创建
y = x #对象的别名被创建,即对象被赋值给一个新变量
foobar(x) #对象作为参数传递给函数(新的本地引用)
myList = [123, x, 'xyz'] #对象成为容器对象的一个元素
减少引用计数:
一个本地引用离开了其作用范围,比如上面的foobar()函数结束时。
del y #对象的别名被显式的销毁
x = 123 #对象的一个别名被赋值给其它的对象
myList.remove(x) #对象被从一个窗口对象中移除
del myList #窗口对象本身 离开作用范围 或 被销毁
del语句:
del obj1[, obj2[...]] #可以一次删除多个对象的引用
上面的 del y,会将y从现在的名字空间中删除,且y指向的对象引用计数减1。再执行del x后,该对象引用计数为0,对象从此“无法访问”。追踪或调试程序会增加对象的一个额外引用。
5.5 垃圾收集
垃圾收集器是一段独立代码,用来寻找 引用计数为0的对象,也检查那些 引用计数不为0但应该被销毁的对象,然后释放其内存。
当两个对象相互引用时,引用计数可能不会为0,因此垃圾收集器也包含循环垃圾收集器,来清理未引用的循环。
6 第一个Python程序
介绍两个处理文本文件的脚本,分别实现写入/读取文件内容。
makeTextFile.py,创建文件,输入每行文本:
#!/usr/bin/env python
'makeTextFile.py -- create text file'
import os
ls = os.linesep #换行符,Unix为'\n',Windows为'\r\n',用os.linesep则不需关心平台。起个别名,减少名字查询,代码跑得快,名字也短
while True:
fname = raw_input('Enter filename: ')
if os.path.exists(fname): #判断文件是否已存在,存在则返回True
print "ERROR: '%s' already exists" % fname
else:
break
all = []
print "\nEnter lines ('.' by itself to quit).\n"
while True:
entry = raw_input('> ')
if entry == '.': #输入'.'时结束输入
break
else:
all.append(entry) #将输入内容添加到列表中
fobj = open(fname, 'w')
fobj.writelines(['%s%s' % (x, ls) for x in all]) #列表解析。writelines接收包含行结束符的结果列表,并写入文件
fobj.close()
print 'DONE!'
readTextFile.py,读取并显示文本内容:
#!/usr/bin/env Python
'readTextFile.py -- read and display text file'
fname = raw_input('Enter filename: ')
print
try: #捕获异常语句,适用于没有合适函数处理异常的情况。这里没有使用上面的os.path.exsits()函数,因为打开文件错误可能不仅仅是文件名错误导致
fobj = open(fname, 'r')
except IOError, e: #如果打开文件错误,则捕获异常,并输出异常提示信息,然后跳过else
print "*** file open error:", e
else: #如果成功打开文件,则执行该子句
for eachLine in fobj:
print eachLine, #创建文件时,我们人为加入了换行符,所以这里打印的时候要抑制print生成的换行符
fobj.close()
7 相关模块和开发工具
工具:Python代码风格指南(PEP8),Python快速参考和Python常见问答。
模块:
- 调试模块pdb:允许设置(条件)断点,代码逐行执行,检查堆栈。还支持事后调试。
- 日志模块logging:定义了一些函数和类帮程序实现灵活的日志系统。有五级日志级别:紧急,错误,警告,信息,调试。
- 性能测试模块:profile,hotshot,cProfile。
练习题
3–11 字符串格式化 不再抑制readTextFile.py中print语句生成的 NEWLINE 字符,修改你的代码,在显示一行之前删除每行末尾的空白。这样,你就可以移除print语句末尾的逗号了。
提示:使用字符串对象的strip()方法
1 #!/usr/bin/env Python 2 3 'readTextFile.py -- read and display text file' 4 5 fname = raw_input('Enter filename: ') 6 print 7 8 try: 9 fobj = open(fname, 'r') 10 except IOError, e: 11 print "*** file open error:", e 12 else: 13 for eachLine in fobj: 14 print eachLine.strip() #将逗号改为 .strip() ,此时读入的字串后面的换行符作为空白被舍弃,但是仍然输出print的换行符 15 fobj.close()