变量这个东西,几乎每个学Python的人第一天就会碰到,但说实话,很多人写了几个月代码,对“变量到底是个什么玩意儿”这件事仍然是糊涂的。不是因为笨,而是大部分教程把变量讲得太抽象了。什么“变量是存储数据的容器”啦,什么“变量就是给数据起个名字”啦,这些说法其实都不算错,但放在Python里,它们都容易把你带偏。
我见过太多初学者卡在同一个地方:明明照着教程写了a = 1,然后再写b = a,接着改a = 2,结果发现b也变了,就开始怀疑人生。还有人分不清==和is、搞不懂为什么1 == True居然是成立的、不知道为什么函数里改了列表外面也跟着变。所有这些困惑,归根结底都是一个原因:没有真正理解Python变量和数据类型的工作机制。
所以这篇文章我不想按照教科书的路子,把变量和数据类型一条一条摆出来念。我想用“踩坑+原理+实操”的方式,把变量到底是什么、Python的数据类型到底怎么用、以及初学者最容易在哪里翻车,一次讲透。内容覆盖变量引用机制、数值/字符串/布尔等核心类型、类型转换、可变与不可变对象、命名规范,以及一套排查变量相关报错的完整思路。无论你是刚装好Python还没写过几行代码的纯新手,还是已经写过一阵子但总感觉某些概念模模糊糊的人,这篇文章都值得你从头到尾过一遍。
先声明一下我写这篇文章的立场:不堆概念、不抄文档,尽量用大白话把底层逻辑讲明白,然后给你能直接抄作业的写法和避坑清单。
1. 变量不是盒子:Python的“标签式引用”到底意味着什么
如果你以前学过C语言或者Java,你脑子里对变量的印象大概率是——“变量就是一个盒子,里面装着数据”。这个模型在Python里恰恰是错的,而且是很多后续混乱的根源。
1.1 变量是贴在数据上的标签
Python官方文档里有一个非常经典的说法:变量是对象的“名字”,你可以把变量理解成贴在对象上的标签,而不是装对象的盒子。
怎么理解这句话?你写这样一行代码:
python复制a = 1024
Python实际做的事情是:先在内存里创建出一个值为1024的整数对象,然后拿一个叫a的标签贴到这个对象上。严格来说,是名字a绑定到了对象1024上。
接着你再写:
python复制b = a
这行代码并不是“把a里面的值复制一份给b”,而是“让b这个名字也贴到1024这个对象上”。此时a和b都指向同一个对象,它们只是同一个对象的两个不同名字。
那如果你再写:
python复制a = 2048
会发生什么?Python会创建一个新的整数对象2048,然后把a这个标签撕下来贴到2048上。此时b依然贴在1024上,所以b的值还是1024,而a变成了2048。
这就是标签模型的全部逻辑。明白了这一点,你就不难理解为什么Python的变量赋值这么“随意”了——因为Python的变量是“先有对象,再贴标签”,标签本身没有任何类型限制。同一个标签,这一秒可以贴在一个整数上,下一秒可以撕下来贴到一个字符串上。这就是Python“动态类型”的真正来源。
1.2 动态类型不是“没类型”,而是“变量不绑类型”
很多教程说“Python是动态类型语言,变量没有类型”。这句话不能说错,但很容易让人误解成“Python的数据没有类型”。
实际上Python的数据对象都有自己的类型,整数是int,小数是float,文本是str,这一点从来没有含糊过。动态类型说的是:变量本身不绑定类型,同一个变量可以先后指向不同类型的对象。
举个例子:
python复制x = 10 # x 指向 int 对象
print(type(x)) # <class 'int'>
x = "hello" # x 改指向 str 对象
print(type(x)) # <class 'str'>
这段代码在Python里完全可以正常运行,因为Python在给变量赋值时根本不去检查“这个变量之前是什么类型”,它只负责把标签贴到新对象上。
对比之下,C语言和Java是“静态类型”,变量在声明时就固定了类型,后面再也改不了。这也是为什么很多从Java转过来的人总觉得Python太“随便”——不是Python随便,而是它的设计哲学就不一样:类型是数据本身的属性,不是变量名的一部分。
初学者可能觉得这无所谓,但你在以后写函数参数、做类型判断、写大型项目的时候,深刻理解“变量是标签、对象才有类型”这件事,能帮你避开至少一半莫名其妙的问题。
1.3 从变量机制理解Python的内存管理
既然说到了标签机制,顺便提一嘴Python的内存管理。
还是回到刚才的例子:
python复制a = 1024
b = a
a = 2048
当a被重新绑定到2048之后,原来的1024对象依然存在,因为b还贴在它身上,所以它不会被清理。
但如果你再执行:
python复制b = 4096
此时1024对象身上一个标签都没有了,Python的垃圾回收机制就会在合适的时机把它回收掉,释放内存。
这套机制对初学者来说有个很实际的启发:Python里“删除变量”del a删的不是对象本身,而是撕掉一个标签。只有当一个对象身上的所有标签都被撕掉,它才会真正消失。
注意:Python对小整数(通常是-5到256)和某些短字符串有一个“驻留”机制,也就是说这些对象会被提前创建并复用,所以当你用
is比较两个值相同的小整数时,结果往往是True。但这只是CPython实现层面的优化,不代表你应该依赖它。遇到比较值时,老老实实用==,不要用is。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据类型全家福:从数值到布尔,每一类都该怎么用
Python内置的数据类型不算多,但每个都值得认真过一遍。这里我不打算按文档顺序把list、tuple、dict、set全部铺开讲,因为那会变成另一篇长文。这篇文章聚焦在最基础的几个:数值、字符串、布尔,外加简单提一句列表和字典的基本特性。
2.1 数值类型:int、float、complex的分工与合作
Python里有三种数值类型:int(整数)、float(浮点数,也就是小数)、complex(复数)。日常写代码用到最多的是前两个。
int在Python 3里是任意精度的,意思是你想写多大的整数都行,不会像C语言的int那样有上限:
python复制big_num = 123456789012345678901234567890
print(big_num * 2)
这段代码能正常运行并给出一个更大的数字,不会溢出。这个特性在做大数运算时非常省心。
float就是带小数点的数,比如3.14、-0.5、1.0。需要注意的是,Python的浮点数遵循IEEE 754标准,也就是说它其实是个“近似值”。你可以在交互式环境里试试:
python复制print(0.1 + 0.2)
输出结果是0.30000000000000004,而不是0.3。这不是Python的bug,而是浮点数在二进制里无法精确表示十进制小数的必然结果。所有主流语言都有这个问题,Java、JavaScript、C++无一例外。
如果你在做货币计算或者需要精确小数的场景,别用float,直接用decimal模块:
python复制from decimal import Decimal
a = Decimal("0.1")
b = Decimal("0.2")
print(a + b) # 输出 0.3
注意我用了字符串来构造Decimal,如果你直接写Decimal(0.1),等于先把0.1转成了二进制的近似浮点数,再转回来,精度损失已经发生了,结果就不对了。
2.2 字符串:被引号包裹的“小世界”
字符串用str表示,是编程世界里最常用的数据形态。Python里的字符串可以用单引号、双引号或三引号来包裹:
python复制s1 = 'hello'
s2 = "world"
s3 = """多行
字符串"""
单引号和双引号在Python里没有任何区别,只是为了让你能在字符串里方便地包含另一种引号。比如"he said 'hi'"就不用转义,这个细节很小但很实用。
字符串有一个很重要的特性:它是不可变的(immutable)。所谓不可变,就是你不能修改一个已经存在的字符串对象。你可能会说:
python复制s = "hello"
s[0] = "H"
这行代码会直接报错:TypeError: 'str' object does not support item assignment。因为字符串对象一旦创建,它的内容就不能被修改。你想得到一个“新字符串”,只能通过拼接、切片、替换等操作创建出一个新的字符串对象,然后再赋值给变量。
python复制s = "hello"
s = "H" + s[1:]
print(s) # Hello
这个例子中,"H" + s[1:]创建了一个新的字符串对象"Hello",然后变量s重新贴到了这个新对象上。原来的"hello"对象依然没人动过。
字符串的切片操作非常常用,语法是[start:end:step]。举个例子:
python复制text = "Python"
print(text[0:2]) # Py
print(text[-3:]) # hon
print(text[::-1]) # nohtyP
最后那个[::-1]是个经典技巧,表示从尾到头步长为-1,直接实现了字符串反转。切片这个语法在列表、元组里也通用,建议一次记牢。
2.3 布尔值:True和False背后的逻辑基础
布尔类型bool只有两个值:True和False。但Python里有个非常容易踩坑的点:布尔值其实是整数的一个子类型。在Python里:
python复制print(True == 1) # True
print(False == 0) # True
print(True + True) # 2
True在参与数值运算时会被当作1,False会被当作0。这个设计在底层是有历史原因的(布尔值就是从整数演化来的),但在实际编码中经常坑到人。
比如你从数据库或者接口里拿到一个字段,值是数字1或0,你想判断它是不是等于True:
python复制status = 1
if status == True:
print("启用")
这段代码会输出“启用”,因为1 == True成立。但如果你拿到的是字符串"1"呢?"1" == True就是False了,因为字符串和整数不会做隐式转换。这种“看着一样但类型不同”的坑,在真实项目里特别常见。
另外一个常见误区是is和==的区别。==比较的是值是否相等,is比较的是两个变量是否指向同一个对象。对于布尔值,True is 1是False,但True == 1是True。所以判断布尔值时,直接用if status:或者if not status:就好,别去和True比,少很多麻烦。
布尔值还有一个重要应用:条件判断时,Python会把一些“空”的东西当成False。比如0、0.0、""(空字符串)、[](空列表)、{}(空字典)、None都会被当成False,其他值当成True。这个叫做“真值测试”。
python复制name = ""
if name:
print("有名字")
else:
print("名字为空")
这段代码会输出“名字为空”,因为空字符串在布尔上下文里是False。利用这个特性可以让代码更简洁,但也要注意:如果你本来想判断“这个变量的值是不是False”,结果它只是“为空”,那你可能会踩坑。要根据场景选对判断方式。
2.4 列表和字典:先记住它们的可变性
虽然这篇文章的重点是基础,但列表(list)和字典(dict)作为最常用的容器类型,我也必须在这里提一下,因为它们和后面“可变与不可变”那一章直接相关。
列表用方括号表示,可以装任意类型的元素:
python复制fruits = ["apple", "banana", "cherry"]
fruits.append("orange")
print(fruits) # ['apple', 'banana', 'cherry', 'orange']
字典用花括号表示,是“键值对”的集合:
python复制person = {"name": "Alice", "age": 25}
person["city"] = "Beijing"
print(person) # {'name': 'Alice', 'age': 25, 'city': 'Beijing'}
列表和字典最大的特点是:它们是可变的(mutable)。也就是说,你可以直接修改它们内部的内容,而不需要创建一个新对象。这一点和字符串形成鲜明对比。具体的差异和后果,我会在第四章详细展开。这里你先留个印象:列表和字典是“能改内容”的,字符串和数字是“改不了内容”的。
3. 类型转换:显式转换、隐式转换,以及最容易被忽略的坑
写代码时经常需要在不同类型之间切换。比如从输入框里拿到的永远是字符串,你要参与运算就得转成数字。Python的类型转换分两类:显式转换和隐式转换,都有不少细节值得注意。
3.1 显式转换的三个常用函数:int()、float()、str()
int()可以把一个字符串或浮点数转成整数:
python复制num_str = "42"
num = int(num_str)
print(num + 8) # 50
这里有个常见坑:如果你用int("42.5"),Python会直接报错,因为字符串里的内容不是合法的整数格式。你需要先转成float,再转成int:
python复制num = int(float("42.5"))
print(num) # 42
注意int()在把浮点数转成整数时,是向零取整,不是四舍五入。int(42.9)得到42,int(-42.9)得到-42。如果你想四舍五入,用round()函数。
float()可以把字符串或整数转成浮点数:
python复制pi_str = "3.14"
pi = float(pi_str)
print(pi * 2) # 6.28
str()可以把任何对象转成字符串。这个函数在实际开发里用途非常广,尤其是在拼接文本和日志输出时:
python复制age = 25
# 直接拼接会报错
# message = "I am " + age + " years old" # TypeError
message = "I am " + str(age) + " years old"
print(message) # I am 25 years old
在Python 3.6以后,更推荐用f-string来做这种拼接,简洁且不容易出错:
python复制message = f"I am {age} years old"
print(message)
3.2 隐式转换:Python偷偷帮你做的类型“变脸”
隐式转换发生在不同类型的数据参与同一个运算时。Python会自动把“窄类型”转成“宽类型”,以避免信息丢失。
python复制result = 10 + 3.5
print(result) # 13.5
这里的整数10被自动转成了浮点数10.0,然后和3.5相加,结果是浮点数13.5。这个过程你不需要做任何操作,Python自动完成。
布尔值和整数之间也有隐式转换,正如前面提到的,True会被当成1,False会被当成0。所以True + 1的结果是2。这就是为什么在写复杂条件判断时要格外小心:如果你把一个布尔表达式当成数字来用,它真的会“变成”数字。
字符串和数字之间没有隐式转换:
python复制# 下面这句会报错
# result = "age is " + 25
Python不会自动把25转成字符串,而是直接抛TypeError。所以当看到TypeError: can only concatenate str (not "int") to str时,你就知道:有人把字符串和数字直接拼了。
3.3 几个“看似正常但结果诡异”的转换案例
初学阶段最容易碰到的转换坑,我归纳成三类:
第一类是字符串数字的比较。你可能会写:
python复制if "10" > 2:
print("10 is greater than 2")
这行代码在Python 3里会直接报错:TypeError: '>' not supported between instances of 'str' and 'int'。Python不让你直接比较字符串和数字,因为两者的“大小”没有可比性。你需要在比较之前先统一类型。
第二类是bool参与运算。看这段代码:
python复制total = 0
items = [1, 2, 3]
if items:
total = sum(items) + True
这个True会被当成1加到总和里,结果变成7而不是6。这种代码即便在真实项目中偶尔也会看到,但可读性和可维护性都很差。要表示加1,就直接写+ 1。
第三类是列表或字典转换为布尔值。这个前面提过,空列表[]和空字典{}在布尔上下文里是False,非空的是True。很多人用if list:来判断列表是否为空,这没问题,但要记得这是依赖“真值测试”的隐式规则,不是直接的比较操作。
4. 可变与不可变:最容易让初学者翻车的底层概念
这可能是整个Python基础语法里最值得花时间搞透的点。可变(mutable)与不可变(immutable)不仅仅是概念,它直接决定了你的代码在不同场景下表现出的行为。很多人在函数传参时遇到“诶,我的列表怎么被改了”,根子就在这里。
4.1 不可变类型:为什么int和str“改不动”
不可变类型的典型代表有:int、float、str、tuple、bool。
不可变的意思是:对象一旦创建,其内容就无法被修改。前面举过字符串的例子——s[0] = "H"会报错。整数的不可变性其实更隐蔽,因为a = a + 1看起来很像是在修改a,但实际流程是:
- 读取
a当前指向的对象(比如整数10) - 执行加法,创建出一个新的整数对象11
- 把变量
a的标签贴到新对象11上
原来的整数对象10没有发生任何变化,它只是被“丢弃”了(如果没有其他变量引用它的话)。所以你看,a = a + 1并不是“把a里面的数值改了”,而是“把a换了个新对象贴上”。
tuple(元组)是另一种常见的不可变类型。它和列表长得几乎一样,只是用圆括号:
python复制point = (10, 20)
# point[0] = 99 # 报错,tuple不可变
元组的不可变性意味着一旦创建,就不能添加、删除或修改元素。这个特性在很多场景下是有意为之的:当你希望某个数据在传给别的函数后不会被意外改动时,用元组比用列表更安全。
4.2 可变类型:列表和字典到底哪里“可变”
可变类型的典型代表有:list、dict、set。
可变的意思是:对象的内容可以被修改,而且对象本身还是那个对象,内存地址不变。
python复制items = [1, 2, 3]
items.append(4)
print(items) # [1, 2, 3, 4]
这里的append直接在原列表上添加了一个元素,没有创建新列表。items这个名字还是贴在那个列表对象上,内存地址没变。
你可以用id()函数验证这一点:
python复制items = [1, 2, 3]
print(id(items)) # 1401234567890(假设值)
items.append(4)
print(id(items)) # 还是同一个值,因为列表没换
但如果执行items = items + [5],那结果就不一样了——+会创建一个新列表,items会贴到新对象上,内存地址发生变化。而items += [5]则比较特殊:对于可变类型,+=会原地修改;对于不可变类型,+=会创建新对象。这个差异有点绕,但记住结论就行。
4.3 传参时的经典翻车现场
可变和不可变最经典的分歧场景是函数传参。
先看不带返回值的修改:
python复制def add_one_to_number(n):
n = n + 1
print("inside:", n)
x = 10
add_one_to_number(x)
print("outside:", x)
运行结果是:
code复制inside: 11
outside: 10
这是因为n传递过来的是10这个整数对象的引用,但n = n + 1让局部变量n绑到了新对象11上。外面的x依然指向10,所以外面的值没变。
再看列表参数:
python复制def add_item(lst):
lst.append("new")
my_list = [1, 2, 3]
add_item(my_list)
print(my_list)
运行结果是:
code复制[1, 2, 3, 'new']
外面的my_list被改了!因为lst和my_list指向的是同一个列表对象,lst.append("new")修改的是对象本身的内容,所以调用方也能看到这个修改。
这个特性用好了可以省事(不需要用返回值就能让函数修改数据),用不好就是灾难。比如你本来只是想在函数里临时操作一下列表,没想到把外面的原始数据改了。为了避免这种意外,可以在传参时拷贝一份:
python复制def add_item_safe(lst):
lst = lst.copy()
lst.append("new")
return lst
my_list = [1, 2, 3]
new_list = add_item_safe(my_list)
print(my_list) # [1, 2, 3],没变
print(new_list) # [1, 2, 3, 'new']
copy()是浅拷贝,对一维列表够用。如果列表里还嵌套了列表,就得用copy.deepcopy()做深拷贝,否则内层列表还是共享的。
4.4 用一个比喻彻底记住可变与不可变
把不可变对象想象成一张打印好的照片。你想换个表情,只能重新打印一张新照片,原来的照片永远不会变。把可变对象想象成一块白板。你随时可以在白板上擦掉、重写,白板还是那块白板。
这个比喻能帮你快速判断:如果某操作“重新生成一份数据”,那多半意味着操作的是不可变对象;如果某操作“在原有数据上动手脚”,那多半是可变对象。
5. 变量命名的学问:不只是规范,更是可读性“工程”
命名这件事,初学阶段最容易忽略,但恰恰是决定你代码能不能被自己和别人看懂的关键。Python的命名规则说起来不多,但每条都可能影响程序的正确性和可读性。
5.1 硬性规则与PEP 8建议
先看硬性规则,违反了程序直接报错:
- 变量名只能包含字母、数字、下划线,不能有空格和特殊符号。
- 不能以数字开头。
- 不能是Python的关键字,比如
if、for、while、class、def等。 - 变量名区分大小写,
name和Name是两个不同的变量。
再看PEP 8建议,违反了程序能跑,但代码不地道:
- 变量名和函数名使用小写字母加下划线,比如
user_name、get_total,这叫“蛇形命名法”。 - 常量(值不打算改变的变量)用全大写加下划线,比如
MAX_RETRY_COUNT。 - 类名使用驼峰式命名,比如
UserProfile,不过这是学面向对象之后的事。 - 避免使用容易混淆的字符,比如小写字母
l和数字1、大写字母O和数字0。
如果你用的是Visual Studio Code或者PyCharm,还可以装上linter插件(比如Pylint或flake8),它会在你违反PEP 8时给出提示。刚开始可能觉得烦,但养成习惯后写出的代码会干净很多。
5.2 好名字让代码自文档化
很多人写代码的时候图省事,变量名用a、b、c、tmp、data凑合。短期看是省了几秒钟,长期看是在给自己挖坑——一个礼拜后你回来看这段代码,根本想不起来tmp是什么。
我见过最好的命名风格是:让代码读起来像一句自然语言。比如:
python复制# 糟糕的命名
n = 3.14159
r = 5
s = n * r * r
# 清晰的命名
PI = 3.14159
radius = 5
area = PI * radius * radius
第二段代码不用任何注释,你都知道它在算圆的面积。而第一段代码虽然也能运行,但可读性差了一个量级。
还有一个更实用的小技巧:用形容词开头的布尔变量名,让条件判断更自然:
python复制is_valid = True
has_permission = False
is_empty = len(items) == 0
if is_valid and has_permission:
# 执行逻辑
pass
这样的代码读起来接近英文句子:“if is valid and has permission”,一眼就能懂。
5.3 变量命名的几个实操建议
第一,同一个变量不要一会儿存字符串、一会儿存数字。虽然Python允许,但代码会变得极难维护。如果你发现自己在一个变量里反复切换不同类型,大概率是设计上有问题,应该拆成两个变量或者重新思考逻辑。
第二,不要太短的缩写。cnt、msg、usr这种缩写在一两个文件里还能看懂,但项目一大就疯了。现在IDE都有自动补全,多用几个字符不会拖慢你的速度。
第三,避免关键字和内置函数名。比较隐蔽的坑是:Python内置了很多函数,比如list、dict、str、type。有人习惯用list当变量名,结果后面再用list([1,2,3])时就报错,因为list已经被你的变量覆盖了。命名时尽量避开内置函数名,或者至少在覆盖时心里有数。
6. 初学阶段最容易犯的错,以及对应的排查思路
这章我想直接给你一套排查方法,而不是只罗列错误。学编程不只是“会写正确的代码”,更重要的是“会诊断错误的代码”。这里挑三个最典型的变量/数据类型相关错误,带你走一遍完整的排查链路。
6.1 NameError:变量名“不存在”了
最常见的报错之一:
code复制NameError: name 'age' is not defined
看到这个报错,第一反应是:这个变量还没被赋值。可能的原因有:
- 变量名拼写错误。检查一下是不是
age写成了aage。 - 变量赋值代码还没执行就被使用了。
- 变量是在另一个作用域里定义的,比如在函数内部定义的变量,在函数外部访问不到。
排查步骤:
- 看看报错行之前有没有给这个变量赋过值。
- 用编辑器的全局搜索(或者
grep命令)搜一下这个变量名,确认拼写一致。 - 确认作用域。函数内部的变量默认是局部变量,函数外面访问不到,除非用
return返回或者使用global关键字(但不推荐随便用)。
6.2 TypeError:类型对不上号
另一个高频报错:
code复制TypeError: unsupported operand type(s) for +: 'int' and 'str'
这说明有人试图把整数和字符串相加。排查思路是:先看这个变量实际上是什么类型。
python复制age = input("请输入年龄: ")
result = age + 1 # 报错
input()返回的永远是字符串,所以age其实是"18"这种字符串,不是数字18。你需要在运算前转换类型:
python复制age = int(input("请输入年龄: "))
result = age + 1
这是一个非常典型的“你以为它是数字,其实是字符串”的问题。在排查时,可以用type()函数打印变量类型,或者用print()把变量值和类型一起打出来:
python复制age = input("请输入年龄: ")
print(age, type(age))
这一步能帮你快速确认问题根源。
6.3 列表“被悄悄修改”:可变性的实战坑
假设你写了一个函数,想处理一个列表但不改变原始数据:
python复制def clean_data(data):
for item in data:
if item < 0:
data.remove(item)
return data
scores = [10, -5, 8, -3, 12]
clean_data(scores)
print(scores)
这段代码的问题有两层。第一,data.remove()直接在原列表上做修改,导致传入的scores也被改掉了。第二,在遍历列表的同时删除元素,Python会跳过部分元素,结果可能不如预期。
排查思路:
- 在函数开头复制一份列表:
data = data.copy()。 - 不要边遍历边删除,更好的做法是“过滤出新列表”:
python复制def clean_data(data):
return [item for item in data if item >= 0]
scores = [10, -5, 8, -3, 12]
clean_scores = clean_data(scores)
print(scores) # [10, -5, 8, -3, 12] 原列表没变
print(clean_scores) # [10, 8, 12]
这段代码用列表推导式创建了一个新列表,既不修改原数据,也避免了遍历中删除的坑。
6.4 推荐一套调试习惯
变量相关的问题,百分之八十靠三句话就能定位:
print(type(变量名)):看类型对不对。print(变量名):看值对不对。- 把出问题的代码拆成几步,逐步打印中间结果。
初学阶段不要觉得用print调试丢人,它反而是最直接、最不打断思路的定位方式。等你熟练了,再慢慢接触IDE断点调试也不迟。
7. 一课时练手:用刚刚学的知识写一个“信息登记小程序”
讲了这么多,不动手写点东西很难消化。最后给你一个小练习,把变量、数据类型、输入输出、类型转换、列表操作都串起来。
目标:写一个简单的信息登记程序,可以连续录入多名学生的姓名和年龄,最后打印出所有学生信息和平均年龄。
python复制students = []
while True:
name = input("请输入学生姓名(输入q退出): ")
if name == "q":
break
age_str = input("请输入学生年龄: ")
try:
age = int(age_str)
except ValueError:
print("年龄必须是数字,请重新录入")
continue
students.append({"name": name, "age": age})
if students:
total_age = sum(student["age"] for student in students)
avg_age = total_age / len(students)
print("\n学生信息列表:")
for student in students:
print(f"姓名: {student['name']}, 年龄: {student['age']}")
print(f"平均年龄: {avg_age:.1f}")
else:
print("没有录入任何学生信息")
这个练习里用到的知识点:
- 变量绑定:
name、age_str每次循环都重新绑定到新的输入对象上。 - 数据类型:
name是str,age是int。 - 类型转换:用
int()把输入的字符串转成整数。 - 异常捕获:如果输入的不是数字,用
try...except捕获ValueError。 - 列表和字典作为可变数据容器:不断
append新数据。 - f-string格式化输出:注意
{student['name']}里的引号嵌套。
你可以把这个程序扩展一下:加一个性别字段、算最大年龄、统计人数按年龄排序等等。改完你会发现,基础语法这东西,练着练着就熟了。
我在实际带新人或者自己回头看代码时,最深的体会是:变量和数据类型的核心不是“记住语法”,而是“建立正确的模型”。模型对了,后面那些看似复杂的问题其实都有迹可循。你只需要反复提醒自己三件事:变量是标签不是盒子、数据对象才有类型、可变对象要小心被原地修改。这三句话,足够你走很远的路。
