0x01 模块 or global

很多初学者有个误区,就是在Python中需要配置一个全局的参数时,首先想到的是global关键字,而实际上global不是干这个事的,global的功能是在将局部作用域的变量声明为全局的,这样可以在局部修改全局的变量。

但这种用法其实非常不好,按照函数式的规范而言,纯函数的输入应该只由输入参数确定,不应该在执行过程中引用外部变量。并且,global也不是用来进行全局配置用的。

在Python中,模块是天然的单例,模块会在项目初始化后执行一次,之后一般不重复执行,符合单例模式的特点。因此,利用模块的这一特性,将整个工程文件中需要配置的选项都配置到一个模块中,在需要用的模块中通过import导入,才是Python中全局配置正确打开方式。

虽然这种规范已经在江苟(Django)等开源框架中展示了无数遍,但“如何在Python中设置全局变量”这个问题仍然是Python社区的月经贴。

通过模块配置全局变量的试例如下,在configs.py中定义CONFIG_A和CONFIG_B。在user.py中用import导入。

这个其实是Python中的基本操作了,本来是没啥好讲的,不过在这篇文章最后我展示了一种根据json配置的动态模块,供大家参考。

0x02 单例字典

在讲模块之前,我想谈谈我尝试过另一种方式,就是自定义单例字典,具体做法是这样的。

先继承collections模块中MutableMapping,并重写相关接口。这是在Python中自定义数据类型的基本操作了,自定义完成后然后写一个装饰器将继承的类转化成单例的类。

单例模式的写法可以看Stackoverflow上关于单例模式的高票回答。

我习惯采用第一种函数装饰器的写法:

def singleton(class_):

instances = {}

def getinstance(*args, **kwargs):

if class_ not in instances:

instances[class_] = class_(*args, **kwargs)

return instances[class_]

return getinstance

@singleton

class MyClass(BaseClass):

pass

这种写法非常好理解,用一个类变量instances保存该类生成的实例,每次类被调用的时候判断一下这个类是否在instances字典里,如果不在着生成一个实例并放入instances字典。

但这个写法有个问题,装饰后的返回的不是一个类,而是一个函数,虽然Python语法讲究一切皆对象,但函数是享受不到类的诸如继承之类的特性的。

如果需要返回一个单例类的话需要用元类的写法,或者第四种类装饰器的写法。当然,具体到这里而言,这个类是继承了一个MutableMapping的,不能再继承别的元类了,元类的写法在这里不适用。

0x03 单例字典的问题

用单例字典做全局配置看着比模块炫酷,其实并没那么好用。原因是单例模式自身的一个弊病,违背了单一职责原则,这个在相关设计模式的教程里有讲到。而且,字典在这一块还有个弊病就是根本不知道需要用到的key是不是存在字典中。

单例字典是我在项目初期引入,并在项目的迭代过程中给我造成最大困扰的一个东西,在开始时几乎将所有的配置都写入到这个字典中,然后在程序运行中这个字典又被分散在程序各处的各个实例修改,运行到后面根本不知道字典里有什么,字典里的某个内容是否被修改过。不过由于GIL,倒是不需要考虑锁的问题,可能是唯一的一个幸事。

在后期将这个庞大的字典进行重构,重构的过程按照下面的方式进行:

1、将各个类中该字典的引用点,由各个方法收拢到__init__方法。

不应该

class A:

def __init__(self,):

pass

def fun_a(self):

a = Singleton()['a']

def fun_b(self):

b = Singleton()['a']

应该

class B:

def __init__(self,):

self.a = Singleton()['a']

self.b = Singleton()['b']

def fun_a(self):

a = self.a

def fun_b(self):

b = self.b

2、将各个引用点的名称统一。

不应该

class A:

def __init__(self,):

self.sets = Singleton()

def fun_a(self):

SET = Singleton()

def fun_b(self):

self.SET = Singleton()

b = self.SET['b']

应该

class B:

def __init__(self,):

self.sets = Singleton()

def fun_a(self):

SET = self.sets

def fun_b(self):

b = self.set['b']

3、将子函数中直接引用单例字典的参数放到函数的参数列表中,由调用方获取单例字典内容,由传参的方法传入被调用函数,这样做是为了满足函数式编程中纯函数的原则。

不应该这么用:

def b():

return Singleton()['c']+'a'

def a():

returrn b()

应该这样用

def b(c):

return c +'a'

def a()

c = Singleton()['c']

return b(c)

4、将单一的单例字典分成多个单例字典,并将部分单例字典转换成模块,这个就不举例了。

0x04 动态模块

模块的用法很简单,在一个文件里配置好,直接import就行。需要注意的是引用的入口最好在同一个地方。

不过模块有个地方不好就是动态修改不方便,具体到项目中去就是,该项目通过工厂模式生成了一系列产品,每个产品所需的配置参数都不一样。

这里有个办法就是每个产品都通过同一个模块来配置,然后在初始化时根据以产品名称命名的一个json文件修改模块的参数。这样就可以达到引用模块的方式不变,但模块的内容是根据json文件的内容来配置的。

详细的代码见github,主要用来动态修改模块的语句如下:

[setattr(module, k.decode('utf-8'), v) for k, v in d.items()]

其实就是通过setattr这个常用的给对象动态的添加功能的函数,d.tiems()是一个从json文件中读取的字典对象。

0x04 动态模块的优势

现在,一个配置模块的方案就成了导入configs模块,调用update_config_by_name函数,即动态修改函数,并按照相应的json文件修改模块的值。

相对于在每个类初始化时直接调用json配置变量这种方案是有好处的,定义了configs模块有助于代码的静态检查,形成了一种像C语言中.h文件和.c文件的关系,在头文件中定义相关的变量,在.c文件中实现或使用。这里就成了在configs模块中定义变量,变量的值由json文件确定,然后在其他模块中通过import实现,并且这个东西是全局共享的。当然,这个全局的意思指的是整个解释器。

这段代码还是有个坑,一般出现在单元测试中,来看两段代码:

from configs import CONFIG_A, CONFIG_B

print("use config:", CONFIG_A,CONFIG_B)

import configs

print("use config:", configs.CONFIG_A,configs.CONFIG_B)

在单元测试中由于deepcopy的问题,根据导入的层级不一样,CONFIG_X的值也发生了不一样的改变,这是个还在研究的bug。

本文地址:

专栏地址:

python怎么定义全局字典_一种Python全局配置规范以及其魔改相关推荐

  1. 一种Python全局配置规范以及其魔改

    專 欄 ❈ 丁果,Python中文社区作者.对 django.pyqt.opencv.tornado感兴趣. GitHub: https://github.com/lidingke ❈ 一.模块 or ...

  2. python类定义中__init__()_转:python学习——类中为什么要定义__init__()方法

    学习Python的类,一直不太理解为什么一定要定义init()方法,现在简要谈一下自己的理解吧. 1.不用init()方法定义类 定义一个矩形的类,目的是求周长和面积. 1 classRectangl ...

  3. python去复杂的水印_两种Python基于OpenCV的固定位置半透明水印去除方案

    OpenCV基础 OpenCV(Open Source Computer Vision Library)是一个跨平台计算机视觉库,实现了图像处理和计算机视觉方面的很多通用算法 环境搭建 #python ...

  4. python统计词频 创建字典_如何利用Python进行文本词频统计

    问题描述 Python在自然语言处理这个方面,有其天然的优势:简单,快捷.所以我们经常会遇到利用Python从一篇文档中,统计文本词频的问题.以<三国演义>这部名著为例,文中哪些人物的出场 ...

  5. python爬虫编程100例_哪种Python程序员最赚钱?爬虫数据告诉你!

    世界上有三种宝贵的东西: 知识.粮食和友谊. 那么,1 块钱,你能买到什么呢? 骑一次共享单车 买 2 包辣条 1/190 件格子衬衫 1/299 支口红 1/10099 个 iPhone XS 但是 ...

  6. python如何初始化对象数组_如何理解Python中的面向对象编程?

    (由Python大本营付费下载自视觉中国) 作者 | Radek Fabisiak 译者 | 弯月,责编 | 郭芮 出品 | CSDN(ID:CSDNnews) 现如今面向对象编程的使用非常广泛,本文 ...

  7. python3 将列表中元素转化为字典_软件测试学Python(七):Python中的变量和标准数据类型...

    上一篇文章中,凯哥给大家介绍了Python中的常量(字面值). 测试凯哥:软件测试学Python(六):Python中的常量​zhuanlan.zhihu.com 在这篇文章中,凯哥带各位学习测试的知 ...

  8. python逻辑与界面分离_一种数据与逻辑分离的Python单元测试工具

    一种数据与逻辑分离的Python单元测试工具 几个概念 TestCase TestCase是一个完整的测试单元,最小的测试执行实体,就是我们常说的测试用例. TestSuite 以某种特性将测试用例组 ...

  9. python语言的编程模式_一种基于Python语言的EDA开发平台及其使用方法与流程

    本发明涉及EDA开发的技术领域,尤其是指一种基于Python语言的EDA开发平台及其使用方法. 背景技术: 目前,主流的EDA设计语言Verilog HDL能实现完整的芯片硬件逻辑电路开发,但是其代码 ...

最新文章

  1. 任正非:华为5G芯片用在iPhone上?我持开放态度
  2. 在敏捷中应用测试驱动开发
  3. memcpy/memset函数的c语言实现
  4. spring(三)-事务管理
  5. 如何提高PHP代码的质量?第三部分 端到端/集成测试
  6. 基于Pandas的数据清洗
  7. html入门难,HTML+CSS入门之打造全网最劲富文本系列之大话技术难点与特色设计
  8. (自学笔记) 谭浩强 C语言程序设计 第五版 第二章:算法
  9. 计算机网络实验四协议分析心得,计算机网络原理实验_使用网络协议分析仪Wireshark...
  10. 常见图片文件格式简析
  11. 红米note94g版和红米note8Pro哪个好
  12. 向量组是否相关与行列式,方程组,秩的联系。
  13. 昌乐一中2021年高考成绩查询,喜 报
  14. 基于DE2的开源片上系统Freedom E310移植
  15. mysql之第n高的薪水
  16. @PostMapping和@GetMapping使用详解
  17. 利用Python理解TTF矢量字体显示原理
  18. 【lwIP(第三章)】内存管理
  19. studio one 3 机架声道设置_PotPlayer:最强电脑影音视频播放器 | 附基础设置和精美皮肤推荐...
  20. 中国单反数码相机市场现状动态及前景规模调查报告2022-2028年版

热门文章

  1. 刷新SqlServer所有视图【存储过程】
  2. 将一串随机数输入到二维坐标轴中,不断刷新JPanel,实现动态显示的效果微笑
  3. IOS开发基础之使用AFNetworking框架实现xml文件的解析
  4. C++实现黑客帝国流星雨效果
  5. office2010表格计算机,2010年职称计算机考试:Word编辑表格
  6. influxdb tsm文件_Influxdb中的Compaction操作
  7. 项目部署到tomcat6.0启动成功后访问页面报500_.net core IIS部署教程
  8. 微服务的好处与弊端_在云原生时代,就一定要用微服务吗?
  9. java序列化_夯实Java基础系列22:一文读懂Java序列化和反序列化
  10. Android开发之ApiCloud轮播图开发