在 C# 里定义属性,用{ get; set; }——编译器帮你生成 backing field,语法简洁。
在 Python 里定义属性,用@property——装饰器实现 getter/setter,灵活但需要手动管理。
C# 的属性是"语法糖",Python 的属性是"装饰器魔法"。刚用的时候觉得 Python 的写法啰嗦,但用久了会发现它更灵活——你可以在 getter 里做任何事情。
基础语法对比
C# 版本:
public class Person { // 自动属性 public string Name { get; set; } public int Age { get; set; } // 只读属性 public string Description => $"{Name} is {Age} years old"; // 带验证的属性 private string _email; public string Email { get => _email; set { if (string.IsNullOrEmpty(value)) throw new ArgumentException("邮箱不能为空"); _email = value; } } // 计算属性 public bool IsAdult => Age >= 18; }Python 版本:
class Person: def __init__(self, name, age, email): self.name = name self.age = age self._email = email # 用 _ 前缀表示私有 @property def email(self): return self._email @email.setter def email(self, value): if not value: raise ValueError("邮箱不能为空") self._email = value @property def description(self): return f"{self.name} is {self.age} years old" @property def is_adult(self): return self.age >= 18对比一下:
| 特性 | C# | Python |
|---|---|---|
| 自动属性 | { get; set; } | 不需要(直接赋值) |
| 只读属性 | { get; } | @property |
| 计算属性 | => expression | @property |
| 验证 | 在 setter 里写 | 在@xxx.setter里写 |
| backing field | 编译器自动生成 | 手动管理_xxx |
C# 的属性是"语法糖",Python 的属性是"装饰器魔法"。
@property 详解
C# 版本:
public class Temperature { private double _celsius; public double Celsius { get => _celsius; set => _celsius = value; } public double Fahrenheit { get => _celsius * 9 / 5 + 32; set => _celsius = (value - 32) * 5 / 9; } public double Kelvin { get => _celsius + 273.15; set => _celsius = value - 273.15; } }Python 版本:
class Temperature: def __init__(self, celsius=0): self._celsius = celsius @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): self._celsius = value @property def fahrenheit(self): return self._celsius * 9 / 5 + 32 @fahrenheit.setter def fahrenheit(self, value): self._celsius = (value - 32) * 5 / 9 @property def kelvin(self): return self._celsius + 273.15 @kelvin.setter def kelvin(self, value): self._celsius = value - 273.15 # 使用 t = Temperature(100) print(t.celsius) # 100 print(t.fahrenheit) # 212.0 t.fahrenheit = 32 print(t.celsius) # 0.0Python 的@property让你可以在属性访问时执行自定义逻辑。
描述符(Descriptor)
C# 版本:
// C# 没有直接的描述符概念 // 但可以用属性和接口实现类似功能 public interface IValidatable { bool IsValid(); } public class Person : IValidatable { private string _name; public string Name { get => _name; set { if (string.IsNullOrEmpty(value)) throw new ArgumentException("名字不能为空"); _name = value; } } public bool IsValid() => !string.IsNullOrEmpty(Name); }Python 版本:
# 描述符是一个实现了 __get__、__set__、__delete__ 方法的对象 class Validated: def __init__(self, validator): self.validator = validator def __set_name__(self, owner, name): self.name = name def __get__(self, obj, objtype=None): if obj is None: return self return getattr(obj, f'_{self.name}', None) def __set__(self, obj, value): if not self.validator(value): raise ValueError(f"{self.name} 验证失败") setattr(obj, f'_{self.name}', value) class Person: name = Validated(lambda v: bool(v)) # 非空验证 age = Validated(lambda v: isinstance(v, int) and v >= 0) # 正整数验证 def __init__(self, name, age): self.name = name # 触发验证 self.age = age # 使用 p = Person("Alice", 25) # OK # p = Person("", 25) # ValueError: name 验证失败 # p = Person("Alice", -1) # ValueError: age 验证失败Python 的描述符让你可以创建可复用的属性验证逻辑。
slots
C# 版本:
// C# 没有直接的 __slots__ 概念 // 但可以用 readonly 或 struct 实现类似效果 public readonly struct Point { public double X { get; } public double Y { get; } public Point(double x, double y) { X = x; Y = y; } }Python 版本:
# __slots__ 限制实例属性,节省内存 class Point: __slots__ = ['x', 'y'] def __init__(self, x, y): self.x = x self.y = y p = Point(1, 2) print(p.x, p.y) # 1 2 # p.z = 3 # AttributeError: 'Point' object has no attribute 'z' # 好处:节省内存,防止随意添加属性 import sys p = Point(1, 2) print(sys.getsizeof(p)) # 比没有 __slots__ 的类小__slots__让你限制实例可以拥有的属性,节省内存。
类属性 vs 实例属性
C# 版本:
public class Person { // 静态字段(类属性) public static int InstanceCount = 0; // 实例字段 public string Name { get; set; } public Person(string name) { Name = name; InstanceCount++; } } Console.WriteLine(Person.InstanceCount); // 0 var p1 = new Person("Alice"); Console.WriteLine(Person.InstanceCount); // 1Python 版本:
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) # 1C# 用static关键字,Python 直接在类级别定义。
设计哲学
C# 的属性是"编译时特性"——编译器生成 backing field,语法简洁,类型安全。
Python 的属性是"运行时魔法"——装饰器实现 getter/setter,灵活但需要手动管理。
C# 的属性像是工厂生产线,标准、高效; Python 的属性像是手工作坊,灵活、可定制。
坑点提醒
@property 不要定义同名属性——会覆盖:
class Person: @property def name(self): return self._name @name.setter def name(self, value): self._name = value # 不要这样定义同名属性! # name = "Alice" # 这会覆盖 @propertyslots的限制——不能添加未声明的属性:
class Point: __slots__ = ['x', 'y'] p = Point(1, 2) # p.z = 3 # AttributeError描述符的优先级——数据描述符优先于实例字典:
class Descriptor: def __get__(self, obj, objtype=None): return "descriptor" def __set__(self, obj, value): pass class MyClass: attr = Descriptor() obj = MyClass() obj.__dict__['attr'] = 'instance' # 实例字典 print(obj.attr) # "descriptor"(数据描述符优先)@property 的性能——每次访问都会调用函数:
class Person: @property def name(self): return self._name p = Person() p._name = "Alice" # 每次访问 p.name 都会调用 name 方法 # 频繁访问时考虑缓存迁移指南:C# 开发者最容易犯的错
这一章节的内容比较特殊,迁移指南会在后续文章中详细讨论。
一句话总结
C# 的属性是"编译时特性",Python 的属性是"运行时魔法"——都能实现属性封装,但机制不同。
第三阶段完结!下一篇进入第四阶段:异常处理 + 文件IO + 测试。
📦示例代码:C# 转 Python 全系列配套练习代码(含 48 章示例)
GitHub:GitHub - LadyKiller1025/csharp-python-demos: C# vs Python 学习系列 - Demo 代码合集 · GitHub
Gitee:https://gitee.com/qakjhzx/csharp-python-demos
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!