在 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.0
Python 的 @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); // 1
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
C# 用 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" # 这会覆盖 @property
slots 的限制------不能添加未声明的属性:
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 章示例)
💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!