文章目录

  • Python实现常量
  • 一、 概述
  • 二、 实现
  • 1、 单例模式
  • 1.1 元类
  • 1.2 装饰器
  • 1.3 魔法方法
  • 2、 常量类
  • 三、 打包

Python实现常量

一、 概述

很多高级编程语言都提供了定义常量的方法,一个常量一旦被定义,就无法再修改,这样做的意义在于防止其他人修改一些关键参数和配置。在C++,C#, php可以使用const关键字,java可以使用final,python没有提类似供定义常量的关键字,但我们可以通过一些技术手段实现类似的效果。

对于常量,有两个要求:

  1. 常量的命名必须都是大写字母,可以使用下划线连接单词
  2. 变量一旦赋值后,不可再修改

思路上,定义一个Const类,重写魔法方法__setattr__, 这个方法可以对对象的属性赋值进行干预。创建一个Const类的对象,所有想要定义的常量都是这个对象的属性,这样一来,我们还要防止生成多个Const类的对象,因此,需要实现单例模式。

魔法方法:

单例模式有三种实现方式,元类、装饰器、魔法方法,这里我们使用魔法方法来实现单例模式。

二、 实现

1、 单例模式

首先,我们来回顾一下单例模式的实现方法,三种方式都回顾一下:

1.1 元类
from typing import Any
from threading import RLock

single_lock = RLock()
class Meta(type):
    def __init__(self, *args: Any, **kwargs: Any) -> Any:
        self.__instance = None
        super(Meta, self).__init__(*args, **kwargs)


    def __call__(self, *args: Any, **kwds: Any) -> Any:
        with single_lock:
            if self.__instance is None:
                self.__instance = super(Meta, self).__call__(*args, **kwds)
        return self.__instance

class Single(metaclass=Meta):
    pass

这个实现单例模式的原理是,元类只会初始化一次,但是,每实例化一个类,都会回调一次元类对象

1.2 装饰器
from multiprocessing import Lock
from threading import RLock

single_lock = RLock()
def Singleton(cls):
    instance = {}

    def _singleton_wrapper(*args, **kargs):
        with single_lock:
            if cls not in instance:
                instance[cls] = cls(*args, **kargs)
        return instance[cls]

    return _singleton_wrapper

@Singleton
class Single():
    pass

使用局部变量来记录是否创建了一个类

1.3 魔法方法
from typing import Type, Any
from threading import RLock
from typing_extensions import Self

single_lock = RLock()

class Single():
    def __new__(cls: Type[Self], *args: Any, **kwargs: Any) -> Self:
        with single_lock:
            if not hasattr(cls, "__instance"):
                setattr(cls, "__instance", super(Single, cls).__new__(cls))  
        return getattr(cls, "__instance")

原理是,将__instance数据绑定到类中,实现单例模式

2、 常量类

constant.py文件中,写入如下代码:

from typing import Type, Any
from threading import RLock
from typing_extensions import Self
import sys

single_lock: RLock = RLock()  # 线程锁

class Const(object):
    def __new__(cls: Type[Self], *args: Any, **kwargs: Any) -> Self:
        """实现单例模式"""
        with single_lock:
            if not hasattr(cls, "__instance"):
                setattr(cls, "__instance", super(Const, cls).__new__(cls))  
        return getattr(cls, "__instance")
    
    class ConstValueError(PermissionError):
        pass
    
    class ConstCaseError(PermissionError):
        pass

    def __setattr__(self: Type[Self], __name: str, __value: Any) -> None:
        if __name in self.__dict__:
            """不能进行二次修改"""
            raise self.ConstValueError(f"不能修改常量 {__name} 的值 ")

        if not __name.isupper():
            """规范性验证"""
            raise self.ConstCaseError(f"常量名称 {__name} 必须大写")

        self.__dict__[__name] = __value  # 设置值
    
    def __getattr__(self: Type[Self], __name) -> Any:
        """通过点获取元素"""
        return self.__dict__[__name]
    
    def __setitem__(self: Type[Self], __name: str, __value: Any) -> None:
        if __name in self.__dict__:
            """不能进行二次修改"""
            raise self.ConstValueError(f"不能修改常量 {__name} 的值 ")
        if not __name.isupper():
            """规范性验证"""
            raise self.ConstCaseError(f"常量名称 {__name} 必须大写")

        self.__dict__[__name] = __value  # 设置值
    
    def __getitem__(self: Type[Self], __name: str) -> Any:
        """通过字典的键值对获取元素"""
        return self.__dict__[__name]    


sys.modules[__name__]: Const = Const()  # 将类添加到模块中

测试文件:

import constant as const

const.A = "asd"  # 定义一个常量
print(const.A)
const.A = "asdq"

三、 打包

最后,我们还可以将我们的项目进行打包,发布到pypi里面,供大家使用:

这里使用的是poetry进行包管理

在项目根目录下:

poetry publish --build  # 按照要求进行配置账号就可以了,注意,项目名需要是惟一的,不然会发布失败

发布完成后,我们就可以使用我们自己创建的插件了。