本章以及后续几章,将从程序员的视角快速浏览 Python: 本章介绍语法以及内置的数字、字符串和 None,接下来依次介绍容器、 控制流、函数、模块、类、静态类型、类属性, 以及对象清理。 这里假设你已经有编程经验。 补充信息可以在官方语言文档中查找。

脚本与编程

Python 的存在,就是为了提高你的生产力。 这门语言的目标,是尽可能帮助你。 它也尽量少给你制造阻碍。 它不会强加任意规则,也不会强迫你采用某一组特性。

人们经常把 Python 称作脚本语言, 但脚本语言往往存在局限, 尤其是它们能够解决的问题范围通常有限。 Python 是一门同时支持脚本编写的编程语言。 它非常适合写脚本,你完全可以用 Python 脚本替换所有 batch files、 shell scripts 和简单程序。

如何阅读示例

# 表示一条一直延续到行尾的注释, 就像 C++ 和 Java 中的 // 注释。 示例 解释了代码第一行的文件名,以及 #: 输出标记。

缩进与代码块

Python 写起来很干净。 即使代码已经写完很久,你自己的代码仍然容易阅读。 缩进决定语句如何组合成代码块。 下面的脚本可以通过 python if.py 运行:

# if.py

response = "yes"
if response == "yes":
    print("affirmative")
    val = 1
#: affirmative
print("continuing...")
#: continuing...
print(val)
#: 1

C/C++ 的 if 要求条件表达式外面必须有括号。 Python 不需要括号,不过即使你写了,它也不会抱怨。

条件子句以冒号结束。 接下来是一组缩进后的语句:也就是 if 语句的“then”部分。 print() 函数把参数发送到标准输出。 下一行给名为 val 的变量赋值。 再下一条语句没有缩进,因此它不再属于这个 if。

缩进代码块会把语句组织在一起,但不会创建作用域。 在 if 内部给 val 赋值后,它在后面仍然可见, 这和 C++ 或 Java 中声明在花括号内部的变量不同。 函数、类和模块会引入新的作用域。 if 或 for 代码块不会。 名称绑定仍然取决于实际执行路径:如果 response 不是 "yes", Python 就不会绑定 val,此时 print(val) 会抛出 NameError。

缩进可以任意层级地嵌套。 惯例是每层四个空格, 同一个代码块中混用 tab 和空格会产生 TabError。 不同于 C++ 或 Java 中关于花括号放在哪一行的争论, 缩进不是风格偏好问题:它就是程序结构本身。 因此,任意两个作者写出的 Python 代码,其结构都会以相同方式对齐, 这也是 Python 能够保持一致可读性的主要原因之一。

Python 通常一行只有一条语句, 所以行尾不需要分号。 (你也可以在同一行写多条语句,只需要用分号把它们分开。)

变量与引用

Python 中的变量,是绑定到对象的名称,而不是一个装着值的盒子。 赋值操作会绑定名称。 它不会复制对象。 你不需要声明变量的类型, 而且同一个名称在其生命周期中可以绑定到不同类型的对象。 这就是动态类型(dynamic typing)。 Python 之上还叠加了一套完整的静态类型系统, 本书会从08 · 静态类型 一章开始使用它。

# references.py

x = 10  # x names an int
x = "ten"  # The same name now binds to a str
a = [1, 2, 3]
b = a  # b binds to the same list, not a copy
b.append(4)
print(a)  # The same object: a and b
#: [1, 2, 3, 4]
print(a is b)  # Identical objects
#: True
c = a[:]  # Copies the list, not its contents
print(a is c, a == c)  # Different object, equal value
#: False True

使用 == 判断两个对象的值是否相等。 使用 is 判断两个名称是否引用同一个对象。 is 应保留给 None 和其他 singleton 使用。 a[:] 是一次浅拷贝(shallow copy): 它复制列表本身,但不会复制列表里的对象, 所以如果其中包含嵌套列表,a 和 c 仍然会共享那个嵌套列表。