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

免费编程软件「python+pycharm」 链接:pan.quark.cn/s/48a86be2f...

一个JavaScript程序员转Python的第一天

小王做了三年前端,最近开始学Python。他打开编辑器,敲下了人生中第一段Python代码:

ini 复制代码
var name = "张三"
let age = 18
const PI = 3.14

运行,报错。他懵了。

"没有var?没有let?也没有const?那Python怎么声明变量?"

他把var删掉,直接写:

ini 复制代码
name = "张三"
age = 18
PI = 3.14

居然可以。他更懵了:"不用声明就能用?这不会乱套吗?"

这个问题问到了点子上。今天我们就来聊聊:Python为什么不用varlet来声明变量? 没有声明关键字,Python是怎么保证代码不乱套的?


先看看别人家是怎么做的

在解释Python之前,先看看其他语言是怎么声明变量的。

JavaScript:三种方式

ini 复制代码
var x = 10;      // 函数级作用域,有变量提升
let y = 20;      // 块级作用域,没有变量提升
const z = 30;    // 块级作用域,不能重新赋值

三个关键字做同一件事(声明变量),但行为完全不同。新手看到这些规则,头都大了。

Java:类型在前

ini 复制代码
int x = 10;
String name = "张三";
boolean flag = true;

声明变量时必须带上类型。好处是类型安全,坏处是啰嗦。

C语言:和Java类似

ini 复制代码
int x = 10;
char *name = "张三";

声明变量时也必须指定类型。

Rust:let是必须的

ini 复制代码
let x = 10;
let mut y = 20;  // mut表示可修改

Rust用let声明变量,默认不可变,要修改必须加mut

Go:用:=或者var

go 复制代码
x := 10          // 短变量声明
var name = "张三"
var age int = 18 // 完整写法

回到Python,你会发现它做了一件很特别的事:声明变量不需要任何关键字,赋值就是声明

ini 复制代码
x = 10          # 声明一个整数
name = "张三"    # 声明一个字符串
person = {"name": "张三"}  # 声明一个字典

你写x = 10,Python就知道你要创建一个变量叫x,指向整数10。不需要var,不需要let,不需要类型。

这个设计看起来"太随意了",但其实背后有很深的设计哲学。


Python的设计哲学:赋值即声明

Python的创造者Guido van Rossum在设计变量声明时,遵循了一个核心原则:简单就是王道

在Python里,变量的声明和赋值是同一件事。你给一个名字赋值,这个名字就变成了变量。

ini 复制代码
# 这两行是等价的
x = 10           # 声明+赋值,一次性完成
# 在Python里没有单独的"声明"操作

如果你用过JavaScript,你知道var x;只是声明,x = 10;才是赋值。声明和赋值是两步。

Python把这两步合并了。你永远不需要提前说"我要用这个名字",直接用就行。

为什么这样设计?

Guido的想法是:程序员写代码时,90%的变量都是在赋值的同时声明的。既然几乎总是同时做,为什么不合并成一个操作?

这减少了键盘敲击,减少了视觉噪音,让代码看起来更干净。

ini 复制代码
# Python
name = "张三"
age = 18
items = []

# JavaScript
var name = "张三";
let age = 18;
const items = [];

Python少打了varletconst、分号,代码更短。这符合Python"简洁"的追求。


没有关键字,怎么区分变量和常量?

你可能会问:"那const呢?Python怎么定义常量?"

答案是:Python没有真正的常量。

在Python里,一切都是变量。你定义一个名字,它就可以被重新赋值。

ini 复制代码
PI = 3.14
PI = 3.14159   # 合法!Python不会阻止你

那怎么办?约定成俗:所有字母都大写的名字,视为常量,不要修改它。

ini 复制代码
MAX_CONNECTIONS = 100
API_KEY = "sk-123456"

这只是程序员之间的约定,Python解释器不会强制保护。如果你修改了它,Python也不会报错。

听起来很危险?其实还好。在团队开发中,代码审查和规范检查会帮你发现问题。而且真正的"不可变"需求,可以用property__slots__或者数据类来实现更强的保护。

为什么Python不内置const

Guido在邮件列表里解释过:他认为不需要。Python的目标用户不是写大型企业系统的,而是希望快速完成工作的。加一个const会增加语言的复杂性,收益却不大。

如果需要真正的不可变性,可以用types.MappingProxyType创建只读字典,或者用@dataclass(frozen=True)创建不可变数据类。


没有关键字,怎么处理作用域?

这是很多人好奇的点。JavaScript有varletconst来处理作用域,Python什么都没有,它怎么区分局部和全局?

Python的规则很简单:赋值决定作用域。

看例子:

ini 复制代码
x = 10  # 全局变量

def func():
    y = 20  # 局部变量,因为这里赋值了
    print(x)  # 可以读全局的x

func()
print(y)  # 报错!y是func的局部变量

Python在编译函数时,看到y = 20这个赋值语句,就把y标记为局部变量。函数内的所有y都指向这个局部变量,除非你用了globalnonlocal声明。

这和JavaScript完全不同。JavaScript的var有变量提升,let有暂时性死区,规则复杂得多。

Python的做法是:在哪里赋值,就在哪里生效

  • 在函数外赋值 → 全局变量
  • 在函数内赋值 → 局部变量
  • global声明 → 明确指向全局
  • nonlocal声明 → 明确指向外层函数

这个规则简单、一致、可预测。


对比:JavaScript声明变量的混乱史

Python不引入声明关键字,还有一个原因:避免JavaScript踩过的坑

JavaScript的变量声明历史是一部"反复横跳"的血泪史。

最早的时候,只有var

ini 复制代码
if (true) {
    var x = 10;
}
console.log(x);  // 10,x泄漏出来了

var没有块级作用域,只有函数级作用域。这导致了很多bug。

var还有变量提升

ini 复制代码
console.log(x);  // undefined,而不是报错
var x = 10;

变量声明被提升到了作用域顶部,但赋值留在了原地。这让人非常困惑。

ES6引入了letconst

ini 复制代码
if (true) {
    let y = 10;
}
console.log(y);  // 报错!y is not defined

终于有了块级作用域,但代价是语言变得更复杂了。现在JavaScript有三种声明变量的方式,各有各的规则。

Python避免了这一切

从一开始,Python就没有这些历史包袱。它的设计是统一的:

  • 赋值就是声明,没有两步操作
  • 作用域由赋值位置决定,规则清晰
  • 不存在变量提升,使用前必须赋值

Python不需要varlet,因为它从一开始就设计对了。


Python的变量声明,真的没有坑吗?

当然不是。Python的"赋值即声明"也有自己的坑。

坑1:在函数内意外创建局部变量

这是最常见的坑:

ini 复制代码
name = "张三"

def change():
    name = "李四"   # 创建了局部变量,不是修改全局

change()
print(name)  # "张三"------没变

你想修改全局name,结果创建了一个局部变量。Python不会报错,只是静默地做了另一件事。

解决方法 :如果要修改全局变量,用global声明。

坑2:在if/for里创建变量会泄漏

python 复制代码
for i in range(5):
    message = f"数字{i}"

print(message)  # "数字4"------泄漏了

因为Python没有块级作用域,message成为了所在函数或全局的变量。

解决方法:注意命名,或者把循环放进函数里隔离。

坑3:在嵌套函数里修改外层变量

csharp 复制代码
def outer():
    x = 10
    
    def inner():
        x = 20   # 创建了inner的局部变量
    
    inner()
    print(x)  # 10------没变

解决方法 :用nonlocal声明。

这些坑确实存在,但它们都和Python的设计一致------赋值即声明,在哪里赋值就在哪里生效。理解了这一点,这些坑就不难避免。


类型注解:Python的新"声明"

虽然Python不需要声明关键字,但从Python 3.5开始,引入了类型注解(Type Hints)

python 复制代码
name: str = "张三"
age: int = 18
items: list[str] = ["a", "b", "c"]

def greet(name: str) -> str:
    return f"你好,{name}"

这看起来有点像"声明变量",但它不是强制性的

  • 类型注解只是提示,Python解释器会忽略它们
  • 静态类型检查工具(mypy、Pylance)可以利用这些注解做检查
  • 你不写类型注解,代码照样运行

这很Python:给你工具,但不强迫你用

如果你想让变量"不可变",目前Python仍然没有内置机制。但第三方工具(如pydanticattrs)可以帮你实现类似const的效果。


一张表总结各语言的做法

语言 声明关键字 作用域规则 需要类型吗?
Python 无(赋值即声明) 函数级作用域 否(注解可选)
JavaScript var/let/const let/const是块级,var是函数级
Java 类型在前 块级
C 类型在前 块级
Go var/:= 块级
Rust let/let mut 块级
C++ 类型在前/auto 块级

Python在这张表里是独一份的:没有任何声明关键字


如果Python引入var会怎样?

这是一个思想实验。假设Python加了var关键字:

ini 复制代码
var x = 10   # 现在的写法是 x = 10

会发生什么?

  • 现有代码全部不兼容(上百万个Python项目都要改)
  • 开发者面临两套写法:x = 10var x = 10,选哪个?
  • 语言的简洁性大打折扣
  • 并没有解决任何真实问题(Python的赋值已经很好用了)

没有任何好处,全是坏处。所以Python永远不会加varlet


为什么Python的设计"行得通"?

一个重要原因是:Python是强类型动态语言

  • 动态类型:变量不需要声明类型,Python在运行时能自动判断
  • 强类型:不同类型不会自动转换,减少了隐式错误

这两个特点结合起来,让"赋值即声明"变得安全且方便。

你可以随时给变量赋任何类型的值:

ini 复制代码
x = 10          # 整数
x = "hello"     # 字符串------合法
x = [1,2,3]     # 列表------合法

如果你用Java或C,这不行------类型在声明时就固定了,不能随意更换。

而Python灵活的类型系统,配合"赋值即声明",让代码非常灵活,特别适合快速原型开发。


一个实战例子:从JavaScript到Python

如果你从JavaScript转Python,记住一个简单的转换规则:

ini 复制代码
// JavaScript
var name = "张三";     // 函数级作用域
let age = 18;         // 块级作用域
const PI = 3.14;      // 不可变常量
ini 复制代码
# Python
name = "张三"          # 变量(作用域由赋值位置决定)
age = 18              # 变量
PI = 3.14             # 变量(按约定视为常量)

写Python的时候,你不需要想"用var还是let"?直接写名字就行。

但你需要想另外一件事:这个变量是在函数里赋值,还是在函数外赋值?这决定了它是局部变量还是全局变量。


最后总结

为什么Python不用var或let?

  1. 设计哲学:赋值即声明,简洁明了,减少冗余语法
  2. 历史因素:Python从一开始就这么设计,没有历史包袱
  3. 避免复杂 :JavaScript有var/let/const三种规则,Python不需要
  4. 动态类型:类型在运行时确定,不需要提前声明
  5. 实用主义:没必要引入新关键字来解决不存在的问题

记住一句话

在Python里,你不需要告诉Python"我要声明一个变量",你只需要告诉它"这个变量叫什么名字,它指向什么值"。赋值本身就是声明。

如果你习惯了varlet,刚开始可能会不习惯。但写一段时间Python后,你会感受到"赋值即声明"带来的流畅感------少敲几个关键字,想写的逻辑直接写出来。

这就是Python的风格:让你专注于做什么,而不是纠结于怎么声明

相关推荐
程序员cxuan1 小时前
速度太快了!本地可以跑 DeepSeek-V4-Flash 了
人工智能·后端·程序员
阿祖zu2 小时前
芝士就是力量!开源私有化部署与 GitHub 双向同步的个人知识笔记 App
前端·后端·ios
不才不才不不才3 小时前
Spring 源码系列(16): doDispatch 全流程——一次请求的主干链路
java·后端·spring
IT_陈寒3 小时前
SpringBoot自动配置失效?这个隐藏配置坑了我一整晚
前端·人工智能·后端
搜狐技术产品小编20233 小时前
告别“黑盒”与误判:如何用“多智能体对抗辩论”重构内容安全审核系统
后端·多agent
DantyWei3 小时前
controller注册及调用时机
后端
edwarddamon4 小时前
Spring Cloud 配置热更新与 Bean 代理机制梳理
java·后端
码事漫谈4 小时前
我用 Seed Evolving 做了个 AI 小说写作工具
后端
Raas1004 小时前
MAIGateway,魔芋企业级AI网关的FinAPI成本竞争力设计
java·后端·网关·api网关·魔芋ai·finapi·mai gateway
计算机魔术师5 小时前
传统基础架构 - 微服务
大数据·后端·微服务·架构·开发工具·编程语言