3. 变量与基础数据类型
在第 2 章中,你已经学会了如何编写简单的程序来打印文本、接收输入并执行基础运算。不过,这些程序有一个显著的局限:它们不能将信息保存起来以供之后使用,也不能以更复杂的方式处理不同种类的数据。本章你将学习 Python 如何通过变量 (variable) 和数据类型 (data type) 来存储和管理信息。
可以把变量想象成带标签的容器,用来保存信息。就像你在家里用贴有标签的盒子来整理物品一样,Python 使用变量在程序中组织和管理数据。但并非所有数据都是同一种形式——数字和文本在本质上是不同的,Python 需要知道自己正在处理的是什么类型的数据。这就是数据类型 (data type) 的作用所在。
读完本章后,你将理解:
- 如何正确创建和命名变量
- 什么是数据类型,以及它们为什么重要
- 如何处理数字、文本和逻辑值
- 如何在不同数据类型之间进行转换
- Python 如何在内部表示数据
这些概念是你在 Python 中进行任何操作的基础,因此我们会通过大量的实践示例深入讲解它们。
3.1) 变量的命名与创建
3.1.1) 什么是变量以及它们为什么重要
变量 (variable) 是一个名字,用来引用存储在计算机内存中的某个值。当你创建一个变量时,本质上就是创建了一个标签,它指向某个数据。这使你能够:
- 存储信息,以便在程序后续使用
- 多次引用同一个值 而不用重复输入
- 在程序运行过程中更新值
- 让代码更易读,通过有意义的名字替代原始值
来看一个具体的例子。如果不用变量,要计算一个长方形的面积,你可能会写:
# without_variables.py
print("Rectangle area:", 15 * 8)这当然能运行,但如果你需要多次使用这些尺寸怎么办?而且 15 和 8 分别代表什么?使用变量后,代码会更清晰、更灵活:
# with_variables.py
length = 15
width = 8
area = length * width
print("Rectangle area:", area) # Output: Rectangle area: 120现在代码本身就是文档——任何阅读它的人都能立刻明白,我们是在计算一个给定长宽的长方形面积。如果需要改变尺寸,只需修改顶部的变量赋值即可。
3.1.2) 使用赋值语句创建变量
在 Python 中,你通过赋值运算符 (assignment operator)(=)来创建变量。基本语法是:
variable_name = value这里的 = 并不是数学意义上的“相等”,而是“把右侧的值赋给左侧的名字”。这一区别非常关键。当 Python 看到这条语句时,它会:
- 计算
=右侧表达式的值 - 创建或更新左侧的变量名
- 让该变量名引用这个结果值
下面是几个创建变量的例子:
# creating_variables.py
age = 25
temperature = 72.5
name = "Alice"
is_student = True
print(age) # Output: 25
print(temperature) # Output: 72.5
print(name) # Output: Alice
print(is_student) # Output: True注意,我们不需要事先声明每个变量要保存哪种类型的数据——Python 会根据你赋给它的值自动推断。这被称为动态类型 (dynamic typing),也是让 Python 非常灵活、易用的原因之一。
你还可以使用多重赋值 (multiple assignment) 在一行中创建多个变量:
# multiple_assignment.py
x, y, z = 10, 20, 30
print(x) # Output: 10
print(y) # Output: 20
print(z) # Output: 30
# You can even assign the same value to multiple variables
a = b = c = 100
print(a, b, c) # Output: 100 100 100第一种形式(称为元组解包 (tuple unpacking)——我们会在第 14 章学习元组)会按顺序将每个值赋给对应的变量。第二种形式会把同一个值赋给三个变量。
3.1.3) 变量命名规则与约定
Python 对什么是合法的变量名有明确规定。其中一部分是硬性规则(必须遵守),另一部分是约定俗成(为了可读性和与社区代码保持一致,强烈建议遵守)。
硬性规则(必须遵守):
- 必须以字母 (a-z, A-Z) 或下划线 (_ ) 开头:变量名不能以数字开头。
- 可以包含字母、数字和下划线:从第二个字符开始,可以使用字母、数字和下划线的任意组合。
- 不能包含空格或特殊字符:空格、连字符以及大多数标点符号都不允许。
- 不能是 Python 关键字:Python 保留了一些单词供自身使用(例如
if、for、while、def等)。 - 区分大小写:
age、Age和AGE是三个不同的变量。
下面是合法和不合法变量名的例子:
# valid_names.py
# Valid variable names
user_age = 30
firstName = "John"
total_2024 = 1000
_private = "hidden"
x = 5
MAX_SIZE = 100
# Invalid variable names (these will cause errors)
# 2nd_place = "silver" # Error: starts with digit
# user-name = "alice" # Error: contains hyphen
# total amount = 500 # Error: contains space
# class = "Python 101" # Error: 'class' is a keyword约定(为可读性“应该”遵守):
-
普通变量使用小写加下划线:这种风格叫做
snake_case,是 Python 的标准写法。pythonuser_age = 25 total_price = 99.99 is_valid = True -
常量使用全大写:即在程序执行期间不应改变的值。
pythonMAX_ATTEMPTS = 3 PI = 3.14159 DEFAULT_COLOR = "blue" -
使用有描述性的名字:变量名应该清楚地表明它表示的是什么。
python# Good: clear and descriptive student_count = 30 average_temperature = 72.5 # Poor: unclear abbreviations sc = 30 avg_tmp = 72.5 # Poor: too generic x = 30 data = 72.5 -
除特定场景外,避免单字母变量名:
i、j、k这样的名字适用于循环计数器(loop counter,我们会在第 11 章学习循环 (loop)),x、y、z适用于坐标。其他情况下请使用更有意义的名字。 -
不要使用会遮蔽内置函数的名字:虽然 Python 允许,但不要用
list、str、int、print等内置函数名作为变量名。
来看一个实践例子,展示良好的命名习惯:
# good_naming.py
# Constants at the top
SALES_TAX_RATE = 0.08
DISCOUNT_THRESHOLD = 100
# Descriptive variable names
item_price = 75.00
quantity = 3
subtotal = item_price * quantity
# Clear boolean variable
qualifies_for_discount = subtotal >= DISCOUNT_THRESHOLD
if qualifies_for_discount:
discount = subtotal * 0.10
subtotal = subtotal - discount
print("Discount applied: $", discount)
tax = subtotal * SALES_TAX_RATE
total = subtotal + tax
print("Subtotal: $", subtotal)
print("Tax: $", tax)
print("Total: $", total)
# Output:
# Subtotal: $ 202.5
# Tax: $ 16.2
# Total: $ 218.7注意变量名如何让代码“自带文档”。即使没有注释,你也能大致理解程序在计算什么。
3.1.4) 在表达式中使用变量
一旦你创建了变量,就可以在任何本来可以使用该值的地方使用它。Python 在求值表达式时会自动使用变量当前的值:
# using_variables.py
# Create some variables
hours_worked = 40
hourly_rate = 25.50
# Use variables in calculations
gross_pay = hours_worked * hourly_rate
print("Gross pay:", gross_pay) # Output: Gross pay: 1020.0
# Use variables in other expressions
bonus = gross_pay * 0.10
total_pay = gross_pay + bonus
print("Total with bonus:", total_pay) # Output: Total with bonus: 1122.0
# Use variables in strings (we'll learn more about this in Chapter 5)
message = "You worked " + str(hours_worked) + " hours"
print(message) # Output: You worked 40 hours变量也可以和你在第 2 章学过的函数搭配使用:
# variables_with_functions.py
name = input("What is your name? ")
age = input("What is your age? ")
greeting = "Hello, " + name + "!"
print(greeting)
age_next_year = int(age) + 1
print("Next year you will be", age_next_year, "years old.")
# If user enters "Alice" and "25":
# Output: Hello, Alice!
# Output: Next year you will be 26 years old.这个例子还展示了一个重要概念:input() 函数总是返回文本(一个字符串 (string)),所以如果你想对其进行数学运算,必须先把它转换成数字。我们会在本章稍后(3.7 小节)详细介绍这种转换过程。
3.2) 变量的赋值与重新赋值
3.2.1) 理解赋值
当你给变量赋值时,Python 会在变量名和内存中的某个值之间创建一条联系。重要的是要明白,变量并不是以“物理方式”装着这个值——而是一个名字,用来引用或指向存储在内存中某处的值。
可以把它类比成一张写着名字的便利贴,贴在装着实际值的盒子上。变量(便利贴)本身不是值;它只是指向装有该值的盒子。当你给变量重新赋值时,你是在把便利贴移到另一只盒子上,而不是改变原来盒子的内容。
这一点在讨论重新赋值时就显得很重要了。来看一个关于赋值和重新赋值的例子:
# assignment_basics.py
x = 10
print(x) # Output: 10
# Reassign x to a new value
x = 20
print(x) # Output: 20
# The old value (10) is gone; x now refers to 20逐步发生的事情:
x = 10:Python 在内存中创建整数值 10,并让名字x引用它print(x):Python 查找x引用的值(10),并打印出来x = 20:Python 在内存中创建整数值 20,并让x改为引用这个新值print(x):Python 查找x引用的值(现在是 20),并打印出来
原来的值 10 会在内存中暂时保留,但由于已经没有变量再引用它,Python 的自动内存管理(称为 garbage collection)最终会把它清理掉。
3.2.2) 重新赋值与更新变量
编程中最常见的模式之一,就是基于变量当前的值对其进行更新。例如,你可能想增加一个计数器、累加总和,或者根据某个计算结果修改值:
# updating_variables.py
score = 0
print("Initial score:", score) # Output: Initial score: 0
# Add 10 points
score = score + 10
print("After first update:", score) # Output: After first update: 10
# Add 5 more points
score = score + 5
print("After second update:", score) # Output: After second update: 15
# Double the score
score = score * 2
print("After doubling:", score) # Output: After doubling: 30我们来拆解 score = score + 10 中发生了什么:
- Python 先计算右侧:
score + 10 - 查找
score当前的值(0) - 计算 0 + 10,得到 10
- 将这个新值 10 赋给
score - 此时
score不再引用 0,而是引用 10
这种模式如此常见,以至于 Python 提供了简写运算符,我们会在第 4 章(4.3 小节)学习。不过现在,更重要的是理解这种完整写法,因为它清晰地展示了操作顺序:先完整计算右侧表达式,再把结果赋给左侧变量名。
3.2.3) 变量是相互独立的
当你把一个变量赋给另一个变量时,Python 拷贝的是“引用”,而不是值本身(对数字和字符串等简单类型来说尤其如此)。不过,就本章学习的这些基础类型而言,变量在赋值之后的行为是相互独立的:
# independent_variables.py
a = 10
b = a # b now refers to the same value as a (10)
print("a:", a) # Output: a: 10
print("b:", b) # Output: b: 10
# Change a
a = 20
print("After changing a:")
print("a:", a) # Output: a: 20
print("b:", b) # Output: b: 10 (unchanged!)当我们执行 b = a 时,让 b 引用了当时 a 所引用的值(10)。当后来把 a 改为引用 20 时,b 没有受到影响——它依然引用 10。
对数字和字符串来说,这种行为很直观,但在列表 (list)、字典等集合类型上就会复杂一些。我们会在第 17 章详细探讨 Python 的对象模型和引用语义。
3.2.4) 在赋值前使用变量
初学者常犯的一个错误是:在变量还没赋值之前就尝试使用它。Python 遇到这种情况会抛出 NameError:
# undefined_variable.py
print(total) # Error: NameError: name 'total' is not defined这个错误是因为 Python 不知道 total 指的是什么——你还没创建它。修复方法很简单:在使用变量之前先给它赋一个初始值:
# defined_variable.py
total = 0 # Initialize the variable first
print(total) # Output: 0
# Now we can update it
total = total + 10
print(total) # Output: 10这种在使用变量之前先初始化 (initialize) 它的模式,在编程中非常基础。你会在全书中反复看到这种写法,尤其是在循环 (loop) 中处理计数器和累加器时(第 10 章)。
3.2.5) 交换变量的值
一个常见操作是交换两个变量的值。在很多编程语言中,你需要借助一个临时变量:
# swap_with_temp.py
x = 10
y = 20
print("Before swap: x =", x, ", y =", y) # Output: Before swap: x = 10 , y = 20
# Swap using a temporary variable
temp = x # Save x's value
x = y # Put y's value in x
y = temp # Put saved value in y
print("After swap: x =", x, ", y =", y) # Output: After swap: x = 20 , y = 10不过,Python 提供了一种更优雅的方式,使用同时赋值 (simultaneous assignment):
# swap_pythonic.py
x = 10
y = 20
print("Before swap: x =", x, ", y =", y) # Output: Before swap: x = 10 , y = 20
# Swap in one line
x, y = y, x
print("After swap: x =", x, ", y =", y) # Output: After swap: x = 20 , y = 10这之所以可行,是因为 Python 会在进行任何赋值操作之前,先完整计算右侧的 (y, x)。它会创建一个临时结构保存 y 和 x 的当前值,然后再把这些值依次解包赋给 x 和 y。这很好地体现了 Python 的哲学:让常见操作既简单又可读。
3.3) 数据类型的概念与 type()
3.3.1) 什么是数据类型?
数据类型 (data type)(通常简称 type)定义了某个值属于哪一类数据,以及可以对它执行哪些操作。你可以把数据类型看作一个类别,用来告诉 Python 应该如何理解和处理不同种类的信息。
例如:
- 数字
42是一个整数 (integer)(整型) - 数字
3.14是一个浮点数 (floating-point number)(带小数点的数) - 文本
"Hello"是一个字符串 (string)(字符序列) - 值
True是一个布尔值 (boolean)(逻辑真/假值)
为什么数据类型很重要?因为不同类型支持的操作不同:
# type_operations.py
# You can add numbers
result1 = 10 + 5
print(result1) # Output: 15
# You can also "add" strings (concatenation)
result2 = "Hello" + " " + "World"
print(result2) # Output: Hello World
# But you can't add a number and a string directly
# result3 = 10 + "5" # Error: TypeError: unsupported operand type(s)最后一行出错,是因为 Python 不知道如何把一个数字和一个字符串相加——它们本质上是不同类型的数据。它应该把数字转成文本后拼接吗?还是应该把文本转换成数字后做数学加法?Python 要求你明确表达自己的意图,通过手动把其中一个值转换成与另一个相同的类型(我们会在 3.7 小节学习如何做)。
3.3.2) 使用 type() 检查数据类型
Python 提供了一个内置函数 type(),用来查看某个值或变量的类型。这在理解数据和排查错误时非常有用:
# checking_types.py
# Check types of literal values
print(type(42)) # Output: <class 'int'>
print(type(3.14)) # Output: <class 'float'>
print(type("Hello")) # Output: <class 'str'>
print(type(True)) # Output: <class 'bool'>
# Check types of variables
age = 25
name = "Alice"
temperature = 98.6
is_valid = False
print(type(age)) # Output: <class 'int'>
print(type(name)) # Output: <class 'str'>
print(type(temperature)) # Output: <class 'float'>
print(type(is_valid)) # Output: <class 'bool'>输出中的 <class 'int'> 表示“这个值属于名为 int 的类 (class/type)”。在 Python 中,类型实际上就是类(我们会在本书第 VIII 部分详细学习类),目前你可以认为这两个词在这里是等价的。
3.3.3) Python 的动态类型
Python 是一种动态类型 (dynamic typing) 语言,这意味着:
- 变量本身没有固定类型:同一个变量在程序运行的不同时刻可以引用不同类型的值
- 类型在运行时确定:Python 在程序执行时才确定类型,而不是在你写代码的阶段
- 你不需要显式声明类型:不像某些语言,你不用写“这个变量将保存一个整数”
下面是展示动态类型的一个例子:
# dynamic_typing.py
x = 42
print(x, "is of type", type(x)) # Output: 42 is of type <class 'int'>
x = "Hello"
print(x, "is of type", type(x)) # Output: Hello is of type <class 'str'>
x = 3.14
print(x, "is of type", type(x)) # Output: 3.14 is of type <class 'float'>注意 x 在程序的不同阶段可以引用不同类型的值。Python 并不会报错——它只是更新 x 当前所引用值的类型。
虽然这种灵活性很方便,但也意味着你需要更加小心。如果你以为某个变量保存的是数字,而它实际上保存的是字符串,就可能遇到奇怪的错误:
# type_confusion.py
value = "100" # This is a string, not a number!
# Trying to do math with it will fail
# result = value + 50 # Error: TypeError: can only concatenate str to str
# You need to convert it first
result = int(value) + 50
print(result) # Output: 150这也是为什么理解数据类型非常重要——它能帮助你预测哪些操作是可行的,并在错误发生之前就发现问题。
3.3.4) Python 内置类型总览
Python 拥有多种内置数据类型。本章我们会专注于最基础的一些:
在本章中,我们会讲解:
- int:整数(整型)
- float:浮点数(带小数点的数)
- str:字符串(文本)
- bool:布尔值(True 和 False)
- NoneType:特殊值
None
我们会简单提一下复数 (complex)(带有虚部的数),不过在日常编程中并不常用。至于集合类型 (collection types)(如列表 (list)、元组 (tuple)、字典 (dict) 和集合 (set)),它们非常重要,因此在本书第 IV 部分会用单独章节来讲。
3.3.5) 实战中的类型检查
在处理用户输入或外部数据时,理解类型尤为重要。记住:input() 总是返回字符串,即使用户输入的是数字:
# input_types.py
user_input = input("Enter a number: ")
print("You entered:", user_input)
print("Type:", type(user_input)) # Output: Type: <class 'str'>
# Even if user types "42", it's still a string!
# To use it as a number, convert it:
number = int(user_input)
print("As a number:", number)
print("Type:", type(number)) # Output: Type: <class 'int'>
# Now we can do math with it
doubled = number * 2
print("Doubled:", doubled)当你运行这个程序并输入 42 时,会看到:
Enter a number: 42
You entered: 42
Type: <class 'str'>
As a number: 42
Type: <class 'int'>
Doubled: 84这说明了一个关键点:字符串 "42" 和整数 42 在 Python 中是不同的东西,尽管打印出来的样子很像。字符串是两个字符('4' 和 '2')组成的序列,而整数是一个可以参与数学运算的数值。
3.4) 整数与浮点数
3.4.1) 整数 (int)
整数 (integer)(类型 int)是没有小数点的整数值。整数可以是正数、负数或零。在 Python 3 中,整数可以任意大——唯一的限制是你计算机的可用内存。
# integers.py
# Positive integers
age = 25
year = 2024
population = 8000000000
# Negative integers
temperature = -15
debt = -5000
# Zero
balance = 0
# Very large integers (Python handles these easily)
huge_number = 123456789012345678901234567890
print(huge_number) # Output: 123456789012345678901234567890
print(type(huge_number)) # Output: <class 'int'>Python 允许你在大数字中使用下划线以提高可读性。下划线对 Python 来说会被忽略,但对人类阅读很有帮助:
# readable_numbers.py
# These are all the same number
million = 1000000
million = 1_000_000 # Much easier to read!
# Works with any size
population = 8_000_000_000
national_debt = 31_000_000_000_000
print(million) # Output: 1000000 (underscores not shown in output)
print(population) # Output: 8000000000你也可以使用特殊前缀以不同的进制书写整数:
# number_bases.py
# Binary (base 2) - prefix 0b
binary = 0b1010 # This is 10 in decimal
print(binary) # Output: 10
# Octal (base 8) - prefix 0o
octal = 0o12 # This is 10 in decimal
print(octal) # Output: 10
# Hexadecimal (base 16) - prefix 0x
hexadecimal = 0xFF # This is 255 in decimal
print(hexadecimal) # Output: 255这些进制形式在某些场景下很有用(例如处理网页颜色或底层编程),但在大多数日常编程中,你会使用十进制整数。
3.4.2) 浮点数 (float)
浮点数 (floating-point number)(类型 float)是带小数点的数字。浮点数用于表示实数,即不一定是整数的值:
# floats.py
# Numbers with decimal points
price = 19.99
temperature = 98.6
pi = 3.14159
# Very small numbers
electron_mass = 0.00000000000000000000000000000091093837
# Very large numbers
speed_of_light = 299792458.0
print(price) # Output: 19.99
print(temperature) # Output: 98.6
print(electron_mass) # Output: 9.1093837e-31 (scientific notation)
print(speed_of_light) # Output: 299792458.0注意,非常小或非常大的浮点数会以科学计数法 (scientific notation)(也叫指数表示法)显示。比如 9.1093837e-31 就表示 “9.1093837 × 10⁻³¹”,也就是“9.1093837 除以 10 的 31 次方”。
你也可以直接用科学计数法书写浮点数:
# scientific_notation.py
# These are equivalent
avogadro = 602214076000000000000000.0
avogadro = 6.02214076e23 # Much more readable!
# Small numbers
planck = 0.000000000000000000000000000000000662607015
planck = 6.62607015e-34 # Much more readable!
print(avogadro) # Output: 6.02214076e+23
print(planck) # Output: 6.62607015e-34这里的 e(或 E)表示“指数 (exponent)”。e 后面的数字表示小数点要移动的位数(正数向右移,负数向左移)。
3.4.3) 整数 vs 浮点数:关键差异
虽然整数和浮点数都表示数字,但它们之间有一些重要区别:
1. 精度与表示方式:
整数是精确的——值 42 就是精确的 42,没有近似的问题。而浮点数由于是用二进制来表示十进制数,因此往往是近似值:
# float_precision.py
# This might surprise you!
result = 0.1 + 0.2
print(result) # Output: 0.30000000000000004
# The result isn't exactly 0.3 due to floating-point representation
print(result == 0.3) # Output: False这不是 bug,而是计算机表示十进制小数的固有限制。我们会在第 4 章(4.10 小节)更详细讨论这个问题,现在只需记住:浮点数计算可能并不完全精确。
2. 运算与结果类型:
当你对整数和浮点数进行运算时,Python 遵循一些规则:
# int_float_operations.py
# Integer operations
int_result = 10 + 5
print(int_result, type(int_result)) # Output: 15 <class 'int'>
# Float operations
float_result = 10.0 + 5.0
print(float_result, type(float_result)) # Output: 15.0 <class 'float'>
# Mixed operations: result is always float
mixed_result = 10 + 5.0
print(mixed_result, type(mixed_result)) # Output: 15.0 <class 'float'>
# Division always returns float, even with integers
division_result = 10 / 5
print(division_result, type(division_result)) # Output: 2.0 <class 'float'>关键规则是:只要运算中有一个操作数是 float,结果就会是 float。这很合理,因为浮点数能表示更广泛的值(包括非整数),所以 Python 会把结果“提升”到更通用的类型。
3. 内存和性能:
整数通常比浮点数占用更少内存,运算速度也更快。对大多数程序而言,这点差异可以忽略不计,但在性能要求极高或处理巨大数据集的场景中就可能值得关注。
3.4.4) 何时使用 int,何时使用 float
下面是选择整数或浮点数的一些实用建议:
适合用整数的场景:
- 计数离散的事物(人数、物品数量、迭代次数)
- 表示不能分割的精确数量(学生人数、点击次数)
- 作为序列的索引或位置
- 需要完全精确的算术运算
# use_integers.py
student_count = 30 # Can't have 30.5 students
page_number = 42 # Can't be on page 42.7
loop_counter = 0 # Counting iterations适合用浮点数的场景:
- 表示测量值(温度、距离、重量)
- 处理金额(需要注意精度问题——我们将在第 4 章讨论)
- 计算比例、百分比或平均值
- 可以接受近似值
# use_floats.py
temperature = 72.5 # Temperature can be fractional
price = 19.99 # Money amounts have cents
average_score = 87.3 # Averages are often fractional
percentage = 0.15 # 15% as a decimal3.5) 字符串与布尔字面量(快速预览)
本节我们将快速介绍另外两种基础数据类型:字符串和布尔值。我们会在第 5、6 章中深入讲解字符串,在第 7 章详细讲解布尔值。但要编写哪怕是最简单的程序,现在你也需要对它们有基本了解。
3.5.1) 字符串基础
字符串 (string)(类型 str)是一串字符序列,本质上就是文本。通过在文本两侧加上引号即可创建字符串。Python 接受单引号 (') 或双引号 ("):
# string_basics.py
# Single quotes
name = 'Alice'
message = 'Hello, World!'
# Double quotes (exactly equivalent)
name = "Alice"
message = "Hello, World!"
# Print them
print(name) # Output: Alice
print(message) # Output: Hello, World!
print(type(name)) # Output: <class 'str'>单引号和双引号在功能上完全等价——使用哪种取决于你的喜好,但在同一份代码中最好保持一致。很多 Python 程序员更偏向双引号,因为其他语言里更常见,但单引号同样完全合法。
为什么要有两种引号? 主要是为了在字符串中包含引号时更方便:
# quotes_in_strings.py
# Use double quotes when string contains single quotes
sentence = "It's a beautiful day!"
print(sentence) # Output: It's a beautiful day!
# Use single quotes when string contains double quotes
speech = 'She said, "Hello!"'
print(speech) # Output: She said, "Hello!"
# Or use escape sequences (we'll learn more in Chapter 5)
sentence = 'It\'s a beautiful day!' # \' means a literal single quote
speech = "She said, \"Hello!\"" # \" means a literal double quote字符串可以是空的(不包含任何字符):
# empty_string.py
empty = ""
also_empty = ''
print(empty) # Output: (nothing—it's empty!)
print(len(empty)) # Output: 0 (we'll learn about len() later)
print(type(empty)) # Output: <class 'str'>你可以使用 + 运算符连接字符串(称为拼接 (concatenation)):
# string_concatenation.py
first_name = "John"
last_name = "Doe"
full_name = first_name + " " + last_name
print(full_name) # Output: John Doe
# Be careful: you can't concatenate strings and numbers directly
age = 25
# message = "I am " + age + " years old" # Error: TypeError
# Convert the number to a string first
message = "I am " + str(age) + " years old"
print(message) # Output: I am 25 years old我们会在第 5、6 章更深入学习字符串,包括转义序列、字符串方法、格式化和文本处理。现在只需要记住:字符串表示文本,并通过加引号来创建。
3.5.2) 布尔值基础
布尔值 (boolean)(类型 bool)是逻辑值,只能是 True 或 False。这两个是 Python 中仅有的布尔值,用来在逻辑运算中表示“真”和“假”:
# boolean_basics.py
# Boolean literals
is_sunny = True
is_raining = False
print(is_sunny) # Output: True
print(is_raining) # Output: False
print(type(is_sunny)) # Output: <class 'bool'>重要: 布尔值 True 和 False 必须严格按此大小写书写。Python 区分大小写,因此 true、TRUE、false、FALSE 都不能使用:
# boolean_case.py
correct = True # Correct
# wrong = true # Error: NameError: name 'true' is not defined
# wrong = TRUE # Error: NameError: name 'TRUE' is not defined布尔值通常来自比较 (comparison) 或逻辑运算:
# boolean_from_comparisons.py
age = 25
# Comparison operators produce boolean results
is_adult = age >= 18
print(is_adult) # Output: True
is_senior = age >= 65
print(is_senior) # Output: False
# You can use booleans in conditions (we'll learn more in Chapter 8)
if is_adult:
print("You can vote!") # Output: You can vote!
if is_senior:
print("You get a senior discount!") # (no output—condition is False)常见的比较运算符会产生布尔结果:
==:等于!=:不等于<:小于>:大于<=:小于或等于>=:大于或等于
# comparison_operators.py
x = 10
y = 20
print(x == y) # Output: False (10 is not equal to 20)
print(x != y) # Output: True (10 is not equal to 20)
print(x < y) # Output: True (10 is less than 20)
print(x > y) # Output: False (10 is not greater than 20)
print(x <= 10) # Output: True (10 is less than or equal to 10)
print(y >= 20) # Output: True (20 is greater than or equal to 20)重要提示: 不要把 =(赋值)和 ==(比较)搞混:
x = 10表示“把值 10 赋给 x”x == 10表示“检查 x 是否等于 10”(产生 True 或 False)
# assignment_vs_comparison.py
x = 10 # Assignment: x now refers to 10
result = (x == 10) # Comparison: is x equal to 10?
print(result) # Output: True
# This is a common mistake for beginners:
# if x = 10: # Error: SyntaxError (can't use assignment in if condition)
if x == 10: # Correct: comparison
print("x is 10") # Output: x is 10我们会在第 7 章详细学习布尔值,包括逻辑运算符 and、or、not,真值与假值,以及 Python 如何在条件中使用布尔值。现在你只需记住:布尔值表示真/假,通常由比较运算产生。
3.5.3) 字符串和布尔值在上下文中的配合
下面这个简单的例子展示了字符串和布尔值如何配合使用:
# strings_booleans_example.py
# Get user input
name = input("What is your name? ")
age_input = input("What is your age? ")
# Convert age to integer
age = int(age_input)
# Create boolean conditions
is_adult = age >= 18
is_child = age < 13
# Build messages using strings
if is_adult:
status = "adult"
else:
status = "minor"
# Combine everything in output
print("Hello, " + name + "!")
print("You are an " + status + ".")
if is_child:
print("You are a child.")
# When user enters "Alice" and "16":
# Output: Hello, Alice!
# Output: You are a minor.这个例子展示了多种数据类型如何协同工作:字符串用于文本,整数用于数字,布尔值用于逻辑判断。不同类型之间的这种配合,是所有编程的基础。
3.6) None 值及其用途
3.6.1) 什么是 None?
Python 有一个特殊值 None(类型为 NoneType),用来表示“没有值”或“值缺失”。它是 Python 表达“这里什么都没有”或“暂时还没有值”的方式。
# none_basics.py
result = None
print(result) # Output: None
print(type(result)) # Output: <class 'NoneType'>None 不等同于 0、空字符串或 False——它是一个独立的值,专门用来表示“没有任何值”:
# none_vs_others.py
x = None
y = 0
z = ""
w = False
print(x) # Output: None
print(y) # Output: 0
print(z) # Output: (empty—nothing prints)
print(w) # Output: False
# They're all different types
print(type(x)) # Output: <class 'NoneType'>
print(type(y)) # Output: <class 'int'>
print(type(z)) # Output: <class 'str'>
print(type(w)) # Output: <class 'bool'>3.6.2) None 会在什么时候出现
None 在多种常见场景中会出现:
1. 作为将来要赋值变量的占位符:
# none_placeholder.py
# Initialize variables that will be assigned later
user_name = None
user_age = None
# Later in the program, after getting user input:
user_name = input("Enter your name: ")
user_age = int(input("Enter your age: "))
print("Name:", user_name)
print("Age:", user_age)2. 作为不显式返回值的函数的默认返回值:
我们将在第 19 章学习函数 (function)。函数可以返回值。如果一个函数没有显式使用 return 返回任何东西,Python 会自动返回 None:
# none_from_function.py
# The print() function returns None
result = print("Hello!") # Output: Hello!
print(result) # Output: None
print(type(result)) # Output: <class 'NoneType'>这看起来有点奇怪,但很实用。这意味着每个函数都会返回一些东西,即便那个“东西”是“没有有意义的值”。
3. 表示可选或缺失的数据:
# none_optional.py
# Representing optional middle name
first_name = "John"
middle_name = None # No middle name
last_name = "Doe"
if middle_name is None:
full_name = first_name + " " + last_name
else:
full_name = first_name + " " + middle_name + " " + last_name
print(full_name) # Output: John Doe3.6.3) 检查是否为 None
检查某个值是否为 None 时,要使用 is 运算符(而不是 ==):
# checking_none.py
value = None
# Correct way: use 'is'
if value is None:
print("Value is None") # Output: Value is None
# Also correct: use 'is not'
if value is not None:
print("Value has a value")
else:
print("Value is None") # Output: Value is None
# While == works, 'is' is preferred for None
if value == None: # This works but is not idiomatic Python
print("Value is None") # Output: Value is None为什么用 is 而不是 ==?is 用来检查两个名字是否引用同一个内存对象(我们会在第 17 章进一步讨论)。由于 Python 中 None 只有一个唯一对象实例,使用 is 来检查既高效又精确。这也是标准的 Python 写法。
3.6.4) None 的实际应用
下面是一个实际例子,展示 None 在真实程序中的用法:
# none_practical.py
# Simulate looking up a user's age
# (In real programs, we'd use functions and dictionaries from later chapters)
user_name = input("Enter a name (Alice, Bob, or Charlie): ")
# Check each name and assign age or None
if user_name == "Alice":
user_age = 25
elif user_name == "Bob":
user_age = 30
elif user_name == "Charlie":
user_age = 35
else:
user_age = None # User not found
# Check if we found the user
if user_age is not None:
print(user_name, "is", user_age, "years old")
else:
print(user_name, "not found")
# When user enters "Alice":
# Output: Alice is 25 years old
# When user enters "David":
# Output: David not found在这个例子中,None 清楚地表示“未找到该用户”,这和找到了一个年龄为 0 的用户(这对新生儿来说是完全合法的年龄)是不同的含义。
3.6.5) None vs 空值
区分 None 和空值非常重要:
# none_vs_empty.py
# These are all different
nothing = None
zero = 0
empty_string = ""
# Note: We'll learn about lists in Chapter 13
# empty_list = []
print(nothing is None) # Output: True
print(zero is None) # Output: False
print(empty_string is None) # Output: False
# None means "no value"
# 0 means "the number zero"
# "" means "text with no characters"每一个都代表不同的概念:
None:没有任何值0:具体的数值 0"":存在的字符串,只是里面没有字符
理解这几点差别有助于你编写更清晰、更正确的程序。
3.7) 使用 int()、float() 和 str() 进行基础类型转换
3.7.1) 为什么需要类型转换
如前所述,Python 对不同类型能执行哪些操作有严格规定。你不能把数字和字符串直接相加,不能用浮点数去乘一个字符串,也不能对文本做数学运算。当你需要以另一种类型来使用一个值时,必须显式地对其进行转换 (conversion)。
类型转换(也叫类型强制转换 (type casting))指的是将一个值从一种类型变为另一种类型。Python 提供了一些最常用的内置转换函数:
int():转换为整数float():转换为浮点数str():转换为字符串
下面我们来逐一讲解。
3.7.2) 使用 int() 转换为整数
int() 函数可以将一个值转换为整数。主要有这些用法:
将浮点数转换为整数:
# float_to_int.py
# int() truncates (cuts off) the decimal part
x = int(3.14)
y = int(3.99)
z = int(-2.7)
print(x) # Output: 3 (not 4—it doesn't round!)
print(y) # Output: 3 (not 4—it truncates!)
print(z) # Output: -2 (truncates toward zero)重要: int() 不会进行四舍五入,而是简单地去掉小数部分(向零截断)。很多初学者会误以为 int(3.9) 会得到 4,这是常见误解。
将字符串转换为整数:
在处理用户输入时,这一点非常常见:
# string_to_int.py
# Convert string containing a number
age_str = "25"
age_int = int(age_str)
print(age_str, type(age_str)) # Output: 25 <class 'str'>
print(age_int, type(age_int)) # Output: 25 <class 'int'>
# Now we can do math with it
next_year = age_int + 1
print("Next year:", next_year) # Output: Next year: 26
# Practical example with input()
user_age = int(input("Enter your age: "))
print("In 10 years, you'll be", user_age + 10)转换非法字符串会怎样?
如果你试图把一个不能表示整数的字符串转换为整数,Python 会抛出 ValueError:
# invalid_int_conversion.py
# These work
print(int("123")) # Output: 123
print(int("-456")) # Output: -456
print(int(" 789 ")) # Output: 789 (whitespace is ignored)
# These don't work
# print(int("12.5")) # Error: ValueError: invalid literal for int()
# print(int("hello")) # Error: ValueError: invalid literal for int()
# print(int("12 34")) # Error: ValueError: invalid literal for int()我们会在第 27 章学习如何优雅地处理这类错误(异常处理)。目前只要记住:在转换前要确保字符串的内容是合法的整数表示。
将布尔值转换为整数:
布尔值也可以转换为整数,其中 True 会变成 1,False 会变成 0:
# bool_to_int.py
print(int(True)) # Output: 1
print(int(False)) # Output: 0
# This is sometimes useful in calculations
is_premium = True
is_student = False
# Calculate discount (10% for premium, 5% for students)
discount = int(is_premium) * 0.10 + int(is_student) * 0.05
print("Discount:", discount) # Output: Discount: 0.1不过,直接把布尔值用于算术运算通常不太推荐,因为会降低代码可读性。我们会在第 7 章进一步讨论这一点。
3.7.3) 使用 float() 转换为浮点数
float() 函数可以将一个值转换为浮点数:
将整数转换为浮点数:
# int_to_float.py
x = float(42)
y = float(-17)
z = float(0)
print(x, type(x)) # Output: 42.0 <class 'float'>
print(y, type(y)) # Output: -17.0 <class 'float'>
print(z, type(z)) # Output: 0.0 <class 'float'>将字符串转换为浮点数:
# string_to_float.py
# Convert string containing decimal number
price_str = "19.99"
price_float = float(price_str)
print(price_str, type(price_str)) # Output: 19.99 <class 'str'>
print(price_float, type(price_float)) # Output: 19.99 <class 'float'>
# Strings without decimal points work too
x = float("42")
print(x, type(x)) # Output: 42.0 <class 'float'>
# Scientific notation strings work
big = float("1.5e10")
print(big) # Output: 15000000000.0非法转换:
和 int() 类似,float() 在遇到非法字符串时会抛出 ValueError:
# invalid_float_conversion.py
# These work
print(float("3.14")) # Output: 3.14
print(float(" 2.5 ")) # Output: 2.5 (whitespace ignored)
print(float("-0.5")) # Output: -0.5
print(float("inf")) # Output: inf (infinity)
# These don't work
# print(float("hello")) # Error: ValueError
# print(float("1.2.3")) # Error: ValueError将布尔值转换为浮点数:
# bool_to_float.py
print(float(True)) # Output: 1.0
print(float(False)) # Output: 0.03.7.4) 使用 str() 转换为字符串
str() 函数可以把任意值转换成它的字符串表示形式:
将数字转换为字符串:
# number_to_string.py
# Convert integer
age = 25
age_str = str(age)
print(age_str, type(age_str)) # Output: 25 <class 'str'>
# Convert float
price = 19.99
price_str = str(price)
print(price_str, type(price_str)) # Output: 19.99 <class 'str'>
# Now we can concatenate with other strings
message = "The price is $" + price_str
print(message) # Output: The price is $19.99在构建字符串消息时,这尤其有用:
# building_messages.py
name = "Alice"
age = 25
height = 5.6
# Without str(), this would cause an error
# message = "Name: " + name + ", Age: " + age # Error!
# With str(), it works
message = "Name: " + name + ", Age: " + str(age) + ", Height: " + str(height)
print(message) # Output: Name: Alice, Age: 25, Height: 5.6将布尔值转换为字符串:
# bool_to_string.py
is_valid = True
is_error = False
print(str(is_valid)) # Output: True
print(str(is_error)) # Output: False
# Useful in messages
status = "Status: " + str(is_valid)
print(status) # Output: Status: True将 None 转换为字符串:
# none_to_string.py
value = None
value_str = str(value)
print(value_str) # Output: None
print(type(value_str)) # Output: <class 'str'>
# The string "None" is different from the value None
print(value is None) # Output: True
print(value_str is None) # Output: False
print(value_str == "None") # Output: True3.7.5) 实战中的转换模式
下面是一个综合例子,展示常见的类型转换模式:
# conversion_patterns.py
# Get user input (always strings)
name = input("Enter your name: ")
age_str = input("Enter your age: ")
height_str = input("Enter your height in feet: ")
# Convert to appropriate types
age = int(age_str)
height = float(height_str)
# Perform calculations
age_in_months = age * 12
height_in_inches = height * 12
# Convert back to strings for output
print("Hello, " + name + "!")
print("You are " + str(age_in_months) + " months old.")
print("Your height is " + str(height_in_inches) + " inches.")
# Alternative: use multiple arguments to print() (no conversion needed)
print("Hello,", name + "!")
print("You are", age_in_months, "months old.")
print("Your height is", height_in_inches, "inches.")
# When user enters "Alice", "25", and "5.5":
# Output: Hello, Alice!
# Output: You are 300 months old.
# Output: Your height is 66.0 inches.
# Output: Hello, Alice!
# Output: You are 300 months old.
# Output: Your height is 66.0 inches.注意,当 print() 使用多个参数(用逗号分隔)时,会自动把它们转换为字符串并在中间加空格。这通常比手动调用 str() 并拼接字符串更加方便。
3.7.6) 转换流程图
下面是常见类型转换的可视化表示:
关于转换的关键点:
- int → float:总是安全的,只是加上
.0 - float → int:会截断小数部分(不四舍五入)
- 任意类型 → str:总是安全的,会得到对应的文本表示
- str → int/float:仅当字符串是合法的数字表示时才可行
- bool → int/float:True 对应 1/1.0,False 对应 0/0.0
3.7.7) 常见的转换错误
下面是初学者在类型转换时常见的一些错误:
错误 1:忘记转换用户输入
# conversion_mistake1.py
# Wrong: trying to do math with string
age = input("Enter your age: ")
# next_year = age + 1 # Error: TypeError
# Right: convert first
age = int(input("Enter your age: "))
next_year = age + 1
print("Next year:", next_year)错误 2:在不需要时进行转换
# conversion_mistake2.py
# Unnecessary: print() handles conversion automatically
age = 25
print("Age:", age) # This works fine
# No need for:
print("Age:", str(age)) # Unnecessary str() conversion错误 3:试图转换非法字符串
# conversion_mistake3.py
# This will crash if user enters non-numeric input
# age = int(input("Enter your age: ")) # Crashes on "twenty"
# We'll learn to handle this safely in Chapter 27错误 4:以为 int() 会四舍五入
# conversion_mistake4.py
# Wrong expectation: int() truncates, doesn't round
x = int(3.7)
print(x) # Output: 3 (not 4!)
# If you want rounding, use round()
x = round(3.7)
print(x) # Output: 43.8) 使用 str() 和 repr() 获取字符串表示
3.8.1) str() 与 repr() 的区别
Python 提供了两种方式来获取对象的字符串表示:str() 和 repr()。它们看起来很像,但用途不同:
- str():生成“友好”的、面向用户的字符串表示
- repr():生成“官方的”、明确无歧义、面向开发者的字符串表示
对于数字这样的简单类型,它们往往相同,但对其他类型来说,可能差别很大:
# str_vs_repr.py
# For numbers, they're usually the same
x = 42
print(str(x)) # Output: 42
print(repr(x)) # Output: 42
# For strings, they differ
text = "Hello"
print(str(text)) # Output: Hello
print(repr(text)) # Output: 'Hello' (includes quotes!)
# For strings with special characters, repr() shows escape sequences
message = "Hello\nWorld"
print(str(message)) # Output: Hello
# World (newline is interpreted)
print(repr(message)) # Output: 'Hello\nWorld' (shows the \n literally)3.8.2) 何时使用 str()
当你想要为最终用户展示可读性好的结果时,使用 str():
# using_str.py
price = 19.99
quantity = 3
# Create user-friendly messages
message = "Total: $" + str(price * quantity)
print(message) # Output: Total: $59.97
# str() is what print() uses automatically
print("Total: $", price * quantity) # Output: Total: $ 59.97str() 旨在生成对人类来说易读的输出,即便这可能牺牲某些技术上的精确性。当你调用 print() 时,Python 会自动对传入的值调用 str()。
3.8.3) 何时使用 repr()
在调试时,通常需要使用 repr(),因为你希望看到精确的内容:
# using_repr.py
# Debugging: see exactly what's in a variable
text = "Hello\nWorld"
print("Debug info:", repr(text)) # Output: Debug info: 'Hello\nWorld'
# Compare two similar-looking strings
str1 = "42"
str2 = "42 " # Has trailing space
print(str1) # Output: 42
print(str2) # Output: 42 (space not obvious)
print(repr(str1)) # Output: '42'
print(repr(str2)) # Output: '42 ' (space is visible!)repr() 的目标是展示 Python 眼中的精确内容,包括在普通输出中不易察觉的细节,因此在调试时非常有用。
3.8.4) repr() 的目标:可重建的表示
理想情况下,repr() 产生的字符串应该可以被 Python 解释器执行,从而重建原始对象。对于基础类型,这通常成立:
# repr_recreate.py
# For numbers
x = 42
x_repr = repr(x)
print(x_repr) # Output: 42
# You could use this to recreate x
x_recreated = eval(x_repr) # eval() evaluates a string as Python code
print(x_recreated) # Output: 42
# For strings
text = "Hello"
text_repr = repr(text)
print(text_repr) # Output: 'Hello'
# This could recreate the string
text_recreated = eval(text_repr)
print(text_recreated) # Output: Hello重要说明: eval() 会把字符串当作 Python 代码执行。这里提到它只是为了说明 repr() 的设计目标,但绝不要对不可信输入使用 eval()——这存在严重安全风险。我们会在后续章节介绍更安全的替代方案。
3.8.5) str() 和 repr() 的实际例子
例 1:调试字符串问题
# debugging_strings.py
# User input might have unexpected whitespace
user_input = " Alice "
print("User entered:", user_input) # Output: User entered: Alice
print("Debug view:", repr(user_input)) # Output: Debug view: ' Alice '
# Now the extra spaces are obvious!
cleaned = user_input.strip() # Remove leading/trailing whitespace
print("Cleaned:", repr(cleaned)) # Output: Cleaned: 'Alice'例 2:比较看起来相似的不同类型
# comparing_types.py
# These look similar but are different
num = 42
text = "42"
print("Number:", num) # Output: Number: 42
print("Text:", text) # Output: Text: 42
print("Number repr:", repr(num)) # Output: Number repr: 42
print("Text repr:", repr(text)) # Output: Text repr: '42'
# repr() makes the difference clear
print("Are they equal?", num == text) # Output: Are they equal? False3.8.6) str() 和 repr() 在其他类型上的表现
来看一下 str() 和 repr() 在本章学习的几种类型上的表现:
# str_repr_types.py
# Integers
x = 42
print("int str:", str(x)) # Output: int str: 42
print("int repr:", repr(x)) # Output: int repr: 42
# Floats
y = 3.14159
print("float str:", str(y)) # Output: float str: 3.14159
print("float repr:", repr(y)) # Output: float repr: 3.14159
# Booleans
b = True
print("bool str:", str(b)) # Output: bool str: True
print("bool repr:", repr(b)) # Output: bool repr: True
# None
n = None
print("None str:", str(n)) # Output: None str: None
print("None repr:", repr(n)) # Output: None repr: None
# Strings (where they differ most)
s = "Hello\nWorld"
print("string str:", str(s)) # Output: string str: Hello
# World
print("string repr:", repr(s)) # Output: string repr: 'Hello\nWorld'3.8.7) 关键要点总结
在这些场景使用 str():
- 为最终用户生成输出
- 构建面向用户的消息
- 格式化数据用于展示
- 你想要“友好”、易读的结果
在这些场景使用 repr():
- 调试代码
- 记录技术日志信息
- 需要精确查看变量中到底是什么
- 需要一种无歧义、尽量可重建的表示
请记住:
print()会自动对其参数调用str()- 对大部分基础类型来说,
str()和repr()的结果很相似 - 对字符串来说,
repr()会包含引号并显示转义序列 repr()的设计目标是“无歧义”和“尽可能可重建”
本章小结
在本章中,你学习了 Python 中变量与数据类型的基础概念。下面我们来回顾一下关键要点:
变量 (variables):
- 变量是指向内存中值的名字
- 使用赋值语句创建:
variable_name = value - 可以在任意时刻重新赋值为新值
- 必须遵守命名规则,并应当遵循命名约定
- 使用有意义的名字可以让代码“自带文档”
数据类型 (data types):
- Python 中的每个值都有自己的类型
- 使用
type()可以查看某个值的类型 - Python 是动态类型语言——变量在不同时间可以引用不同类型
- 不同类型支持的操作不同
基础类型:
- int:整数(可以是正数、负数或零),大小仅受内存限制
- float:浮点数(带小数点的数),可能存在精度限制
- str:用引号括起来的文本(字符串)
- bool:逻辑值
True或False - None:表示“没有值”的特殊值
类型转换:
int():转换为整数(会截断浮点数,或解析字符串)float():转换为浮点数str():转换为字符串(对任何值都适用)- 非法输入会导致转换失败(抛出
ValueError)
字符串表示:
str():面向用户的可读字符串表示repr():面向开发者的、无歧义的表示,适合调试print()内部使用的是str()
这些概念构成了你在 Python 中进行其他所有操作的基础。你写的每一个程序都会用到变量来存储数据,而理解数据类型则能帮助你预测哪些操作是可行的,哪些可能会导致错误。
在下一章中,我们将在此基础上进一步扩展,深入探讨如何处理数字,包括算术运算、运算符优先级以及常见的数值模式。你将学习如何进行各种计算,如何使用数学函数,以及如何应对浮点数运算中的那些“怪脾气”。