在 C# 里定义类,就像写一份蓝图------构造函数、属性、方法、字段,每个都有明确的职责。
在 Python 里定义类,就像搭积木------__init__ 初始化,self 代表自己,属性想放哪放哪。
当我第一次写 Python 类发现没有 new 关键字的时候,我的内心是:"这对象怎么创建出来的?"
但写多了之后发现,Python 的类定义就是这么"随性"------约定大于约束。
基础语法对比
C# 版本:
public class Person
{
// 字段
private string name;
private int age;
// 构造函数
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
// 属性
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
// 方法
public string Greet()
{
return $"Hello, I'm {name}";
}
}
// 使用
var person = new Person("Alice", 25);
Console.WriteLine(person.Greet());
Python 版本:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Hello, I'm {self.name}"
# 使用
person = Person("Alice", 25)
print(person.greet())
对比一下:
| 对比项 | C# | Python |
|---|---|---|
| 类定义 | public class MyClass { } |
class MyClass: |
| 构造函数 | public MyClass() |
def __init__(self): |
| self/this | 隐式 this |
显式 self(必须第一个参数) |
| 属性 | 需要定义 get/set | 直接赋值就行 |
| 字段 | 必须声明类型 | 不需要声明 |
| 访问修饰符 | public/private/protected | 没有(约定 _ 开头为私有) |
| 实例化 | new MyClass() |
MyClass()(没有 new) |
C# 的类像写合同,每一条都清清楚楚;Python 的类像搭积木,放哪都行。
构造函数与初始化
C# 版本:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
// 默认构造函数
public Person() { }
// 带参数的构造函数
public Person(string name, int age)
{
Name = name;
Age = age;
}
// 静态工厂方法
public static Person Create(string name)
{
return new Person(name, 0);
}
// 构造函数链
public Person(string name) : this(name, 0) { }
}
Python 版本:
class Person:
def __init__(self, name="", age=0):
self.name = name
self.age = age
@classmethod
def create(cls, name):
return cls(name, 0)
# Python 没有构造函数重载,用默认参数
# 或者用 @classmethod 或 @staticmethod
C# 支持构造函数重载,Python 用默认参数替代。
属性 vs 直接赋值
C# 版本:
public class Person
{
private string name;
private int age;
// 完整属性
public string Name
{
get { return name; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("名字不能为空");
name = value;
}
}
// 自动属性
public int Age { get; set; }
// 只读属性
public string Description => $"{Name} is {Age} years old";
}
Python 版本:
class Person:
def __init__(self, name, age):
self._name = name # 约定为私有
self.age = age
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if not value:
raise ValueError("名字不能为空")
self._name = value
@property
def description(self):
return f"{self.name} is {self.age} years old"
C# 的属性是语言特性,Python 用 @property 装饰器模拟。
类变量 vs 实例变量
C# 版本:
public class Person
{
// 静态字段(类变量)
public static int InstanceCount = 0;
// 实例字段
public string Name { get; set; }
public Person(string name)
{
Name = name;
InstanceCount++; // 每次创建实例都加1
}
}
Console.WriteLine(Person.InstanceCount); // 0
var p1 = new Person("Alice");
Console.WriteLine(Person.InstanceCount); // 1
var p2 = new Person("Bob");
Console.WriteLine(Person.InstanceCount); // 2
Python 版本:
class Person:
# 类变量(所有实例共享)
instance_count = 0
def __init__(self, name):
self.name = name # 实例变量
Person.instance_count += 1
print(Person.instance_count) # 0
p1 = Person("Alice")
print(Person.instance_count) # 1
p2 = Person("Bob")
print(Person.instance_count) # 2
C# 用 static 关键字,Python 直接在类级别定义变量。
魔术方法(Dunder Methods)
C# 版本:
public class Vector
{
public double X { get; }
public double Y { get; }
public Vector(double x, double y)
{
X = x;
Y = y;
}
// 运算符重载
public static Vector operator +(Vector a, Vector b)
{
return new Vector(a.X + b.X, a.Y + b.Y);
}
// ToString 重写
public override string ToString()
{
return $"({X}, {Y})";
}
// 相等性比较
public override bool Equals(object obj)
{
if (obj is Vector other)
return X == other.X && Y == other.Y;
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
Python 版本:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
# 运算符重载
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
# 字符串表示
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __str__(self):
return f"({self.x}, {self.y})"
# 相等性比较
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
C# 用 operator 关键字重载运算符,Python 用魔术方法(__add__、__repr__ 等)。
dataclass(Python 3.7+)
Python 3.7+ 引入了 @dataclass,大幅简化类的定义:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
email: str = ""
def greet(self):
return f"Hello, I'm {self.name}"
# 自动生成 __init__、__repr__、__eq__ 等方法
person = Person("Alice", 25, "alice@example.com")
print(person) # Person(name='Alice', age=25, email='alice@example.com')
C# 9+ 的 record 类似:
public record Person(string Name, int Age, string Email = "");
var person = new Person("Alice", 25);
Console.WriteLine(person); // Person { Name = Alice, Age = 25, Email = }
设计哲学
C# 的类是"蓝图模式"------先定义类型,再创建实例,类型安全,编译检查。
Python 的类是"搭积木模式"------属性想放哪放哪,方法可以动态添加,灵活但需要自律。
C# 的类像是写建筑图纸,每根钢筋都有编号; Python 的类像是搭乐高,怎么搭都行,但得自己保证不塌。
坑点提醒
忘记 self------Python 类方法的第一个参数必须是 self:
class Person:
def greet(): # 错误!缺少 self
return "Hello"
# 正确写法
class Person:
def greet(self):
return "Hello"
类变量的陷阱------可变对象共享:
class MyClass:
items = [] # 类变量,所有实例共享!
a = MyClass()
b = MyClass()
a.items.append(1)
print(b.items) # [1]!b 也受影响了
# 正确写法:在 __init__ 中初始化
class MyClass:
def __init__(self):
self.items = [] # 实例变量
self 不是关键字------只是约定:
class Person:
def __init__(me, name): # 用 me 也行,但不推荐
me.name = name
迁移指南:C# 开发者最容易犯的错
这一章节的内容比较特殊,迁移指南会在后续文章中详细讨论。
一句话总结
C# 的类是"蓝图模式",Python 的类是"搭积木模式"------都能搭出漂亮的建筑,但风格完全不同。
下一篇咱们来聊聊访问修饰符------Python 的"君子协定" vs C# 的 private/protected/public。
📦 示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!