Python技术迷

Python 的类装饰器

今天就来聊一个不少面试会考,但很多人平时压根不咋用的技术点:Python 的类装饰器(Class Decorators)。

说实话,装饰器这个东西,最早我看到的时候是有点懵的,尤其是 @property、@classmethod、@staticmethod 这些用在类里的,看着都差不多,一不小心就混了。

但如果你真理解了它们在类中扮演的角色,那你会发现——嘿,写代码变得优雅了,逻辑也清爽了不少。

装饰器:property 上线!

我第一次真香 @property,是写个图形类的时候。需求是这样的:有一个 Circle 类,必须保证半径不能为负数,偏偏这个 radius 还得支持动态赋值。

正常人的写法是这么写的:

classCircle:
def__init__(self, radius):
if radius < 0:
raise ValueError("Radius must be >= 0")
        self._radius = radius

defget_radius(self):
return self._radius

defset_radius(self, value):
if value < 0:
raise ValueError("Radius must be >= 0")
        self._radius = value

然后使用的时候:

c = Circle(5)
print(c.get_radius())
c.set_radius(-2)  # 啪,爆炸

但你看这用法,get/set 跟 Java 一模一样,写 Python 还这么用,不累吗?这时候 @property 一出手,优雅值直接拉满。

classCircle:
def__init__(self, radius: float) -> None:
        self._radius = radius

    @property
defradius(self) -> float:
return self._radius

    @radius.setter
defradius(self, value: float) -> None:
if value < 0:
raise ValueError('Radius must be >= 0!')
        self._radius = value

然后使用方式简直太舒服:

c = Circle(3)
c.radius = 5# 自动调用 setter
print(c.radius)  # 自动调用 getter

这时候你会发现,“getter/setter”三个字不见了,但控制力一点没少,还更 Pythonic。而且还自带封装性,变量前加了下划线 _radius,外部直接访问不了。

我觉得这才是 Python 设计优雅的地方,写起来不费力,用起来像变量,但背后逻辑依然强大。

静态方法 staticmethod:就你最独立

接下来是 @staticmethod。如果你在类里面写一个方法,它既不操作实例属性(没有用 self),也不操作类属性(没有用 cls),那它干嘛放类里面呢?

听着好像多余,其实就是为了代码组织性。比如我写一个 Circle 类,我想加一个方法,告诉大家“圆形”用英文怎么说,但这个跟对象本身一毛钱关系都没有。

classCircle:
    @staticmethod
defshape_string() -> str:
return'circle'

然后你就可以:

print(Circle.shape_string())  # 输出 'circle'

注意哦,这不是实例方法,不需要 Circle()。你说直接写一个模块函数也行啊,对,没毛病,但放类里逻辑更聚合,对读代码的人更友好,这是架构感的一部分。

类方法 classmethod:这个家族我罩的

接下来是我最爱用的 @classmethod,这个东西我真的觉得被低估了太多。尤其是你写一些“辅助构造函数”的时候,简直神器。

举个例子,你写一个 Circle 类,想根据圆的面积来创建一个对象,那就得从面积反推半径。

import math

classCircle:
def__init__(self, radius: float) -> None:
        self._radius = radius

    @classmethod
deffrom_area(cls, area: float) -> "Circle":
        radius = math.sqrt(area / math.pi)
return cls(radius)

使用方式非常丝滑:

c = Circle.from_area(math.pi)
print(c.radius)  # 输出 1.0

这个 cls 就是当前类,它不像 self 指向实例,它指向类本身,这样就算你后续子类化了,也可以返回子类对象,非常适合写“多形态的构造器”。

我觉得只要你写代码时,发现 __init__ 参数不够表达需求了,就该考虑是不是可以加个 @classmethod 构造器。

面试考察点

面试的时候,遇到这种你能加但不一定加的功能点,就是体现你“代码设计意识”的好机会。

我遇到过一个题:实现一个图形类,有圆、有矩形,有公共的接口,还要支持扩展。我一开始写了个基类,子类去继承。

后来我直接加了个 @classmethod def from_string(cls, s: str),输入是 "circle:5",就能解析成对象。

面试官一听眼睛亮了:“你还会设计构造器工厂啊?”我笑着说:“还能加缓存呢,要不要我顺便上个 @lru_cache?”

有时候你主动带出一个点,哪怕不是关键点,面试官都会记得你。

你以为只有这些装饰器?

当然不是了,@property、@staticmethod、@classmethod 是“类装饰器三兄弟”,但你完全可以写自己的装饰器,或者用现成的,比如:

  • functools.lru_cache:让函数支持缓存,特别适合递归计算。
  • contextlib.contextmanager:写上下文管理器不用写类。
  • @dataclass:Python 3.7 以后有了这个神器,数据类代码量省一半。

你要是能在项目里用得顺手,面试讲出来,绝对加分。

面试题最优解

题目:设计一个 Rectangle 类,要求支持:

  • 设置长宽
  • 面积属性只读
  • 提供一个 from_string("3x4") 方法生成对象

最优解参考代码:

classRectangle:
def__init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    @property
defarea(self) -> float:
return self.width * self.height

    @classmethod
deffrom_string(cls, s: str) -> "Rectangle":
        parts = s.lower().split('x')
if len(parts) != 2:
raise ValueError("Invalid format, expected 'WxH'")
        width, height = map(float, parts)
return cls(width, height)

使用方式:

r = Rectangle.from_string("3x4")
print(r.area)  # 输出 12.0

这个答案的关键点是用到了 @property 做只读面积,@classmethod 做构造辅助,非常贴合面试的考察目标。

最后,我为大家打造了一份deepseek的入门到精通教程,完全免费:https://www.songshuhezi.com/deepseek

也可以看我写的这篇文章《DeepSeek满血复活,直接起飞!》来进行本地搭建。

对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。
🔥虎哥私藏精品 热门推荐🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》。

资料包含了《IDEA视频教程》、《最全python面试题库》、《最全项目实战源码及视频》及《毕业设计系统源码》,总量高达650GB,全部免费领取