1. 前言
从 JavaScript 的角度学习 Python,你会发现两者有很多相似之处(都是动态、解释型、多范式语言),但语法和设计哲学上也有显著差异。下面我以一名 JavaScript 开发者的视角,带你快速理解 Python 的核心语法和概念,相信通过本文的学习,你就算入门 Python 了。
2. 核心差异速览
| 特性 | JavaScript | Python |
|---|---|---|
| 代码块 | { } |
缩进(通常4空格) |
| 语句结束 | ; 可选 |
换行 |
| 布尔值 | true / false |
True / False |
| 空值 | null / undefined |
None |
| 逻辑运算 | && 两个竖线 ! |
and or not |
| 数组 | Array |
list |
| 对象 | Object |
dict |
| 当前实例 | this |
self(方法第一个参数) |
| 构造函数 | constructor() |
__init__() |
| 继承 | extends |
class Child(Parent): |
3. 变量声明
JavaScript 使用 var、let、const 声明变量,而 Python 直接赋值即可,无需关键字。Python 变量的作用域由所在代码块(函数、模块)决定,没有块级作用域。
js
// JavaScript - 需要声明关键字
const name = 'Alice' // 常量,不可重新赋值
let age = 25 // 块作用域变量
var city = 'Beijing' // 函数作用域变量(旧式)
py
# Python - 直接赋值,动态类型
name = 'Alice' # 变量无需关键字,作用域由所在代码块决定
age = 25
city = 'Beijing' # 约定常量全大写,但实际仍可修改(如 PI = 3.14)
4. 代码块与缩进规则
Python 使用缩进 (通常4个空格)表示代码块,不再使用花括号 {}。缩进必须严格一致,不能混用空格和制表符。
js
// JavaScript - 使用花括号 {} 界定代码块
if (score >= 90) {
let grade = 'A';
console.log('优秀');
} else {
let grade = 'B';
console.log('加油');
}
// 花括号内缩进仅为可读性,语法不强制
py
# Python - 使用缩进表示代码块
if score >= 90:
grade = 'A' # 缩进内的代码属于if块
print('优秀')
else:
grade = 'B'
print('加油')
# 缩进必须一致,不能混用空格和制表符
4. 布尔值、空值与逻辑运算
Python 的布尔值首字母大写(True/False),空值只有 None(没有 undefined)。逻辑运算符使用英文单词 and、or、not。
js
// JavaScript - 布尔小写,空值有null/undefined
let isDone = true; // 或 false
let empty = null; // 主动赋空
let notDefined; // undefined
if (isDone && !empty) { // 逻辑运算符 && || !
console.log('执行');
}
py
# Python - 布尔首字母大写,空值只有None
is_done = True # 或 False
empty = None # 唯一空值
# 变量必须赋值后才能使用,无undefined
if is_done and not empty: # 逻辑运算符 and or not
print('执行')
5. 数组与对象 / 列表与字典
Python 的列表(list)对应 JavaScript 的数组,字典(dict)对应 JavaScript 的对象。但方法名称和访问方式有所不同。
js
// JavaScript - 数组和对象
let arr = [1, 2, 3];
arr.push(4); // 添加元素
let obj = { name: 'Alice', age: 25 };
obj.city = 'Beijing'; // 添加属性
console.log(obj.name); // 点语法访问
py
# Python - 列表(list)和字典(dict)
arr = [1, 2, 3]
arr.append(4) # 添加元素
obj = {'name': 'Alice', 'age': 25}
obj['city'] = 'Beijing' # 字典键访问(必须用中括号)
# 点语法不适用于字典,只能用 obj['key']
print(obj['name'])
6. 函数定义
Python 使用 def 关键字定义函数,支持默认参数、可变参数(*args、**kwargs)。匿名函数用 lambda 表达式,但只能写单行。
js
// JavaScript - function 或箭头函数
function add(a, b) {
return a + b;
}
const multiply = (a, b) => a * b; // 箭头函数
py
# Python - def 关键字
def add(a, b):
return a + b
multiply = lambda a, b: a * b # lambda 仅限单行表达式
# 可变参数示例
def log(*args, **kwargs):
print(args, kwargs)
7. 类定义(构造函数、继承)
Python 的类使用 class 关键字,构造函数为 __init__,所有实例方法第一个参数必须是 self(相当于 JavaScript 的 this)。继承时在类名后加括号指定父类。
js
// JavaScript - ES6 class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hi, I'm ${this.name}`;
}
}
class Student extends Person {
constructor(name, age, grade) {
super(name, age);
this.grade = grade;
}
}
py
# Python - class,self 显式
class Person:
def __init__(self, name, age): # 构造函数
self.name = name
self.age = age
def greet(self): # 方法必须带 self
return f"Hi, I'm {self.name}"
class Student(Person): # 继承
def __init__(self, name, age, grade):
super().__init__(name, age) # 调用父类构造函数
self.grade = grade
8. 控制流(if / for)
Python 的 if 条件语句不需要括号,但条件后必须跟冒号。for 循环直接迭代可迭代对象,类似 JavaScript 的 for...of。range() 用于生成数字序列。
js
// JavaScript - if 和 for
if (x > 0) {
console.log('正数');
} else {
console.log('非正数');
}
for (let i = 0; i < 5; i++) {
console.log(i);
}
for (let item of arr) { // for...of 遍历值
console.log(item);
}
py
# Python - if 和 for
if x > 0:
print('正数')
else:
print('非正数')
for i in range(5): # range 生成数字序列(0-4)
print(i)
for item in arr: # 直接遍历可迭代对象
print(item)
9. 异常处理
Python 使用 try/except 捕获异常,可以指定异常类型,并支持 else 子句(无异常时执行)。finally 无论是否异常都会执行。
js
// JavaScript - try/catch
try {
let result = 10 / 0;
if (!isFinite(result)) throw new Error('除零错误');
} catch (error) {
console.log(error.message);
} finally {
console.log('清理工作');
}
py
# Python - try/except
try:
result = 10 / 0
except ZeroDivisionError as e:
print('除零错误', e)
except Exception as e:
print('其他错误', e)
else:
print('没有异常发生')
finally:
print('清理工作')
10. 异步编程
Python 通过 asyncio 库和 async/await 实现异步编程,需要显式运行事件循环。与 JavaScript 不同,Python 的异步是协作式多任务,需要主动让出控制权(如 await asyncio.sleep())。
js
// JavaScript - async/await
async function fetchData() {
let response = await fetch('url');
let data = await response.json();
return data;
}
fetchData().then(console.log);
py
# Python - asyncio
import asyncio
async def fetch_data():
await asyncio.sleep(1) # 模拟IO
return 'data'
async def main():
result = await fetch_data()
print(result)
asyncio.run(main()) # 启动事件循环
11. 模块和导入导出
JavaScript(ES6)使用 import / export 管理模块,而 Python 使用 import 和 from ... import ...。每个 Python 文件都是一个模块,文件夹可以通过包含 __init__.py 成为包(Python 3.3+ 可省略,但推荐保留)。
js
// JavaScript - ES6 模块
// 导出 (math.js)
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export default class Calculator { ... }
// 导入 (main.js)
import { PI, add } from './math.js';
import Calculator from './math.js';
py
# Python - 模块导入
# 导出 (math.py)
PI = 3.14159
def add(a, b):
return a + b
class Calculator:
pass
# 导入 (main.py)
import math
print(math.PI)
print(math.add(1, 2))
from math import PI, add
print(PI)
from math import Calculator
注意:
- Python 的
import会执行整个模块,导入后的名称通过模块名访问(如math.PI)。 - 可以使用
as别名:import math as m或from math import PI as pi。 - 没有默认导出的概念,但可以通过
__all__控制from module import *的行为。
以上是从 JavaScript 角度快速理解 Python 语法的核心要点。掌握这些差异后,你就可以开始编写 Python 代码,并逐步熟悉更多高级特性(如装饰器、生成器等)。两种语言各有千秋,灵活运用会让你的编程之路更加宽广。
附一:为什么有 \ 在代码后面
这个 \ 是 Python 的行续接符(line continuation character) ,表示当前代码行还没结束,下一行是它的延续。
例如:
py
client = lark.Client.builder() \
.app_id(APP_ID) \
.app_secret(APP_SECRET) \
.log_level(LogLevel.INFO) \
.build()
如果不加 \,Python 会把每一行视为独立语句,导致语法错误。加了 \ 后,Python 知道这几行实际上是同一条语句。
用括号包裹的方式更 Pythonic,不需要 \,例如:
py
client = (lark.Client.builder()
.app_id(APP_ID)
.app_secret(APP_SECRET)
.log_level(LogLevel.INFO)
.build())
两者功能完全一致,\ 写法在 Builder 模式中也很常见,只是要注意 \ 后面不能有空格或注释,否则会报错。我们更推荐使用括号包裹的方式。
附二:__init__.py 文件在 Python 中的作用
1. 标记目录为 Python 包
在 Python 中,一个普通的文件夹如果想要被当作包来导入,必须包含一个 __init__.py 文件(在 Python 3.3 之前这是强制性的)。例如,假设你有这样的目录结构:
py
mypackage/
__init__.py
module1.py
module2.py
当你执行 import mypackage 或 from mypackage import module1 时,Python 会识别出 mypackage 是一个包,因为它下面有 __init__.py。如果没有这个文件,Python 就会把该文件夹当作一个普通的目录,无法从中导入模块(除非使用命名空间包,见后文)。
2. 执行包的初始化代码
当包被首次导入时,__init__.py 文件中的代码会被自动执行。你可以在其中放置包级别的初始化逻辑,例如:
- 设置包级别的全局变量
- 导入包内其他子模块或子包
- 配置日志
- 检查运行环境、版本等
例如,在 mypackage/__init__.py 中:
py
# mypackage/__init__.py
print("Initializing mypackage")
VERSION = "1.0.0"
from . import module1
from .module2 import some_function
这样,当用户 import mypackage 时,会看到打印信息,并且可以直接通过 mypackage.some_function 调用 module2 中的函数,而不必写 mypackage.module2.some_function。
3. 控制包的导入行为
定义 __all__
在 __init__.py 中定义一个名为 __all__ 的列表,可以指定当用户使用 from package import * 时,哪些子模块会被导入。
例如:
py
# mypackage/__init__.py
__all__ = ["module1", "module2"]
那么 from mypackage import * 只会导入 module1 和 module2,其他未列出的模块则不会被导入。
延迟导入
有时为了避免循环导入或提高启动速度,可以在 __init__.py 中使用函数或属性来延迟导入子模块:
py
# mypackage/__init__.py
def get_module1():
from . import module1
return module1
4. 简化导入路径
通过 __init__.py 将子模块中的对象"提升"到包级别,可以让用户使用更简洁的导入语句。例如:
py
# mypackage/__init__.py
from .module1 import MyClass
from .module2 import my_function
之后用户可以直接写:
py
from mypackage import MyClass, my_function
而不需要知道 MyClass 具体在 module1 中,my_function 在 module2 中。这种设计在大型库(如 numpy、pandas)中非常常见。
5. Python 3.3+ 的变化:命名空间包
从 Python 3.3 开始,引入了 隐式命名空间包 (PEP 420)。如果一个目录下没有 __init__.py,但它是 Python 路径上的一个有效目录,并且包含其他 Python 模块或子目录,那么它会被视为一个 命名空间包(namespace package)。命名空间包允许将多个目录合并成一个逻辑上的包,常用于将大型项目拆分到多个代码仓库。
重要区别:
- 传统包(regular package)必须包含
__init__.py,可以有初始化代码。 - 命名空间包不需要
__init__.py,但不能包含任何初始化代码(因为它只是多个目录的逻辑合并)。
因此,如果你需要执行包的初始化代码,或者想要明确地将一个目录标记为包(尤其是为了兼容旧版 Python),仍然建议保留 __init__.py。
理解 __init__.py 的作用,有助于你更好地理解 Python 项目。
总结
是不是通过对比学习,突然发现 Python 其实是一门非常简单的语言呢?加油吧,前端们。在 AI 时代掌握多一门语言很重要,并且说 Python 的是 AI 的原生语言也不为过。
我们可以在各种前端技术群里看到很多前端觉得学习 Python 太麻烦了,所以还继续学习 Node.js,当然如果你是一名 Node.js 专家级前端,那么你继续学习使用 Node.js 是没有什么问题的。
如果你只是一名普通的前端,只想找一份工作,那么在这个由后端统治的世界里,即便现在有很多以 Node.js 为主的产品(比如 OpenClaw、Claude Code),但专门的 Node.js 岗位依然少得可怜。尤其是国内,基本没有大一点的公司会把 Node.js 作为核心开发语言。
当然如果你是所谓的独立开发者,那么无论是 Node.js 还是 Python 还是 Java 甚至 PHP 都无所谓了。但从打工人找工作的角度看,就不一样了。
我是程序员Cobyte,欢迎添加 v: icobyte,学习交流 AI 全栈。