2. 变量与数据类型#

2.1. 变量#

在 Python 中,变量是保存值的命名存储位置。您可以将变量视为存储值的容器。要在 Python 中创建变量,只需为变量选择一个名称并使用等号 (=) 为该变量赋值。例如,以下代码创建一个名为 x 的变量并为其分配值 5:

x = 5
print(x)
5

因为Python 是动态类型的,变量不需要指定类型。这意味着如果我们想将 某个变量 作为其他类型重用,在Python 不会有任何问题。如:

x = 5
print(x, type(x))
x = "hello"
print(x, type(x))
5 <class 'int'>
hello <class 'str'>

2.2. 整数类型#

x = 2 # 整数类型
print(x)
print(type(x))
2
<class 'int'>

2.3. 浮点类型#

x = 2 # 整数型
y = 3.0 # 浮点型
z = x + y # 浮点型
print(x, y, z)
print(type(x), type(y), type(z))
2 3.0 5.0
<class 'int'> <class 'float'> <class 'float'>

2.4. 布尔类型#

x = True # 布尔类型
y = not x
print(x, y)
y = not y
print(y)

print(type(x))
print(x + y) # 布尔类型隐式转换成整数类型,进行数学计算
True False
True
<class 'bool'>
2

2.5. 字符串#

python中字符串用成对的单引号 (’…’) 或双引号 (”…”) 来标示,两者完全等同。

s = "hello" # s是字符串,hello 是字符串字面值
w = 'world'
print(s, w)
print(type(s), type(w))
print(s + " " +  w) # 字符串拼接用+符号
hello world
<class 'str'> <class 'str'>
hello world
s = 'First line.\nSecond line.'  # \n 表示换行符
print(s) # \n将会转义
First line.
Second line.

字符串前加r前缀,表示为原始字符串(raw的首字母),那么字符串中加\前缀进行的转义的字符,将表示原始字符串含义。

r = r'don\'t. First line.\nSecond line.'
print(r)
don\'t. First line.\nSecond line.

字符串字面值可以包含多行。 一种实现方式是使用三重引号:”””…””” 或 ‘’’…’’’。 字符串中将自动包括行结束符,但也可以在换行的地方添加一个 \ 来避免此情况

print("""\
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
""")
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to

注意:上面输出中开始的换行符没有被包括在内。

相邻的两个或多个 字符串字面值 (引号标注的字符)会自动合并:

s = 'Py' "thon"
print(s)

text = ('Put several strings within parentheses '
        'to have them joined together.')

print(text)
Python
Put several strings within parentheses to have them joined together.

2.6. 类型转换#

# 整数转换成浮点类型

x = 5
y = float(x)
print(y)
5.0
# 浮点类型转换成整数类型

x = 3.14
y = int(x)

print(y)
3
# numberic characters转换成整数或者浮点类型
x = "5"
y = int(x) 
print(y)

z = "3.14"
y = float(z) # int(z)尝试转换成整数类型,会发生异常
print(y)

x = "hello"
# y = int(x) 将非数字字符串转换成数字类型,会发生异常
5
3.14
# 数字转字符串
x = 3
y = str(x)
print(y, type(y))

y = 3.14
z = str(y)
print(z, type(z))
3 <class 'str'>
3.14 <class 'str'>

2.7. 基本操作符#

在Python中,有不同类型的操作符(operators)(特殊符号)对不同的值进行运算。一些基本运算符包括:

操作符

作用

类别

+

加法

算术运算符

-

减法

*

乘法

/

除法

//

floor division

**

指数(to the power of)

=

赋值

赋值运算符

+=

相加然后赋值

-=

相减然后赋值

*=

相乘然后赋值

==

相等

比较运算符

!=

不等于

<

小于

<=

小于等于

>

大于

>=

大于等于

not

not

布尔运算符

and

or

&

Logical And

Bitwise Operators

Logical OR

^

XOR

~

Negate

>>

Right shift

<<

Left shift

当在单个表达式中使用多个运算符时,运算符优先级(operator precedence)确定表达式的哪些部分按何种顺序求值。首先评估优先级较高的运算符(如数学中的 PEMDAS)。具有相同优先级的运算符从左到右进行计算。

  • () 括号,用于分组

  • ** 指数

  • *, / 乘法和除法

  • +、- 加法和减法

  • ==、!=、<、<=、>、>= 比较

2 + 2
4
50 - 5*6
20
17/3 # 除法运算总是返回一个浮点数
5.666666666666667
17 // 3 # 向下取整除法运算会丢弃小数部分
5
17 % 3 # % 运算返回相除的余数
2
5 * 3 + 2  # 向下取整的商 * 除数 + 余数
17
#  用 ** 运算符计算乘方
5 ** 2  # 5 的平方
25
s = "hello"
print(s * 2) # 字符串乘法,用于重复字符串
hellohello
print(not True) # False

a = "hello"
print(not a) # False

a = None
print(not a) # True

print(True and True) # True
print(True and False) # False
print(True or False) # True
False
False
True
True
False
True

2.7.1. x if y else z#

在Python中,x if y else z 是一个条件表达式,也称为三元条件运算符。它是一种简洁的方式来执行条件判断,其功能类似于标准的if-else语句。

这个表达式的工作原理如下:

  • x 是当条件 y 为真 (True) 时返回的值。

  • z 是当条件 y 为假 (False) 时返回的值。

# 假设我们有一个变量 age,我们想根据年龄判断一个人是否是成年人:
age = 17
adult_status = "成年" if age >= 18 else "未成年"
print(adult_status)  # 输出: 未成年
未成年

2.7.2. 布尔运算符#

  • not 的优先级最高, or 的优先级最低,因此,A and not B or C 等价于 (A and (not B)) or C

  • 布尔运算符 and 和 or 是所谓的 短路 运算符:其参数从左至右求值,一旦可以确定结果,求值就会停止。例如,如果 A 和 C 为真,B 为假,那么 A and B and C 不会对 C 求值。用作普通值而不是布尔值时,短路运算符的返回值通常是最后一个求了值的参数。

string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
non_null = string1 or string2 or string3
non_null
'Trondheim'

2.7.3. **运算符#

** 运算符并没有官方的中文名称。在中文文档和讨论中,通常会直接使用符号本身或者根据上下文翻译为“双星号”、“双星运算符”等。

在不同的编程语境中,** 运算符可能有不同的称呼,例如:

  • 解包运算符:当用于函数调用时,它会将字典解包为关键字参数,或者将可迭代对象解包为一系列参数。

  • 收集关键字参数运算符:当用于函数定义时,它会收集传递给函数的任意数量的关键字参数,并将它们存储在字典中。

  • 双星号运算符:直接称呼,因为符号就是两个星号。

** 运算符通常用于两种情况:

  1. 解构字典: ** 可以用来解构字典,将字典的键值对转换为函数调用的关键字参数。

def func(a, b, c):
    print(a, b, c)

# 字典
d = {'a': 1, 'b': 2, 'c': 3}

# 使用 ** 解构字典
func(**d)

在这个例子中,**d 将字典 d 解构为关键字参数 a=1, b=2, c=3,并传递给 func 函数。

  1. 收集函数调用中的剩余参数: 在定义函数时,** 可以用来收集任意数量的关键字参数,并将它们作为字典传递给函数。

def func(**kwargs):
    for key, value in kwargs.items():
        print(f"{key} = {value}")

# 调用函数时使用 ** 收集关键字参数
func(a=1, b=2, c=3)

2.8. 列表#

列表 ,是用方括号标注,逗号分隔的一组值。列表 可以包含不同类型的元素,但一般情况下,各个元素的类型相同

squares = [1, 4, 9, 16, 25]
print(squares)
print(type(squares))
[1, 4, 9, 16, 25]
<class 'list'>
squares[0]  # 索引操作将返回条目
1
squares[-1] # 返回最后一个条目
25
squares[-3:] # 切片操作,返回一个全新的列表
[9, 16, 25]
squares + [36, 49, 64, 81, 100] # 合并列表操作
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表是 mutable 类型,其内容可以改变:

cubes = [1, 8, 27, 65, 125]
cubes[3] = 64 
cubes.append(216)  # 添加 6 的立方
cubes.append(7 ** 3)  # 和 7 的立方
cubes
[1, 8, 27, 64, 125, 216, 343]

当你将一个列表赋值给一个变量时,该变量将引用 现有的列表。你通过一个变量对列表所做的任何更改都会被引用它的所有其他变量看到:

rgb = ["Red", "Green", "Blue"]
rgba = rgb
print(id(rgb) == id(rgba))  # 它们指向同一个对象
rgba.append("Alph")
rgb
True
['Red', 'Green', 'Blue', 'Alph']
# 切片操作返回包含请求元素的新列表。以下切片操作会返回列表的 浅拷贝:
correct_rgba = rgba[:]
correct_rgba[-1] = "Alpha"
print(rgba)
print(correct_rgba)
['Red', 'Green', 'Blue', 'Alph']
['Red', 'Green', 'Blue', 'Alpha']
# 为切片赋值可以改变列表大小,甚至清空整个列表:
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
print(letters)
print(len(letters)) # 内置函数len返回列表长度

# 替换一些值
letters[2:5] = ['C', 'D', 'E', "H"]
print(letters)

# 现在移除它们
letters[2:5] = []
print(letters)

# 通过用一个空列表替代所有元素来清空列表
letters[:] = []
print(letters)
['a', 'b', 'c', 'd', 'e', 'f', 'g']
7
['a', 'b', 'C', 'D', 'E', 'H', 'f', 'g']
['a', 'b', 'H', 'f', 'g']
[]
# 嵌套列表
a = ['a', 'b', 'c']
n = [1, 2, 3]
x = [a, n]
print(x)
[['a', 'b', 'c'], [1, 2, 3]]

2.8.1. 切片操作#

切片的基本语法如下:

sequence[start:stop:step]
  • sequence 是要进行切片的序列。

  • start 是切片开始的索引(包含该索引位置的元素),如果不设置,默认为0。反向索引时候,最后一个元素是-1。

  • stop 是切片结束的索引(不包含该索引位置的元素),如果不设置,则切片到序列的末尾。

  • step 是步长,表示取元素的间隔,如果不设置,默认为1。

注意: 对于不可变的数据类型(如字符串或元组),切片操作总是返回一个新的副本,修改这个副本不会影响原始数据。而对于可变的数据类型(如列表),切片操作返回的是一个新的序列对象,但它可能只是原始数据的一个引用,这种情况下修改副本可能会影响原始数据。

sequence = list(range(0, 10))
sequence 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 获取序列的全部元素:
sequence[:]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 从序列的开始位置到索引5(不包括索引5):
sequence[:5]
[0, 1, 2, 3, 4]
# 从索引5开始到序列的末尾:
sequence[5:]
[5, 6, 7, 8, 9]
# 从索引5开始到索引10(不包括索引10),步长为2(即取索引5, 7, 9):
sequence[5:10:2]
[5, 7, 9]
# 反向切片,从序列的末尾开始到索引5(不包括索引5),步长为-1:
sequence[-1:5:-1]
[9, 8, 7, 6]
# 只取序列的第3个元素(索引从0开始):
sequence[2:3]
[2]
# 跳过索引0和1,从索引2开始每隔一个元素取一个,直到序列末尾:
sequence[2::2]
[2, 4, 6, 8]
print(sequence)
s0 = sequence[2:3]
print(s0)
s0[0] = 20
print(s0, sequence)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[2]
[20] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

2.8.2. 列表对象方法#

列表数据类型支持很多方法,列表对象的所有方法所示如下:

list.append(x)

在列表末尾添加一个元素,相当于 a[len(a):] = [x] 。

list.extend(iterable)

用可迭代对象的元素扩展列表。相当于 a[len(a):] = iterable 。

list.insert(i, x)

在指定位置插入元素。第一个参数是插入元素的索引,因此,a.insert(0, x) 在列表开头插入元素, a.insert(len(a), x) 等同于 a.append(x) 。

list.remove(x)

从列表中删除第一个值为 x 的元素。未找到指定元素时,触发 ValueError 异常。

list.pop([i])

移除列表中给定位置上的条目,并返回该条目。 如果未指定索引号,则 a.pop() 将移除并返回列表中的最后一个条目。 如果列表为空或索引号在列表索引范围之外则会引发 IndexError。

list.clear()

删除列表里的所有元素,相当于 del a[:] 。

list.index(x[, start[, end]])

返回列表中第一个值为 x 的元素的零基索引。未找到指定元素时,触发 ValueError 异常。

可选参数 start 和 end 是切片符号,用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的,而不是 start 参数。

list.count(x)

返回列表中元素 x 出现的次数。

list.sort(*, key=None, reverse=False)

就地排序列表中的元素(要了解自定义排序参数,详见 sorted())。

list.reverse()

翻转列表中的元素。

list.copy()

返回列表的浅拷贝。相当于 a[:] 。

2.8.3. 列表推导式#

语法格式:

[expression for item in iterable if condition]

各部分的意义:

  • expression:对 item 的操作,可以是任意的表达式。

  • item:从 iterable 中依次取出的元素。

  • iterable:一个可迭代对象,比如列表、元组、字符串等。

  • condition:(可选)一个条件表达式,只有满足此条件的 item 才会被包含在最终的列表中。

列表推导式还可以包含多个 for 或 if 子句,例如:

[expression for item1 in iterable1 for item2 in iterable2 if condition]

创建平方值的列表三种方法:

  1. 循环处理

squares = []
for x in range(10):
    squares.append(x**2)
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  1. lambda函数处理

squares = list(map(lambda x: x**2, range(10)))
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
  1. 列表推导式

squares = [x**2 for x in range(10)]
squares
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
squares = [x**2 for x in range(10) if x % 3 == 0]
squares 
[0, 9, 36, 81]
# 带有条件的列表推导式,将一个列表中的正数元素取出来
positive_numbers = [x for x in range(-5, 5) if x > 0]
positive_numbers
[1, 2, 3, 4]
# 嵌套的列表推导式,创建一个矩阵的转置
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
transposed_matrix = [[row[i] for row in matrix] for i in range(len(matrix[0]))]
transposed_matrix
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
# 将两个列表中不相等的元素组合起来:
combs = [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
print(combs)

# 等效于
combs = []
for x in [1,2,3]:
    for y in [3,1,4]:
        if x != y:
            combs.append((x, y))
combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
vec = [-4, -2, 0, 2, 4]
# 对所有元素应用一个函数
[abs(x) for x in vec]
[4, 2, 0, 2, 4]
# 创建一个包含 (数字, 平方) 2 元组的列表
[(x, x**2) for x in range(6)] # 元组必须加圆括号,否则会引发错误
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
# 使用两个 'for' 来展平嵌套的列表
vec = [[1,2,3], [4,5,6], [7,8,9]]
[num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
# 嵌套函数
from math import pi
[str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
# 越界访问时候返回默认值
i  = 3
result = squares[i] if i < len(squares) else -1
print(result)

# 等效
def safe_access(lst, index, default=None):
    try:
        return lst[index]
    except IndexError:
        return default
print(safe_access(squares, 10, -1))
81
-1

2.8.4. del操作#

del 语句用于从列表中移除切片或清空整个列表

a = [-1]
a.extend(range(0, 5))
a
[-1, 0, 1, 2, 3, 4]
del a[0] # 删除第一个元素
a
[0, 1, 2, 3, 4]
del a[2:4] # 删除第三个和第四个元素
a
[0, 1, 4]
del a[:] # 清空列表
a
[]
del a # 删除变量
a # 此时变量是未定义的,再访问时候会报错
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[62], line 2
      1 del a # 删除变量
----> 2 a # 此时变量是未定义的,再访问时候会报错

NameError: name 'a' is not defined
'x' in "xyz"

2.8.5. in / not in#

对于序列类型(list, tuple, range)支持in/not in 判断元素是否存在序列中。参见通用序列操作

a = list(range(0,5))
a
print(1 in a)
print(6 not in a)

2.9. 元组#

元组是 immutable (不可变的),一般可包含异质元素序列,通过解包或索引访问(如果是 namedtuples,可以属性访问)。列表是 mutable (可变的),列表元素一般为同质类型,可迭代访问。

只有一个元素的元组可以通过在这个元素后添加逗号来构建(圆括号里只有一个值的话不够明确)。丑陋,但是有效。

t = 12345, 54321, 'hello!'
t
t[2] # 根据索引访问
# 元组可以嵌套
u = t, (1, 2, 3, 4, 5)
u
# 元组是不可变对象:
t[0] = 88888 # 报错
v = ([1, 2, 3], [3, 2, 1]) # 元组可以包含可变对象
print(v)
v[0][1]=100
v
# 0 个或 1 个元素的元组
empty = () # 空元组
singleton = 'hello',    # <-- 注意末尾的逗号
print(len(empty))
print(len(singleton))
singleton
# 元组解包
x, y, z = t
print(x, y, z)

2.10. 集合#

集合是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。

创建集合用花括号或 set() 函数。注意,创建空集合只能用 set(),不能用 {},{} 创建的是空字典

basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket)                      # 显示重复项已被移除
'orange' in basket # 检查元素是否在集合中
'crabgrass' in basket
a = set("ab", "def")
# 通过列表创建集合
s = set([1, 2, 3, "a"])
s

2.10.1. 集合运算#

a = set('abracadabra')
b = set('alacazam')
a # a 中独有的字母
a - b # 存在于 a 中但不存在于 b 中的字母
a | b  # 存在于 a 或 b 中或两者中皆有的字母
a & b # 同时存在于 a 和 b 中的字母
a ^ b # 存在于 a 或 b 中但非两者中皆有的字母

2.10.2. 推导式#

类似列表推导式,集合也支持推导式

a = {x for x in 'abracadabra' if x not in 'abc'}
a

2.11. 字典#

字典在其他语言中可能会被称为“关联存储”或“关联数组”。

  • 不同于以固定范围的数字进行索引的序列,字典是以 键 进行索引的,键可以是任何不可变类型;

  • 字符串和数字总是可以作为键。 如果一个元组只包含字符串、数字或元组则也可以作为键;

  • 如果一个元组直接或间接地包含了任何可变对象,则不能作为键。

  • 列表不能作为键,因为列表可以使用索引赋值、切片赋值或者 append() 和 extend() 等方法进行原地修改列表。

  • 花括号 {} 用于创建空字典

tel = {'jack': 4098, 'sape': 4139}
tel
tel['guido'] = 4127 # 添加新的键值对
tel
print(tel['jack']) # 访问键,若键不存在,则发生KeyError异常
del tel['sape'] # 删除
tel
tel['irv'] = 4127
print(tel)
list(tel) # 按插入次序排列, 返回该字典中所有键的列表
sorted(tel) # 排序所有键组成的列表

2.11.1. dict 构造函数#

# 直接用键值对序列创建字典
dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
# 关键字参数指定键值创建字典
dict(sape=4139, guido=4127, jack=4098)

2.11.2. 推导式#

{x: x**2 for x in (2, 4, 6)}

2.12. 字典与列表的循环#

2.12.1. 字典执行循环#

当对字典执行循环时,可以使用 items() 方法同时提取键及其对应的值

knights = {'gallahad': 'the pure', 'robin': 'the brave'}
for k, v in knights.items():
    print(k, v)

2.12.2. 序列循环#

Python中常见的序列有:字符串(String)、列表(List)、元组(Tuple)、范围(Range)、集合(Set)、字典(Dict)

在序列中循环时,用 enumerate() 函数可以同时取出位置索引和对应的值:

for i, v in enumerate(['tic', 'tac', 'toe']): # 列表
    print(i, "=", v, end=', ')

print()

for i, v in enumerate("abc中国def"): # 字符串
    print(i, "=", v, end=', ')

print()
for i, v in enumerate(knights): # 字典
    print(i, "=", v, end=', ')

print()
for i, v in enumerate(set('abc')): # 集合
    print(i, "=", v, end=', ')

print()
for i, v in enumerate(range(0, 5)): # 字典
    print(i, "=", v, end=', ')

print()
for i, v in enumerate((12345, 54321, 'hello!')): # 元组
    print(i, "=", v, end=', ')

2.12.3. zip打包后循环#

Python 中的 zip() 函数用于将多个可迭代对象(如列表、元组、字符串等)打包成一个元组的迭代器,然后可以用于创建字典、列表或其他数据结构。

zip(iterable, ..., ...)
  • iterable:一个或多个可迭代对象。

zip() 函数返回一个迭代器,该迭代器生成元组,每个元组包含输入的每个可迭代对象的下一个元素。当最短的可迭代对象耗尽时,迭代结束。

# zip示例1

# 列表
list1 = [1, 2, 3, 4] # 4元素是多余的,不会迭代出来
list2 = ['a', 'b', 'c']
# 使用 zip 函数
zipped = zip(list1, list2)
# 将 zip 对象转换为列表
zipped_list = list(zipped)
print(zipped_list)  # 输出: [(1, 'a'), (2, 'b'), (3, 'c')]
# zip示例2: 创建字典
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'New York']

# 使用 zip 和 dict 函数创建字典
my_dict = dict(zip(keys, values))

print(my_dict)  # 输出: {'name': 'Alice', 'age': 25, 'city': 'New York'}
# zip示例3: 打包后循环
questions = ['name', 'quest', 'favorite color']
answers = ['lancelot', 'the holy grail', 'blue']

for q, a in zip(questions, answers):
    print('What is your {0}?  It is {1}.'.format(q, a))

2.12.4. 逆向循环#

for i in reversed(range(1, 10, 2)):
    print(i, end=", ")

2.12.5. 排序后再循环#

basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
for i in sorted(basket): # sorted不会更改原始序列
    print(i, end=", ")

2.12.6. 去重后再循环#

basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']

for f in sorted(set(basket)):
    print(f, end=", ")

2.13. 序列和其他类型的比较#

  • 序列对象可以与相同序列类型的其他对象比较。这种比较使用 字典式 顺序:首先,比较前两个对应元素,如果不相等,则可确定比较结果;如果相等,则比较之后的两个元素,以此类推,直到其中一个序列结束。

  • 如果要比较的两个元素本身是相同类型的序列,则递归地执行字典式顺序比较。如果两个序列中所有的对应元素都相等,则两个序列相等。

  • 如果一个序列是另一个的初始子序列,则较短的序列可被视为较小(较少)的序列。 对于字符串来说,字典式顺序使用 Unicode 码位序号排序单个字符。

注意: 当比较不同类型的对象时,只要待比较的对象提供了合适的比较方法,就可以使用 < 和 > 进行比较。例如,混合的数字类型通过数字值进行比较,所以,0 等于 0.0,等等。如果没有提供合适的比较方法,解释器不会随便给出一个比较结果,而是引发 TypeError 异常。

(1, 2, 3) < (1, 2, 4)
[1, 2, 3] < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)  < (1, 2, 4)
(1, 2)   < (1, 2, -1)
(1, 2, 3)   == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))  < (1, 2, ('abc', 'a'), 4)