5  Python基础

5.1 运行Python代码

5.1.1 在命令行运行Python脚本

在Anaconda Prompt中,输入python Python脚本名称,即可运行整个Python脚本。

注记说明
  1. 运行脚本使用的解释器是当前激活环境的Python解释器。
  2. Python脚本名称应当包括脚本路径和扩展名.py
图 5.1: 通过Anaconda Prompt运行Python脚本示意图。

5.1.2 用REPL运行Python代码

REPL代表:Read-Eval-Print Loop(读取-求值-打印-循环)。 它是一个交互式编程环境,你输入一行代码,它立即执行并返回结果,然后等待下一行输入。

  1. 在Anaconda Prompt中,输入python,回车,即进入基础的REPL环境。输入exit()并回车退出。( 图 5.2
  2. 如果环境中安装了ipython,输入ipython,回车,则进入ipython。Ipython是更高级的REPL,具有代码补全,语法高亮等功能。 输入exit()并回车退出。( 图 5.3
  3. Jupyter Notebook运行Python代码。Jupyter Notebook允许用编辑器代码,运行任意选中的代码,因此比前两种方式更适合编程作业。
图 5.2: Python REPL使用示例。提示符是>>>。
图 5.3: Ipython使用示例。提示符是[数字]。

5.2 对象

Python 中一切数据都是对象(object)。 对象有类型(type)、值(value)、id三要素。

  • id是对象在内存中占据的地址。 用id()查看对象的 id, 用isis not 运算符比较两个对象的 id 是否相等。
  • 类型决定对象能进行的运算、能取的值、是否可变。 用 type() 查看对象的类型。用 表 5.1 中的英文类型可以将对象转换为相应类型,但并非所有类型转换都能成功。
  • print() 查看对象的值。 用运算符==比较对象的值是否相等。 用运算符!=比较对象的值是否不相等。
表 5.1: Python常用内置数据类型
大类 类型(中文) 类型(英文) 字面量1示例
数值 整数
浮点数
复数
int
float
complex
1
1.0
1+3j
序列 字符串
列表
元组
str
list
tuple
‘三个字’
[1,2,3]
(1,2,3)
映射 字典 dict {‘姓名’:‘孙悟空’,‘武器’:‘金箍棒’}
集合 集合
不可变集合
set
frozenset
{1,2,3}
注记练习

用type()查看上面的字面量的类型。

注记拓展

int,str,dict都是英文单词缩写。 相应的完整英文单词怎么拼写?

注记拓展
  1. 尝试解释type(print)的输出结果。
  2. 尝试解释type(range),type(type)的输出结果。
注记例:数据类型转换示例
float(10)
10.0
int(1.4)
1
str(123)
'123'
int('字符串')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 1
----> 1 int('字符串')

ValueError: invalid literal for int() with base 10: '字符串'
注记例:数据类型决定对象能进行的计算
# 数值数据之间可以进行+操作
1 + 10.5
11.5
# 字符串之间可以进行+操作
'一片' + '两片'
'一片两片'
# 字符串与数字之间不可以进行+操作
'一片' + 123
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 2
      1 # 字符串与数字之间不可以进行+操作
----> 2 '一片' + 123

TypeError: can only concatenate str (not "int") to str

5.3 赋值

Python中的赋值,本质上是将标识符(identifier)绑定到某个对象。标识符是变量、函数、类、模块等对象的名称。如x=1,是将x这个名称绑定到整数对象1。x好比贴纸,1好比一个具体的物品,赋值过程将贴纸x贴到某个值为1的物品上。用关键字del删除标识符与对象的绑定。

x = 1
print(x) # 返回x的值1
del x
print(x) # 报错,因为标识符与对象的绑定已被删除
1
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[8], line 4
      2 print(x) # 返回x的值1
      3 del x
----> 4 print(x)

NameError: name 'x' is not defined

通过赋值将对象与标识符绑定,有助于提高代码的可读性与可维护性:

  • 见名知意的标识符有助于读者理解代码的意图。你能知道200*TAX_RATE是在用税率计算税款,但完全无法知道200*0.25有什么含义。

  • 想象某个对象在代码中多次出现的场景。如果将这个对象与某个标识符绑定,需要修改此对象时,只需在赋值处修改即可,否则需要对代码中该对象出现的所有地方都进行修改,既麻烦也容易出错。

    事实上,代码中没有被赋予标识符的数值数据被称为魔术数字(magic number)。因为从上下文很难看出这些数字的含义,这些数字好像是凭空出现的,如魔法一般。魔数是一种被批判的编程风格。

除了形如x=1的平凡赋值,Python还支持3种赋值方式:

  1. 增强赋值/复合赋值:将算术运算符与=结合在一起,如x += 1,等同于 x = x + 1,其计算顺序是:先计算x + 1的值,然后将这个值绑定到标识符x。 增强赋值的作用是简化书写。编程语言中不增加新的语言功能、只简化书写的实践称为语法糖(syntax sugar)。
x += 1 # x = x + 1
x -= 1 # x = x - 1
x *= 1 # x = x * 1
x /= 1 # x = x/1
# 算术运算符%, //运算符也可以出现在增强赋值中
  1. 链式赋值:x = y = 1,把多个标识符绑定到同一个对象。相当于y = 1; x = y。链式赋值涉及的对象具有相同的id。 明白这一点有助于理解 章节 22 中的可变对象与不可变对象的一些行为。
# 链式赋值涉及的对象的值和id都相同
# 值相同的对象不一定具有相同id
x = 12345
y = x
z = 12345

print(f'x,y的id是否相同:{x is y}, y, z的id是否相同:{y is z}')
print(id(x), id(y), id(z))
x,y的id是否相同:True, y, z的id是否相同:False
2540030176048 2540030176048 2540030177008
# 非重点
# x,y并不处于链式赋值中,却具有相同的id
# 这是由于Python的优化机制驻留interning:
# Python启动时就创建好一些常用的不可变对象,将它们存储在内存的固定位置
# 使用这些值时,Python不创建新对象,而是直接返回已存在的那个对象的引用
x = 1
y = 1

print(x is y, id(x), id(y))
True 140705381200808 140705381200808
  1. 解包赋值:用一个序列同时给多个变量赋值。如
a, b, c = [1, 2, 3] 
a, b, c = (1, 2, 3)
a, b, c = '一二三'
x, y, z = a, b, c
a, b = b, a # 可以方便地交换两个变量的值
注记练习:解释a,x,y值的变化过程
a = 2
x = y = 10
x += 1
a += 10
y, x = x, y
print(f"a={a}, x = {x}, y = {y}")
a=12, x = 10, y = 11
a = 1; x = 2; y = 3
a += 10
x /= 2
y *= 3
print(f"a={a}, x = {x}, y = {y}")
a=11, x = 1.0, y = 9

5.4 函数

函数(function)是可以重复使用的代码块,允许我们方便地完成一些功能。 使用函数称为函数调用(function call)。 调用函数时,先写函数名,后跟一对小括号。如果函数有参数,小括号内要写参数。 调用函数后得到函数的返回值。 我们常用赋值的方式将函数返回值与某个标识符绑定供后续使用。

  1. input(),接受用户输入,将用户输入作为字符串返回。可接受字符串prompt作为参数,prompt是等待用户输入时显示的提示语。

    可以用int(), float()类将input()的返回值转换为整数或浮点数类型。

from datetime import datetime

name = input('请输入您的姓名:') # 将函数返回值与标识符绑定
age = int(input('请输入您的年龄:')) # 用int()将字符串转换为整数类型int
birth_year = datetime.now().year - age
print('您的名字是', name)
print(f'您生于{birth_year}年。')
  1. print(),打印。常用参数:

    • sep,打印多个对象时,对象之间的分隔符。默认值是空格。
    • end,打印完毕后,打印的结束符。默认是换行符。

    print函数的返回值是None(你可用a=print('a'); print(a)验证)。打印字符串的行为,是print函数的副作用2

  2. help(),提供对象的帮助文档。 但如果要学习具体对象的使用方法,建议查阅官方文档,如Python官方文档。官方文档更全面,讲得更清楚。

图 5.4: Python官网文档界面。成熟的第三方库也有自己的官方文档页面。

5.5 运算符

运算符(operator)是对数据执行操作的符号或关键字3,需与操作对象(operand)一起使用。运算符具有优先级(precedence)与结合性(associativity)。 章节 20 列出了所有运算符的优先级与结合性。

  1. 优先级:若一个操作对象的左右两边都有运算符,此操作对象先参与优先级更高的运算符的运算。如乘、除法算术运算符*,/的优先级高于加、减法运算符+,-。可用小括号改变运算优先级,如(1 + 2) * 4
  2. 结合性:若一个操作对象两端的运算符的优先级相同,结合性决定了运算方向是从左至右,还是从右至左。多数运算符的结合性都是从左至右,只有幂运算符**和选择运算符if else的结合性是从右至左。

5.6 模块,包,库

5.6.1 相关术语

保存 Python 代码的文本文件的后缀名为.py,称这种文件为 Python 的源代码文件。 一个.py文件,就是一个模块(module)。模块中可以包含 Python 变量,函数,类等。

相关联的一系列模块放在一个文件夹中,构成一个 (package)。包可以包含多个模块,也可以包含子包 (subpackage)。

相关联的一系列包/模块构成(library)。库和包经常混用。 Python 安装包自带了一系列模块,称为标准库。

5.6.2 使用模块,包,库

import关键字调用模块/包/库。

  1. import 模块名。需用模块名.模块成员名来引用模块成员。
  2. import 模块名 as 别名。同上,需用别名.模块成员名来引用模块成员。
  3. from 模块名 import 模块成员。可直接使用模块成员,不需要加模块名.作为前缀。
  4. from 模块名 import **表示全部对象。用此法导入可直接使用模块中的所有成员,不需任何前缀。但模块成员名与用户自定义的标识符容易冲突,因此不推荐这种导入方法。

下面演示导入并使用math库的求平方根的函数sqrt()

# 导入方法1
import math
math.sqrt(100)
print(math.pi)

# 导入方法2
import math as m
m.sqrt(100)

# 导入方法3
from math import sqrt
sqrt(100)

# 导入方法4
from math import *
sqrt(100)
表 5.2: math模块部分函数
函数名 功能
sqrt() 求平方根
ceil()
floor()
trunc()
向下取整
向上取整
截断小数取整
isclose() 比较浮点数是否相等
sin()
cos()
三角函数
exp()
log()
指数函数
对数函数

5.7 Python代码书写规则

5.7.1 编程语言通用规则

  1. 使用ASCII字符

    除了位于一对引号内的字符可以是任意字符,语法部分的符号应当都是ASCII符号,主要指冒号:,逗号,,引号""``'',小括号()。ASCII符号指英文输入法下的半角符号,而非中文输入法下的全角符号。输入全角版本的相应符号,是一类常见于编程新手的错误。

  2. 引号、括号必须成对出现。

    成对出现的引号应该是相同类型,如同为单引号',同为双引号",同为三引号'''

    一个例外情况是,字符串中可以出现任意字符,因此也可以出现单个引号或括号。但引号中不能直接插入与外围引号相同类型的引号字符,否则会提前结束引号表示的字符串从而引起报错。

# 单引号中允许出现双引号
'"'
'"'
# 双引号中允许出现单引号
"'"
"'"
  1. 标识符命名规则。

    1. 标识符只能由数字、字母下划线组成,且不能以数字开头4
    2. 不能用关键字做标识符。
    3. 标识符最好不与预定义标识符相同。Python的预定义标识符指内置函数,异常名称等,如print。用dir(__builtins__)可打印Python的预定义标识符。
    4. 包括Python在内的多数编程语言区分大小写,即Aa是不同的标识符。
    5. Python中下划线_是特殊标识符,与上一次运算的结果绑定。
    6. Python中,以双下划线开头和结束的名称通常具有特殊含义,应当只在特殊场合使用,如__init__
    7. 还有一些标识符命名建议,如其值不改变的量(常量)的标识符用全大写,类名称一般用驼峰命名法,形如DataLoader
表 5.3: Python关键字
False None True and as
assert async await break class
continue def del elif else
except finally for from global
if import in is lambda
nonlocal not or pass raise
return try while with yield
注记debug:问题出在哪?
x = 1
if x > 0
    print(x) 
  Cell In[16], line 2
    if x > 0:
            ^
SyntaxError: invalid character ':' (U+FF1A)
'春种一粒粟
  Cell In[17], line 1
    '春种一粒粟
    ^
SyntaxError: unterminated string literal (detected at line 1)
print(12)
  Cell In[18], line 1
    print(1,2)
           ^
SyntaxError: invalid character ',' (U+FF0C)
"""
  Cell In[19], line 1
    """
    ^
_IncompleteInputError: incomplete input
  1. 换行与空格

    1. 隐式换行:出现起始括号后、还未出现与之匹配的终止括号前,允许换行。一般是在逗号分隔符,或算术运算运算符后换行。这种情况下换行后可以空几格对齐,这种空格不会被解读为有语法意义的缩进。
    2. 显式换行:在Python代码中插入反斜杠\即可换行。为了可读性,推荐一行不超过79个字符;或以不需要拖动水平滚动条即可看到代码全貌为宜。
    3. 运算符的两端,以及,;等分隔符的后面,一般有一个空格。这不是强制要求,只是增强代码可读性的良好编程实践。但成对出现的括号()[]{}等的两边,表示正负的+-运算符与后面的数字之间,不需要空格。 注意增强赋值+=+=是一个整体,之间不能有空格。
    4. 通常一行只写一个语句5。一行也可以写多个语句,此时同一行的语句之间用分号;分隔。
# 不换行版本
def calculate_total(item_price, item_quantity, tax_rate, discount_percentage):
    total_price = ((item_price * item_quantity) - ((item_price * item_quantity) * (discount_percentage / 100)) + ((item_price * item_quantity) * tax_rate))
    return total_price
# 换行版本
def calculate_total(item_price,
                    item_quantity,
                    tax_rate,
                    discount_percentage):
    total_price = (
            (item_price * item_quantity) -
            ((item_price * item_quantity) * (discount_percentage / 100)) +
            ((item_price * item_quantity) * tax_rate)
    )
    return total_price
# 显式换行
1 + \
    1
2
x = 10
x + = 10
  Cell In[23], line 2
    x + = 10
        ^
SyntaxError: invalid syntax

5.7.2 缩进

缩进(indentation)是Python语法的一部分。表现为出现冒号时,需要换行,下一行开始缩进4个空格,直到本代码块结束时才恢复正常缩进。

缩进主要出现在循环结构(for, while语句),选择结构(if-else语句),函数和类的定义中。

# 循环语句
for i in range(3):
    print(i)

# 嵌套循环
# 缩进会累积
for a in range(1,10):
    for b in range(1,10):
        print(a,'*',b,'=', a*b, end = '\t')

# 选择语句
if (1+1) < 0:
    print("ridiculous")
else:
    pass

# 函数定义
def foo():
    pass
注记说明
  1. 缩进不一定必须是4个空格,但代码中所有缩进的缩进量应当相同,且不要将制表符tab与空格混用。缩进4空格是一种约定俗成。
  2. 如果冒号后的代码块中只有一个语句,可以不换行。但换行的可读性更好。
# 冒号后只有一个语句,可以不换行
def foo(): pass
def foo(): 
pass
  Cell In[25], line 2
    pass
    ^
IndentationError: expected an indented block after function definition on line 1
def foo(): 
    print("first line")
        print("second line")
  Cell In[26], line 3
    print("second line")
    ^
IndentationError: unexpected indent

5.7.3 注释

注释是给人类程序员看的,被注释内容不会被执行。Python的注释方式有:

  1. #单行注释。
  2. 用三引号进行多行注释。三引号是3个连续的引号,可以是单引号或双引号。但严格说来三引号并不是注释,而是多行字符串字面量。
# 单行注释

'''
使用三引号
可以进行
多行注释
一般放在模块、类、函数定义的第一行
用于生成帮助文档
称为文档字符串docstring
'''

"""
三引号可以用单引号
也可以像这个例子
用双引号
"""

5.8 练习

  1. math模块的sqrt()函数,计算 \sqrt{2}
  2. 用random模块模拟点名,记录首次出现同一个学号被点到两次时已点名的次数。
  3. input()函数,接受用户名称与出生年份, 返回用户名与用户的年龄。(如'你好啊小明,你今年5岁了。')。

  1. 字面常量(literal),直接在源代码中明确写出其固定值的数据表示形式↩︎

  2. 副作用 (side effect) 指函数在执行过程中除了返回值之外,还对程序的其他部分产生可观察的影响,如将标识符与对象绑定,打印对象↩︎

  3. 关键字(keywords)是语言语法的一部分,具有固定含义。它们用于定义程序的结构、流程控制等。↩︎

  4. 其实Python3支持使用部分Unicode字符做标识符,如小明=1。但考虑到兼容性与可移植性,不建议标识符中出现非ASCII字符↩︎

  5. 语句(statement)是 Python 程序的过程构造块,用于定义函数、定义类、创建对象、变量赋值、调用函数、控制分支、创建循环等。由操作对象和运算符构成的表达式(expression)也是语句,称为表达式语句↩︎