为什么Python不用var或let声明变量?

📅 2026/6/19 11:00:06
为什么Python不用var或let声明变量?
免费编程软件「pythonpycharm」链接https://pan.quark.cn/s/48a86be2fdc0一个JavaScript程序员转Python的第一天小王做了三年前端最近开始学Python。他打开编辑器敲下了人生中第一段Python代码var name 张三 let age 18 const PI 3.14运行报错。他懵了。“没有var没有let也没有const那Python怎么声明变量”他把var删掉直接写name 张三 age 18 PI 3.14居然可以。他更懵了“不用声明就能用这不会乱套吗”这个问题问到了点子上。今天我们就来聊聊Python为什么不用var或let来声明变量没有声明关键字Python是怎么保证代码不乱套的先看看别人家是怎么做的在解释Python之前先看看其他语言是怎么声明变量的。JavaScript三种方式var x 10; // 函数级作用域有变量提升 let y 20; // 块级作用域没有变量提升 const z 30; // 块级作用域不能重新赋值三个关键字做同一件事声明变量但行为完全不同。新手看到这些规则头都大了。Java类型在前int x 10; String name 张三; boolean flag true;声明变量时必须带上类型。好处是类型安全坏处是啰嗦。C语言和Java类似int x 10; char *name 张三;声明变量时也必须指定类型。Rustlet是必须的let x 10; let mut y 20; // mut表示可修改Rust用let声明变量默认不可变要修改必须加mut。Go用:或者varx : 10 // 短变量声明 var name 张三 var age int 18 // 完整写法回到Python你会发现它做了一件很特别的事声明变量不需要任何关键字赋值就是声明。x 10 # 声明一个整数 name 张三 # 声明一个字符串 person {name: 张三} # 声明一个字典你写x 10Python就知道你要创建一个变量叫x指向整数10。不需要var不需要let不需要类型。这个设计看起来“太随意了”但其实背后有很深的设计哲学。Python的设计哲学赋值即声明Python的创造者Guido van Rossum在设计变量声明时遵循了一个核心原则简单就是王道。在Python里变量的声明和赋值是同一件事。你给一个名字赋值这个名字就变成了变量。# 这两行是等价的 x 10 # 声明赋值一次性完成 # 在Python里没有单独的声明操作如果你用过JavaScript你知道var x;只是声明x 10;才是赋值。声明和赋值是两步。Python把这两步合并了。你永远不需要提前说“我要用这个名字”直接用就行。为什么这样设计Guido的想法是程序员写代码时90%的变量都是在赋值的同时声明的。既然几乎总是同时做为什么不合并成一个操作这减少了键盘敲击减少了视觉噪音让代码看起来更干净。# Python name 张三 age 18 items [] # JavaScript var name 张三; let age 18; const items [];Python少打了var、let、const、分号代码更短。这符合Python“简洁”的追求。没有关键字怎么区分变量和常量你可能会问“那const呢Python怎么定义常量”答案是Python没有真正的常量。在Python里一切都是变量。你定义一个名字它就可以被重新赋值。PI 3.14 PI 3.14159 # 合法Python不会阻止你那怎么办约定成俗所有字母都大写的名字视为常量不要修改它。MAX_CONNECTIONS 100 API_KEY sk-123456这只是程序员之间的约定Python解释器不会强制保护。如果你修改了它Python也不会报错。听起来很危险其实还好。在团队开发中代码审查和规范检查会帮你发现问题。而且真正的“不可变”需求可以用property、__slots__或者数据类来实现更强的保护。为什么Python不内置constGuido在邮件列表里解释过他认为不需要。Python的目标用户不是写大型企业系统的而是希望快速完成工作的。加一个const会增加语言的复杂性收益却不大。如果需要真正的不可变性可以用types.MappingProxyType创建只读字典或者用dataclass(frozenTrue)创建不可变数据类。没有关键字怎么处理作用域这是很多人好奇的点。JavaScript有var、let、const来处理作用域Python什么都没有它怎么区分局部和全局Python的规则很简单赋值决定作用域。看例子x 10 # 全局变量 def func(): y 20 # 局部变量因为这里赋值了 print(x) # 可以读全局的x func() print(y) # 报错y是func的局部变量Python在编译函数时看到y 20这个赋值语句就把y标记为局部变量。函数内的所有y都指向这个局部变量除非你用了global或nonlocal声明。这和JavaScript完全不同。JavaScript的var有变量提升let有暂时性死区规则复杂得多。Python的做法是在哪里赋值就在哪里生效。在函数外赋值 → 全局变量在函数内赋值 → 局部变量用global声明 → 明确指向全局用nonlocal声明 → 明确指向外层函数这个规则简单、一致、可预测。对比JavaScript声明变量的混乱史Python不引入声明关键字还有一个原因避免JavaScript踩过的坑。JavaScript的变量声明历史是一部“反复横跳”的血泪史。最早的时候只有varif (true) { var x 10; } console.log(x); // 10x泄漏出来了var没有块级作用域只有函数级作用域。这导致了很多bug。var还有变量提升console.log(x); // undefined而不是报错 var x 10;变量声明被提升到了作用域顶部但赋值留在了原地。这让人非常困惑。ES6引入了let和constif (true) { let y 10; } console.log(y); // 报错y is not defined终于有了块级作用域但代价是语言变得更复杂了。现在JavaScript有三种声明变量的方式各有各的规则。Python避免了这一切从一开始Python就没有这些历史包袱。它的设计是统一的赋值就是声明没有两步操作作用域由赋值位置决定规则清晰不存在变量提升使用前必须赋值Python不需要var或let因为它从一开始就设计对了。Python的变量声明真的没有坑吗当然不是。Python的“赋值即声明”也有自己的坑。坑1在函数内意外创建局部变量这是最常见的坑name 张三 def change(): name 李四 # 创建了局部变量不是修改全局 change() print(name) # 张三——没变你想修改全局name结果创建了一个局部变量。Python不会报错只是静默地做了另一件事。解决方法如果要修改全局变量用global声明。坑2在if/for里创建变量会泄漏for i in range(5): message f数字{i} print(message) # 数字4——泄漏了因为Python没有块级作用域message成为了所在函数或全局的变量。解决方法注意命名或者把循环放进函数里隔离。坑3在嵌套函数里修改外层变量def outer(): x 10 def inner(): x 20 # 创建了inner的局部变量 inner() print(x) # 10——没变解决方法用nonlocal声明。这些坑确实存在但它们都和Python的设计一致——赋值即声明在哪里赋值就在哪里生效。理解了这一点这些坑就不难避免。类型注解Python的新“声明”虽然Python不需要声明关键字但从Python 3.5开始引入了类型注解Type Hints。name: str 张三 age: int 18 items: list[str] [a, b, c] def greet(name: str) - str: return f你好{name}这看起来有点像“声明变量”但它不是强制性的。类型注解只是提示Python解释器会忽略它们静态类型检查工具mypy、Pylance可以利用这些注解做检查你不写类型注解代码照样运行这很Python给你工具但不强迫你用。如果你想让变量“不可变”目前Python仍然没有内置机制。但第三方工具如pydantic、attrs可以帮你实现类似const的效果。一张表总结各语言的做法语言声明关键字作用域规则需要类型吗Python无赋值即声明函数级作用域否注解可选JavaScriptvar/let/constlet/const是块级var是函数级否Java类型在前块级是C类型在前块级是Govar/:块级是Rustlet/let mut块级是C类型在前/auto块级是Python在这张表里是独一份的没有任何声明关键字。如果Python引入var会怎样这是一个思想实验。假设Python加了var关键字var x 10 # 现在的写法是 x 10会发生什么现有代码全部不兼容上百万个Python项目都要改开发者面临两套写法x 10和var x 10选哪个语言的简洁性大打折扣并没有解决任何真实问题Python的赋值已经很好用了没有任何好处全是坏处。所以Python永远不会加var或let。为什么Python的设计“行得通”一个重要原因是Python是强类型动态语言。动态类型变量不需要声明类型Python在运行时能自动判断强类型不同类型不会自动转换减少了隐式错误这两个特点结合起来让“赋值即声明”变得安全且方便。你可以随时给变量赋任何类型的值x 10 # 整数 x hello # 字符串——合法 x [1,2,3] # 列表——合法如果你用Java或C这不行——类型在声明时就固定了不能随意更换。而Python灵活的类型系统配合“赋值即声明”让代码非常灵活特别适合快速原型开发。一个实战例子从JavaScript到Python如果你从JavaScript转Python记住一个简单的转换规则// JavaScript var name 张三; // 函数级作用域 let age 18; // 块级作用域 const PI 3.14; // 不可变常量# Python name 张三 # 变量作用域由赋值位置决定 age 18 # 变量 PI 3.14 # 变量按约定视为常量写Python的时候你不需要想“用var还是let”直接写名字就行。但你需要想另外一件事这个变量是在函数里赋值还是在函数外赋值这决定了它是局部变量还是全局变量。最后总结为什么Python不用var或let设计哲学赋值即声明简洁明了减少冗余语法历史因素Python从一开始就这么设计没有历史包袱避免复杂JavaScript有var/let/const三种规则Python不需要动态类型类型在运行时确定不需要提前声明实用主义没必要引入新关键字来解决不存在的问题记住一句话在Python里你不需要告诉Python“我要声明一个变量”你只需要告诉它“这个变量叫什么名字它指向什么值”。赋值本身就是声明。如果你习惯了var或let刚开始可能会不习惯。但写一段时间Python后你会感受到“赋值即声明”带来的流畅感——少敲几个关键字想写的逻辑直接写出来。这就是Python的风格让你专注于做什么而不是纠结于怎么声明。