用于字符串的内部API的Pythonic方法

时间:2017-12-21 19:12:05

标签: python json abstract-class constants readonly-attribute

问题

是否有“pythonic”(即规范,官方,PEP8批准等)方式在python内部(和外部)API中重用字符串文字?

背景

例如,我正在使用一些(不一致的)JSON处理代码(数千行),其中有各种JSON“struct s”我们汇编,解析等。其中一个反复出现的问题在代码审查期间出现的是使用相同内部参数名称的不同JSON struct,导致混淆并最终导致错误,例如:

pathPacket['src'] = "/tmp"
pathPacket['dst'] = "/home/user/out"
urlPacket['src'] = "localhost"
urlPacket['dst'] = "contoso"

这两个(示例)数据包具有数十个相同名称的字段,但它们代表非常不同类型的数据。此实现没有代码重用理由。人们通常使用代码完成引擎来获取JSON struct的成员,这最终导致难以调试的问题,因为错误输入的字符串文字导致功能问题,并且不会触发错误早前。当我们必须更改这些API时,需要花费大量时间来查找字符串文字以找出哪些JSON struct使用哪些字段。

问题 - Redux

python社区的成员之间是否有更好的解决方法?如果我在C++中执行此操作,则前面的示例将类似于:

const char *JSON_PATH_SRC = "src";
const char *JSON_PATH_DST = "dst";
const char *JSON_URL_SRC = "src";
const char *JSON_URL_DST = "dst";
// Define/allocate JSON structs
pathPacket[JSON_PATH_SRC] = "/tmp";
pathPacket[JSON_PATH_DST] = "/home/user/out";
urlPacket[JSON_URL_SRC] = "localhost";
urlPacket[JSON_URL_SRC] = "contoso";

我最初的做法是:

  • 使用abc创建无法初始化为对象的抽象基类,并使用只读常量填充它。
  • 在整个项目中将该类用作通用模块。
  • 通过使用这些常量,我可以减少猴子修补错误的可能性,因为如果拼写错误,符号将不存在,而字符串字面错误可能会漏掉代码审查。

我建议的解决方案(接受建议/批评)

from abc import ABCMeta

class Custom_Structure:
    __metaclass__ = ABCMeta

    @property
    def JSON_PATH_SRC():
        return self._JSON_PATH_SRC

    @property
    def JSON_PATH_DST():
        return self._JSON_PATH_DST

    @property
    def JSON_URL_SRC():
        return self._JSON_URL_SRC

    @property
    def JSON_URL_DST():
        return self._JSON_URL_DST

3 个答案:

答案 0 :(得分:4)

通常这样做的方式是:

JSON_PATH_SRC = "src"
JSON_PATH_DST = "dst"
JSON_URL_SRC = "src"
JSON_URL_DST = "dst"


pathPacket[JSON_PATH_SRC] = "/tmp"
pathPacket[JSON_PATH_DST] = "/home/user/out"
urlPacket[JSON_URL_SRC] = "localhost"
urlPacket[JSON_URL_SRC] = "contoso"

表示“常量”的大写是它的方式。您会在标准库中看到这一点,甚至建议在PEP8中使用

  

常量通常在模块级别定义并全部写入   带下划线分隔单词的大写字母。例子包括   MAX_OVERFLOWTOTAL

Python没有真正的常量,并且它似乎在没有它们的情况下幸存下来。如果它让您觉得在使用ABCmeta属性的类中包装它会更加舒适,请继续。实际上,我非常确定abc.ABCmeta不会阻止对象初始化。实际上,如果它 ,那么使用property将无效! property个对象属于类,但是要从实例访问。对我来说,它只是看起来像很多非常小的收获。

答案 1 :(得分:3)

我认为制作常量的最简单方法就是将它们设置为模块中的变量(而不是修改它们)。

JSON_PATH_SRC = "src"
JSON_PATH_DST = "dst"
JSON_URL_SRC = "src"
JSON_URL_DST = "dst"

然后,如果你需要从另一个模块引用它们,它们已经为你命名。

>>> that_module.JSON_PATH_SRC
'src'
>>> that_module.JSON_PATH_DST
'dst'
>>> that_module.JSON_URL_SRC
'src'
>>> that_module.JSON_URL_DST
'dst'

答案 2 :(得分:1)

创建一组常量的最简单方法是将它们放入模块中,并根据需要导入它们。例如,您可以使用

constants.py模块
JSON_PATH_SRC = "src"
JSON_PATH_DST = "dst"
JSON_URL_SRC = "src"
JSON_URL_DST = "dst"

您的代码将执行类似

的操作
from constants import JSON_URL_SRC
...
urlPacket[JSON_URL_SRC] = "localhost"

如果您希望更好地定义常量分组,您可以将它们粘贴到专用包中的单独模块中,允许您像constants.json.url.DST那样访问它们,或者您可以使用{{3} }秒。 Enum类允许您将相关的常量集合分组到单个命名空间中。您可以像这样写一个模块constants.py

from enum import Enum

class JSONPath(Enum):
    SRC = 'src'
    DST = 'dst'

class JSONUrl(Enum):
    SRC = 'src'
    DST = 'dst'

OR

from enum import Enum

class JSON(Enum):
    PATH_SRC = 'src'
    PATH_DST = 'dst'
    URL_SRC = 'src'
    URL_DST = 'dst'

你如何区分你的常数取决于你。你可以拥有一个巨大的枚举,每个类别一个或两者之间。您可以像这样访问代码:

from constants import JSONURL
...
urlPacket[JSONURL.SRC.value] = "localhost"

OR

from constants import JSON
...
urlPacket[JSON.URL_SRC.value] = "localhost"