C#转Python第3.6篇:Python 的 @property 比 C# 的 get/set 更灵活

在 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 章示例)

💬 欢迎点赞、收藏、转发,你的支持是我持续创作的动力!

相关推荐
信誓旦旦的程序猿5 分钟前
【Python量化实战宝典 #21】北向资金跟随手册:聪明钱择时信号(Python 可跑)
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
严谨的麻辣烫21 分钟前
静态 ISP 代理请求超时怎么排查?Python 与 curl 实战
python·http·requests·1024程序员节·网络排障
吴可可1231 小时前
C#调用摄像头拍照的三种API方案
c#
FinelyYang1 小时前
考试系统简答题 AI 阅卷:覆盖度驱动、异步评分与人机协同落地实践
java·大数据·人工智能
hhhjhl1 小时前
Yup 跨端三方库仓颉适配全流程
java·开发语言
用户3126874877202 小时前
Java 对象内存布局:压缩指针、对齐填充与 JOL 实测
java
KeepRobots2 小时前
11-01 温哥华不回拨:用 60 行 Python 查你的工单系统会不会差一小时(KeepRobots 工程笔记)
python
xiao77mwq2 小时前
你以为 OCR 慢?实测 81.3 s 里全是故障代价:如何把「定格态」消掉,让耗时从 6.5 s 到 13 倍差距显出原形
python
东华果汁哥2 小时前
【Spring AI 实战】从零开始调用大模型接口,构建 Java AI 应用
java·人工智能·spring
深圳市益普科技有限公司2 小时前
半导体良率管理YMS:良率与设备参数的相关性,先拉哪几组数据
人工智能·python·算法