logo

Python【折腾 Python】如何用 Python 写一个常量成员变量呢?

头像
路小鹿
74阅读1评论4 个月前

背景

假如,我们需要写一个创建 User 的方法,很自然地,我们写了一个 UserService 类,把需要实现的方法放进去。就像这样:

class UserService:
    @classmethod
    def create(cls, username: str, email: str):
        pass

接下来实现创建逻辑就好啦。
可是烦人的产品同学又加了一个要求:用户名长度不能超过 20 个字符。
好吧,我们需要加上一个判断了。

if len(username) > 20:
    raise Exception(....)

我们对代码颜值有严格的要求,绝对不允许把 20 这样随便地放在代码中。我们会声明一个“常量”,并且在需要它的地方使用它。

USERNAME_MAX_LENGTH = 20

if len(username) > USERNAME_MAX_LENGTH:
    raise Exception(....)

最终代码写成了这样

class UserService:
    USERNAME_MAX_LENGTH: = 20

    @classmethod
    def create(cls, username: str, email: str):
        if len(username) > cls.USERNAME_MAX_LENGTH:
            raise Exception(....)
        pass

啰嗦了那么久,现在才说到了正题:Python 并不像其他语言有 “const” 关键字,这里的 USERNAME_MAX_LENGTH 并不是真的常量呀。怎么样从代码实现的层面彻底避免其他地方不小心修改它呢?(比如手误写了一个 if USERNAME_MAX_LENGTH = 10: ,少写了一个等号)

实现

众所周知,在 Python 中一切皆对象。 UserService 表面上是个浓眉大眼的类,实质上也只个被元类 type 实例化的对象罢了。
作为一个对象,有人要改它的属性 USERNAME_MAX_LENGTH ,就得通过它的 __setattr__ 方法来改。直接从元类入手,禁掉 __setattr__ ,我们的变量就会安全了。

很容易我们就完成需要的元类了:

class Const(type):
    def __setattr__(cls, key, value):
        raise Exception(f"variable '{key}' declared const here")

接下来,让我们自定义的元类来生成 UserService :

class UserService(metaclass=Const):
    USERNAME_MAX_LENGTH: = 20

    @classmethod
    def create(cls, username: str, email: str):
        if len(username) > cls.USERNAME_MAX_LENGTH:
            raise Exception(....)
        pass

大功告成!

其他

在 Python 3.8 版本中,增加了 Final 类型标示,如果有写常量的需求,记得加上它。

相关资料:

[1] What are metaclasses in Python? - stackoverflow.com
[2] typing.Final - docs.python.org

加载中…
精选评论
头像
2 个月前NaNa

Final 这个妙啊,真是妙