【问题标题】:Python constantsPython 常量
【发布时间】:2014-04-16 11:18:22
【问题描述】:

我正在使用 Django 进行开发,我们对是否应该在项目中使用或不使用常量有一些疑问。确切的情况是在任何地方都使用常量(我们知道 Django 中的常量并不是真正的只读)。

有两种情况,我想听听您的意见,哪个更适合您以及为什么:

场景 1(使用常量)

常量.py

​​>
class CONST():
    def NAME(): return "name"
    def SURNAME(): return "surname"
    def ZIPCODE(): return "zipcode"
    def CITY(): return "city"
    def CREATED(): return "created"

admin.py

​​>
from constants import CONST
class RegisterAdmin(admin.ModelAdmin):
    list_display = (CONST.NAME(),CONST.SURNAME(),CONS.ZIPCODE())
    list_filter = [CONST.ZIPCODE(),CONST.CITY()]
    search_fields = [CONST.NAME(), CONST.SURNAME()]
    date_hierarchy = CONST.CREATED()

models.py

​​>
from constants import CONST
class Register(models.Model):
    name = models.CharField(CONST.NAME(), max_length=25)
    surname = models.CharField(CONST.SURNAME(), max_length=25)
    zipcode = models.IntegerField(CONST.ZIPCODE())
    city = models.CharField(CONST.CITY(),max_length=20)

...以及您使用文本的任何视图等都将使用内容...

场景 2(无常量)

admin.py

​​>
class RegisterAdmin(admin.ModelAdmin):
    list_display = ("name","surname","zipcode")
    list_filter = ["zipcode","city"]
    search_fields = ["name","surname"]

models.py

​​>
class Register(models.Model):
    name = models.CharField("name", max_length=25)
    surname = models.CharField("surname", max_length=25)
    zipcode = models.IntegerField("zipcode")
    city = models.CharField("city",max_length=20)

我最喜欢第二种情况(我从 2004 年开始编写 python),对我来说它看起来更高效、清晰且易于理解。第一个场景(由现在编写 Python 代码的 Java/PHP 程序员提出)的优点是它可以帮助开发人员检测它在编写“常量”时犯了错误,因此更容易检测错误,也更容易和更快在不重构源代码的情况下对这类文本进行“大改动”。

我想知道您将编写或使用哪些源代码以及为什么。

谢谢,

【问题讨论】:

  • 在我看来场景 1 太可怕了。
  • 您根本不应该在那里使用“常量”,因为 数据库架构 也取决于这些。更改您的常量,您的架构需要迁移。第一种情况与 Pythonic 相差甚远。即使是简单的属性也会比方法做得更好。您的 Java/PHP 程序员出于类似 YAGNI 的原因引入了复杂性; 你不会需要它
  • 我会担心任何可以更轻松地在架构上实施“大规模更改” 的事情。

标签: python django constants


【解决方案1】:

场景 1 很糟糕。不幸的是,我非常清楚与正在学习 python 的 Java/PHP 开发人员一起工作的问题。

也许您可以通过提议使用python enums 来解决他们的问题,从而与这些人妥协。这些是在 python 3.4+ 中内置的,并且早在 2.4 就已经是backported

from enum import Enum

class Constant(Enum):
    name = "name"
    surname = "surname"
    zipcode = "zipcode"
    city = "city"
    created = "created"

现在您可以更改“值”,例如在枚举定义中将 zipcode 更改为“potato”,同时在源代码的其他任何地方仍然使用名称 Constant.zipcode.value

【讨论】:

  • 他们的目的是让它保持“恒定”,但在 Python 中没有 contants,我做了一个 Contant.name=3 并且它工作并将值更改为 3。所以我仍然没有看到从 model/admin/from/etc... 声明中取出这种常量的意义。
  • 是的。所以呢?场景 1 也不会阻止这种情况。
  • 如果你认为你需要的话,你可以使用 python 属性来近似只读..
  • 另外,您可以将它们作为普通旧类的属性,然后覆盖 __setattr__ 以引发异常。但这并没有真正为您的代码增加价值。
  • @wim 让字符串常量类继承Enum有什么用?
【解决方案2】:

使用常量有时很好,但不是这里

一个,字段的第一个参数是它的详细名称,如果缺少它,则默认使用该字段的名称。因此,对其详细名称和字段名称使用相同的常量正是完全不需要详细名称的场景。

做事

name = CharField(max_length=25)

name = CharField("A beautiful description for a name field", max_length=25)

但是把“名字”放在那里是完全多余的,因此是错误的。不要重复自己。

其次,您的常量实际上用于两个您不想同时更改的不同事物:您在 admin.py 中为属性名称使用相同的常量,但在 models.py 中为详细名称使用相同的常量。他们不会保持不变。

第三,只有当值可能在一天内改变,或者如果它的含义不是很明显,例如,使用常量才真正有用。 PI=3.14。

但是NAME = "name" 并没有让任何事情变得更明显,如果它更改为NAME = "description",它会立即变得具有误导性。在这种情况下,您可能也想更改常量的名称,不是吗?那你为什么要使用常量呢?

这意味着更改 const.py 中的某些内容会更改 Django 期望的数据库结构。有一天,开发人员会绊倒它;将字段名称保留在 models.py 中。

最后,在您使用模型的每个地方,您都需要实际的字段。毕竟,Register 实例只是一个类实例,您可能希望在任何地方使用像register.name 这样的字段。你不会把其他类的属性名放在常量里吧?

只需使用字符串。

【讨论】:

  • 问题不在于冗长的名称,我编写了这样的示例以使其看起来更简单。真正的问题是关于常量是/否以及在哪里。
  • 一个典型的用法是字段的choices 参数,它们通常被定义为它们所属模型的类属性,在大写字母中,因此其他人知道它们是常量。否则,Django 的哲学很大程度上依赖于“不要重复自己”,因此常量没有太多用处——一切都应该已经有了一个单一的定义点。典型的配置常量可以进入Django的settings.py(或conf.py,见django-appconf应用)
【解决方案3】:

场景 1 没有意义。您使用常量来指定字段的verbose_name,并且在list_display 选项中使用它们。 list_display 选项采用字段名列表,而不是字段的 verbose_name

所以你根本不需要常量。字段名称是您的“常量”。

如果您在某些情况下需要一些“常量”,则不应使用方法,而应使用属性,而应使用普通的类/属性名称约定(因此不要使用大写字母):

class SomeConstants():
    name = "name"
    surname = "surname"
    zipcode = "zipcode"
    city = "city"
    created = "created"

Java/PHP 人员可能会说 “是的,但是如果需要生成其中一个常量怎么办?那么你需要一个方法!”。好吧,当然,所以你把它设为property

class SomeConstants():

    @property
    def full_name(self):
        return "{} {}".format(self.name, self.surname)

由于方法上面有@property这一行,调用SomeConstants().full_name会执行并返回该方法的结果。

【讨论】:

    【解决方案4】:

    更直接的事情怎么样:

    常量.py

    NAME = "name"
    SURNAME = "surname"
    

    admin.py

    import constants
    class RegisterAdmin(admin.ModelAdmin):
        list_display = (constants.NAME, constants.SURNAME,constants.ZIPCODE)
    

    与在场景 2 中重复它们相比,这为值提供了一个独特的位置,因此我更喜欢它。对我来说,这似乎比场景 1 更清楚。 “封装”是通过模块名进行的,即使用constants.xxx,所以不要from constants import *

    这里的值显然不是恒定的,但在场景 1 中也不是。

    【讨论】:

    • 这就是 Django 的做法。而且,在我看来,这是一个丑陋的错误——不幸的是,现在框架已经根深蒂固,无法改变。
    • 我仍然相信以这种方式使用常量意味着将属于该类的重要细节置于上下文之外。
    【解决方案5】:

    每个人都同意这是一个坏主意,但可能是您的 Java 朋友正在尝试实现 Java resource bundle 的等效项,也许是因为他们深情地记得使用 .properties files 是多么容易。

    这会导致使用键代替实际名称,然后系统稍后将从包中检索正确的资源。该技术用于应用程序的翻译和本地化。

    典型的资源键看起来像key.trx.hist.term.transfer.completed.success,在运行时等效的资源由应用程序呈现。

    如果是这种情况,在 Python 中的方法有点不同:

    1. 您从基础语言开始。这就是你编写代码的地方。
    2. 所有需要本地化或翻译的字符串都标记为翻译,并带有gettext library。为方便起见,该库提供了一个快捷方法_(),用于标记要翻译的字符串。
    3. 这些字符串然后被提取到一个消息文件;转发给翻译人员。消息文件包含使用字符串的文件名。它是一个纯文本文件,很像 .properties 文件。
    4. 然后将此消息文件编译为优化格式。
    5. 根据客户端的语言/区域,显示正确的翻译字符串。

    您可以在translation documentation 阅读有关如何在 django 中实现此功能的更多信息。

    【讨论】:

    • 我们知道gettext并使用_(),我相信他们试图将字符串集中在一个地方......不确定!
    • 这是我知道的 Java 中唯一可以集中字符串的等价物;如果不是这样 - 那么我怀疑它是 YAGNI 的案例,正如 Martijn 指出的那样。
    猜你喜欢
    • 2014-11-21
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 2012-05-27
    • 2012-11-30
    • 1970-01-01
    • 2015-06-29
    相关资源
    最近更新 更多