变量声明这件事,C# 和 Python 差了十万八千里

0 阅读6分钟

在 C# 里,声明一个变量就像是给银行开户——你得先告诉银行(编译器)这个账户是什么类型:是储蓄账户(int)还是信用卡账户(string)?

在 Python 里,声明变量就像是往兜里塞东西——你直接塞进去就行了,至于是什么类型...解释器会自己看。

当我第一次写 x = 5 然后发现居然不需要声明类型的时候,我的内心是:"这也行?那我之前写的那些 int x = 5; 算什么?"

后来我才明白,Python 的变量只是一个"标签",可以贴在任何对象上。而 C# 的变量是一个"容器",只能装特定类型的东西。

今天咱们来聊聊变量声明和类型系统——C# 的"类型安全" vs Python 的"动态类型"。

变量声明基础对比

C# 版本:

// 显式类型声明
int x = 5;
string name = "张三";
double price = 9.99;
bool isTrue = true;

// 类型推断(var)
var y = 10;        // 编译器推断为 int
var msg = "hello"; // 编译器推断为 string

// 动态类型
dynamic z = 5;     // 运行时才确定类型
z = "hello";       // 可以改变类型

Python 版本:

# Python 天生就是"推断"的
x = 5              # int
name = "张三"      # str
price = 9.99       # float
is_true = True     # bool

# 也可以加类型注解(但解释器不理你)
y: int = 10
name: str = "李四"
对比项C#Python
变量声明int x = 5;x = 5
类型声明必须(或用 var)可选(类型注解)
类型推断var x = 5;天生就是推断的
动态类型dynamic x = 5;所有变量都是动态的
常量const int X = 5;没有 const,用全大写命名

为什么 Python 不需要声明类型?

因为 Python 是动态类型语言,变量的类型在运行时确定,而不是编译时。变量只是一个"标签",可以贴在任何对象上。

x = 5        # x 贴在整数 5 上
x = "hello"  # x 现在贴在字符串 "hello" 上
x = [1, 2]   # x 现在贴在列表上

这就像:C# 的变量是"专用抽屉",只能放一种东西;Python 的变量是"便利贴",可以贴在任何东西上。

类型注解:C# 程序员的安慰剂

C# 的类型注解是强制的:

int x = 5;
string name = "hello";

// var 是语法糖,编译器还是知道类型
var y = 10;  // y 是 int

// 接口和泛型
IEnumerable<int> numbers = new List<int> { 1, 2, 3 };

Python 的类型注解是可选的,解释器根本不理你:

x: int = 5
name: str = "hello"

# 但 IDE 会用它来做智能提示
def greet(name: str) -> str:
    return f"Hello, {name}"

# 泛型(Python 3.12+)
from typing import TypeVar
T = TypeVar('T')

def first(lst: list[T]) -> T:
    return lst[0]
特性C#Python
类型注解目的编译检查IDE 提示 + 文档
类型检查时机编译时运行时(忽略注解)
类型安全强制自愿
错误发现编译时运行时

Python 的类型注解就像考试时老师说"可以翻书,但翻了也不一定有用"。

真实场景:Python 的类型注解主要是给 IDE类型检查工具(如 mypy)用的。在大厂的 Python 项目里,类型注解是强制的,因为代码量太大,没有类型检查会出人命。

常量和只读变量

C# 有真正的常量:

// 编译时常量
const double Pi = 3.14159;
const string Greeting = "Hello";

// 运行时常量(只读)
readonly int MaxRetries = 3;

Python 没有 const,只有约定:

# 用全大写命名约定
PI = 3.14159
GREETING = "Hello"

# 这只是一个约定,你还是可以修改它
PI = 999  # 不会报错,但会被鄙视

# 如果真想保护,可以用 dataclass
from dataclasses import dataclass

@dataclass(frozen=True)
class Config:
    pi: float = 3.14159
    greeting: str = "Hello"

Python 的"常量"全靠自觉,改了也不会报错,但会被同事鄙视。

为什么 Python 不支持 const?

因为 Python 的设计哲学是**"成年人应该被信任"**。它假设你知道自己在做什么,不需要语言层面的强制约束。

变量作用域

C# 的作用域很清晰——大括号内有效:

{
    int x = 5;  // x 在这个块内有效
}
// x = 10;  // 编译错误:x 不存在

Python 的作用域遵循 LEGB 规则(Local → Enclosing → Global → Built-in):

x = "global"  # 全局变量

def outer():
    x = "enclosing"  # 外层函数变量

    def inner():
        x = "local"  # 局部变量
        print(x)     # 输出: local

    inner()

# 如果想修改全局变量
counter = 0
def increment():
    global counter
    counter += 1

Python 的作用域比 C# 复杂——它会一层层往外找变量,找到哪个用哪个。

为什么 Python 要用 LEGB 规则?

因为 Python 的函数可以嵌套定义,闭包是常见模式。LEGB 规则让内层函数可以访问外层函数的变量,这是实现闭包的基础。

类型转换

// C# 类型转换
int x = 5;
double y = (double)x;      // 显式转换
string s = x.ToString();    // 转字符串
int z = int.Parse("123");  // 字符串转 int
int w = Convert.ToInt32("456"); // Convert 方法
# Python 类型转换
x = 5
y = float(x)      # 转浮点数
s = str(x)         # 转字符串
z = int("123")     # 字符串转 int
w = bool(0)        # 转布尔(0 是 False)

C# 有多种转换方式(强转、Parse、Convert),Python 就简单粗暴——int()str()float(),一个函数搞定。

真实案例:C# 的 int.Parse("abc") 会抛异常,而 Python 的 int("abc") 也会抛异常。但 C# 还有 int.TryParse() 可以安全转换,Python 没有这个,你得用 try-except

设计哲学

C# 的哲学是类型安全——编译器帮你检查类型错误,显式声明让代码更清晰,适合大型项目和团队协作。

Python 的哲学是动态类型——类型是值的属性,不是变量的属性,鸭子类型让代码更灵活,适合快速开发。

C# 像是给每个变量办了身份证,随时可以查验; Python 像是让变量自由流动,需要的时候再看它是什么。

更深层的原因

  • C# 需要编译器提前知道类型,才能生成高效的机器码
  • Python 是解释执行的,不需要提前知道类型,运行时动态查找方法

迁移指南:C# 开发者最容易犯的错

  1. 以为没有类型就是"不安全":Python 有类型注解,只是可选
  2. 忘记 global 关键字:在函数里修改全局变量需要 global
  3. 以为变量声明后类型固定:Python 变量可以随时换类型
  4. 过度使用类型注解:Python 不需要像 C# 那样到处写类型
  5. 以为 None 是关键字:Python 里 None 是一个对象,不是关键字

推荐工具:用 VS Code + Pylance 插件,可以获得类似 ReSharper 的类型提示体验。

坑点提醒

类型注解不等于类型检查——写了注解解释器也不理你:

x: int = "hello"  # 不会报错!
print(type(x))    # <class 'str'>

变量可以改变类型——C# 的 dynamic 也没这么自由:

x = 5
print(type(x))    # <class 'int'>
x = "hello"
print(type(x))    # <class 'str'>

常量约定不是强制的——改了也不会报错:

MAX_SIZE = 100
MAX_SIZE = 999  # 不会报错,但违反约定

作用域陷阱——函数内赋值不会修改全局变量:

x = 10
def modify():
    x = 20  # 这是新变量,不是修改全局的 x
    print(x)  # 20

modify()
print(x)  # 还是 10!

一句话总结

Python 的类型注解就是给 C# 程序员的安慰剂——你可以写,但解释器不一定理你。

下一篇咱们来聊聊基本数据类型——C# 的 int 有大小限制,Python 的 int 却能无限大。还有,Python 的 True + True 居然等于 2?!


关注公众号「半亩码田」,获取更多 C# vs Python 对比内容。

回复「C#vsPython」获取本系列完整 demo 代码仓库。