程序设计与计算思维¶

Computer Programming and Computational Thinking

第 2 讲:Python 基础教程、抽象方法¶

2025—2026学年度春季学期

清华大学 韩文弢

Python 基础教程¶

高级语言的三种基本结构:

  • 顺序结构:程序按照代码书写的先后顺序,从上到下逐行执行。这是最基础、最简单的程序结构。
  • 选择结构:程序根据判断条件的真假,选择执行不同的分支路径。也称为分支结构或条件结构。
  • 循环结构:程序在满足特定条件的情况下,反复执行某一段代码块,直到条件不再满足为止。也称为重复结构或迭代结构。

while 循环语句¶

In [1]:
# 斐波那契数列:
# 前两项之和即下一项的值
a, b = 0, 1
while a < 10:
    print(a)
    a, b = b, a+b
0
1
1
2
3
5
8
  • 第一行中的多重赋值:变量 a 和 b 同时获得新值 0 和 1。最后一行又用了一次多重赋值,Python 会首先计算等号右边的所有表达式,将它们的值赋值给左边对应的变量。
  • while 循环只要条件(这里是 a < 10)为真就会一直执行。Python 中任何非零整数都为真,零为假。
  • 这个条件也可以是字符串或列表类型的值,任何序列长度非零就为真,空序列则为假。
  • 示例中的判断是最简单的比较。比较操作符有:< (小于)、 > (大于)、 == (等于)、 <= (小于等于)、 >= (大于等于)及 != (不等于)。
  • 循环体是缩进的:缩进是 Python 组织语句的方式,一般用4个空格代表一层,相同缩进的语句构成一个语句块。

if 条件语句¶

In [2]:
x = 42

if x < 0:
    x = 0
    print('把负数变成零')
elif x == 0:
    print('零')
elif x == 1:
    print('壹')
else:
    print('更多')
更多

可有零个或多个 elif 部分,else 部分也是可选的。关键字 elif 是 else if 的缩写,用于避免过多的缩进。

In [3]:
x = 10

if x < 0:
    x = 0
    print('把负数变成零')
else:
    if x == 0:
        print('零')
    else:
        if x == 1:
            print('壹')
        else:
            print('更多')
x
更多
Out[3]:
10

for 循环语句¶

In [4]:
# 度量一些字符串:
words = ['cat', 'window', 'defenestrate']
for w in words:
    print(w, len(w))
cat 3
window 6
defenestrate 12

Python 的 for 语句在列表或字符串等任意序列的元素上迭代,按它们在序列中出现的顺序进行。

range 函数¶

内置函数 range() 用于生成等差数列:

In [5]:
for i in range(5):
    print(i)
0
1
2
3
4

生成的序列不包括给定的终止值;range(10) 生成 10 个值——长度为 10 的序列的所有合法索引。

range 可以不从 0 开始,且可以按给定的步长递增(即使是负数步长):

In [6]:
list(range(5, 10))
Out[6]:
[5, 6, 7, 8, 9]
In [7]:
list(range(0, 10, 3))
Out[7]:
[0, 3, 6, 9]
In [8]:
list(range(-10, -100, -30))
Out[8]:
[-10, -40, -70]

要按索引迭代序列,可以组合使用 range() 和 len():

In [9]:
a = ['Mary', 'had', 'a', 'little', 'lamb']
for i in range(len(a)):
    print(i, a[i])
0 Mary
1 had
2 a
3 little
4 lamb

这种情况一般使用 enumerate() 函数会更优雅:

In [10]:
for i, w in enumerate(a):
    print(i, w)
0 Mary
1 had
2 a
3 little
4 lamb

如果直接打印一个 range 会发生意想不到的事情:

In [11]:
range(10)
Out[11]:
range(0, 10)

range() 返回的对象在很多方面和列表的行为一样,但其实它和列表不一样。该对象只有在被迭代时才一个一个地返回所期望的列表项,并没有真正生成过一个含有全部项的列表,从而节省了空间。

这种对象称为可迭代对象 iterable,适合作为需要获取一系列值的函数或程序构件的参数。for 语句就是这样的程序构件;以可迭代对象作为参数的函数例如 sum():

In [12]:
sum(range(4))  # 0 + 1 + 2 + 3
Out[12]:
6

定义函数¶

下列代码创建一个可以输出限定数值内的斐波那契数列函数:

In [13]:
def fib(n):    # 打印小于 n 的斐波那契数列
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

# 现在调用我们刚定义的函数:
fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 

定义函数使用关键字 def,后跟函数名与括号内的形参列表。函数语句从下一行开始,并且必须缩进。

编写不直接输出斐波那契数列运算结果,而是返回运算结果列表的函数:

In [14]:
def fib2(n):  # 返回斐波那契数组直到 n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)    # 见下
        a, b = b, a+b
    return result

f100 = fib2(100)    # 调用它
f100                # 输出结果
Out[14]:
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

return 语句返回函数的值。return 语句不带表达式参数时,返回 None。函数执行完毕退出也返回 None。

语句 result.append(a) 调用了列表对象 result 的方法。 方法是从属于对象的函数,其名称为 obj.methodname,其中 obj 是某个对象(可以是一个表达式),methodname 是由对象的类型定义的方法名称。 不同的类型定义了不同的方法。 不同的类型的方法可以使用相同的名称而不会产生歧义。在示例中显示的方法 append() 是由列表对象定义的;它会在列表的末尾添加一个新元素。

pass 语句¶

pass 语句不执行任何动作。语法上需要一个语句,但程序毋需执行任何动作时,可以使用该语句。例如:

In [15]:
while True:
    pass  # 无限等待键盘中断 (Ctrl+C)
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[15], line 2
      1 while True:
----> 2     pass  # 无限等待键盘中断 (Ctrl+C)

KeyboardInterrupt: 

pass 还可用作函数体的占位符:

In [16]:
def func():
    pass  # 记得实现这个!

使用抽象方法¶

抽象是从事物共性中提取关注的特征,忽略其他不关注的属性,从而形成一种可以代表一组相似事物的概念,是具体或特化的反面。

什么是“一”?¶

看一些可以代表“一”的例子。

In [17]:
from fractions import Fraction
import numpy as np
import matplotlib.pyplot as plt
In [18]:
ones = [1, 1.0, 'one', Fraction(1, 1), np.eye(2)]
ones
Out[18]:
[1,
 1.0,
 'one',
 Fraction(1, 1),
 array([[1., 0.],
        [0., 1.]])]

这个列表中的每一项都是“一”的一个具体的或特化的表示:

  1. 作为整数
  2. 作为浮点数
  3. 作为字符串
  4. 作为有理数
  5. 作为 2x2 单位矩阵

当然,这些只是"一"的几个例子。人们长期以来一直在用不同的语言、文字、艺术表达等方式来表示"一"。

计算机是如何根据输入的内容以不同方式看待"一"的?

In [19]:
computer_ones = [type(one) for one in ones]
computer_ones
Out[19]:
[int, float, str, fractions.Fraction, numpy.ndarray]

计算机使用类型来区分不同的值。然而,不用类型的值在符合逻辑的情况下又可以支持相同的操作。

什么是"一"的集合?¶

现在想创建一个"一"的集合。下面是一种可以用不同的"一"来构建这个集合的方式。

In [20]:
element = ones[1]
array = np.full((3, 4), element)
array
Out[20]:
array([[1., 1., 1., 1.],
       [1., 1., 1., 1.],
       [1., 1., 1., 1.]])

当使用不同的"一"时,这段代码几乎都是可以正常运行的。

这正是抽象的含义。换句话说,可以在一个不关心使用的是哪个具体"一"的层面上进行思考和操作。这表明抽象是特化的反面。

尝试使用抽象¶

现在想对一个"一"的集合执行某种操作,而不关心使用的是哪种"一"。

例如,想把一个矩阵复制一份,并修改其中某个位置的一个元素。

In [21]:
def insert(new, A, i, j):
    B = A.copy()
    B[i, j] = new
    return B
In [22]:
one_number_array = np.full((3, 4), 1)
insert(5, one_number_array, 1, 2)
Out[22]:
array([[1, 1, 1, 1],
       [1, 1, 5, 1],
       [1, 1, 1, 1]])
In [23]:
one_string_array = np.full((3, 4), 'one', dtype=np.dtypes.StringDType())
insert('five', one_string_array, 1, 2)
Out[23]:
array([['one', 'one', 'one', 'one'],
       ['one', 'one', 'five', 'one'],
       ['one', 'one', 'one', 'one']], dtype=StringDType())

insert 函数只关心如何将一个对象插入数组,而对数组内部的内容一无所知,但它却能够用于两个完全不同的数组——任意类型的"一"的集合。

本讲小结¶

  • Python 的基本语法
    • 条件 if
    • 循环 while、for
    • 函数定义与调用
  • 抽象方法
    • 计算机语言支持抽象,在编写程序时可以消除掉具体细节,做一些更通用的事情