很多高级编程语言都提供了定义常量的方法,一个常量一旦被定义,就无法再修改,这样做的意义在于防止其他人修改一些关键参数和配置。在C++,C#, php可以使用const关键字,java可以使用final,python没有提类似供定义常量的关键字,但我们可以通过一些技术手段实现类似的效果。
对于常量,有两个要求:
思路上,定义一个Const类,重写魔法方法__setattr__
, 这个方法可以对对象的属性赋值进行干预。创建一个Const类的对象,所有想要定义的常量都是这个对象的属性,这样一来,我们还要防止生成多个Const类的对象,因此,需要实现单例模式。
魔法方法:https://blog.csdn.net/qq_62789540/article/details/127416850
单例模式有三种实现方式,元类、装饰器、魔法方法,这里我们使用魔法方法来实现单例模式。
首先,我们来回顾一下单例模式的实现方法,三种方式都回顾一下:
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
这个实现单例模式的原理是,元类只会初始化一次,但是,每实例化一个类,都会回调一次元类对象
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
使用局部变量来记录是否创建了一个类
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
数据绑定到类中,实现单例模式
在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 # 按照要求进行配置账号就可以了,注意,项目名需要是惟一的,不然会发布失败
发布完成后,我们就可以使用我们自己创建的插件了。