有了AI估计大多数时候都不用手写python了,但看懂python还是很重要的。
C++工程师在学 Python 的初期,都会有一种奇怪的割裂感:
本文将通过对比C++和Python的核心差异,帮助你用“对比”的方式学Python,掌握Python的编程思维,快速进入能看懂Python的阶段。
在 C++ 中,声明变量是开辟内存空间:int a = 10; 是在栈上挖了一个 4 字节的坑,里面填入 10。而在 Python 中,一切皆对象,变量只是一个指针(标签)。
a 是内存地址的别名。a = 10 是先在堆上创建一个 int 对象,然后让 a 指向它。C++:
int a = 10; // 必须声明类型a = "hello"; // 错误!类型不匹配Python:
a = 10 # 不需要声明类型a = "hello" # 完全可以,变量只是对象的标签关键理解: Python变量本质上是对象的引用,类似于C++中的void*指针。Python 的赋值操作更像是 C++ 的智能指针(std::shared_ptr)的拷贝,而不是值的拷贝。Python 不是“没类型”,而是“变量没类型”,类型属于对象。
对于Python:
你可以这样理解:
a = 10更像:
PyObject* a = new IntObject(10);变量 a 本质上只是一个指针引用。
所以 Python 可以:
a = 10a = "hello"a = [1,2,3]因为:
变的是引用指向,而不是变量类型。
C++:
int* p = new int(5);delete p; // 必须手动释放Python:
p = 5 # 自动分配内存# 不需要释放,垃圾回收自动处理关键理解: Python使用引用计数为主、标记清除为辅的垃圾回收机制。当对象引用计数降为0时,自动被删除。
Python 的内置容器几乎可以无缝对接 C++ 程序员的思维模型:
vector<int> v; v.push_back(1); | v = [1, 2, 3] | |
unordered_map<string, int> m; m["a"]=1; | m = {"a": 1, "b": 2} | |
unordered_set<int> s; s.insert(1); | s = {1, 2, 3} | |
tuple<int,string> t(1,"a"); | t = (1, "a") |
小技巧: Python 的 List 推导式(List Comprehension)可以看作是极简版的 std::transform。
Python的独特优势:
# 列表推导式 - 让C++程序员羡慕的特性squares = [x**2 for x in range(10) if x % 2 == 0]# 切片操作arr = [1, 2, 3, 4, 5]arr[1:4] # [2,3,4]arr[::-1] # 反转:[5,4,3,2,1]很多 C++ 工程师第一次看到:
arr = [1, "hello", 3.14]会感觉不可思议。
因为 C++:
std::vector<int>必须固定类型。
而 Python list 更像:
std::vector<PyObject*>里面装的是:
因此:Python 的灵活性,本质来自** “一切皆对象” **。
C++ 程序员最不习惯的通常是 Python 丢弃了分号和花括号。
作用域: C++ 靠 {} 定义作用域,Python 靠缩进。内存管理: C++ 需要 RAII 机制或手动 delete;Python 依靠 引用计数 (Reference Counting) 和 垃圾回收 (GC)。
C++ 工程师看到 Python 的对象传递,经常会懵。
C++: 值传递、引用传递、指针传递Python: 一切都是对象引用传递
def modify(lst): lst.append(4) # 修改原对象 lst = [1, 2, 3] # 重新绑定局部变量my_list = [1, 2, 3]modify(my_list)print(my_list) # 输出 [1, 2, 3, 4],不是 [1,2,3]!关键理解: 这就像C++中传递指针,但指针本身是值传递。修改指针指向的内容会影响外部,但修改指针本身不会。上面代码片段的理解关键点在于 第二步:
lst = [1, 2, 3] 并不是修改原来的列表内容,而是让局部变量 lst不再指向原来的列表,转而指向一个全新的列表。my_list 对这一切浑然不知,它依然牢牢指着原来的那个已经被追加了 4 的列表。lst 被销毁,新创建的那个 [1,2,3] 列表因为没有其他引用,也会被垃圾回收。#用 C++ 类比void modify(void* ptr) { vector<int>* v = (vector<int>*)ptr; v->push_back(4); // 修改原对象 v = new vector<int>{1,2,3}; // 只改变了局部指针的指向,外部 ptr 纹丝不动}int main() { vector<int>* my_vec = new vector<int>{1,2,3}; modify(my_vec); // my_vec 指向的仍是原 vector,内容为 {1,2,3,4}}C++:
class Dog {private: string name;public: Dog(const string& n) : name(n) {}void bark(){ cout << name << "汪汪" << endl; }};Python:
class Dog: def __init__(self, name): self.name = name # 动态添加属性 def bark(self): # self显式传递 print(f"{self.name}汪汪")主要差异:
_name表示保护,__name会名称修饰)self(相当于C++的this)Python 的“鸭子类型”到底是什么?
经典例子:
class Duck: def quack(self): print("duck")class Person: def quack(self): print("person")def make_sound(x): x.quack()Python 不关心:
只关心:“你有没有 quack()”
这和 C++ 模板其实非常像。
C++ 模板:
template<typename T>void make_sound(T x){ x.quack();}只要:
x.quack()能编译就行。
Python 的鸭子类型,本质上更接近“运行时模板”。
C++:
try { throw std::runtime_error("出错了");} catch (const std::exception& e) { std::cout << e.what() << std::endl;}Python:
try: raise RuntimeError("出错了")except RuntimeError as e: print(e) # 更简洁finally: print("不管是否异常都会执行")Python特色:else子句(没有异常时执行)、finally(清理资源)
# 错误示例def add_item(item, lst=[]): # 默认列表是共享的! lst.append(item) return lstprint(add_item(1)) # [1]print(add_item(2)) # [1, 2] 不是 [2]!# 正确做法def add_item(item, lst=None): if lst is None: lst = [] lst.append(item) return lst# 错误for item in lst: if condition: lst.remove(item) # 会导致跳过元素# 正确lst = [item for item in lst if not condition]x = 10def func(): x += 1 # UnboundLocalError!# 正确:使用global或nonlocaldef func(): global x x += 1Python性能技巧:
# 慢:纯Python循环total = 0for i in range(1000000): total += i# 快:使用内置函数total = sum(range(1000000))# 更快:使用NumPy(大数据)import numpy as nptotal = np.sum(np.arange(1000000))int x = 5; | x = 5 | |
const int X = 5; | X = 5(无真正的常量) | |
if (x > 0) { ... } | if x > 0: ... | |
for (int i=0;i<10;i++) | for i in range(10): | |
nullptr | None | |
&& | and | |
&x | id(x) | |
printfstd::format | f"x={x}" |