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

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

相关推荐
老郑聊AI业财智造1 小时前
Spring AI 技术架构与源码分析
java·人工智能·后端·spring·架构·软件工程
省长1 小时前
别人绕过我的网关直接调用资源服务怎么办?使用 Sa-Token 解决:网关转发鉴权、RPC调用鉴权
java·后端·开源
MetaLite1 小时前
SpringBoot异常处理-到底该转换还是继续抛-入口层与调用层不能一刀切
java·spring boot·后端
欧叶冲冲冲1 小时前
Python常见数据结构的CRUD(LeetCode高频版速查)
数据结构·python·leetcode
钱栈up1 小时前
Mac 开发机一键发版不用切环境:我这样改造了团队的后端部署脚本Maven编译卡住20分钟?我靠两步定位到2处隐蔽编译错误
开发语言·python·macos
CSND7401 小时前
DeepSeek Harness实测+入门教程
人工智能·python
gb42152871 小时前
ai中agent,skill Package,skill,tool,prompt,mcp等等概念的关系
python
淼澄研学2 小时前
Python实战:基于LangChain与Chroma构建企业级RAG知识库问答系统
开发语言·python·langchain
赵广陆2 小时前
Spring AI的聊天模型
java·人工智能·spring