【问题标题】:In Python, how to initialize a class attribute from the return value of a class method?在 Python 中,如何从类方法的返回值初始化类属性?
【发布时间】:2015-11-12 12:11:43
【问题描述】:

假设我们有这个类:

class Custom:
    @classmethod
    def get_choices(cls):
        # use cls attributes to return a list

    mapping = {"key": ... }

我想将get_choices() 返回的值关联到key。应该用什么代码代替占位符 ...


编辑:我想保持问题与上下文无关(在我看来,这个要求很常见,但我可能会有偏见)。正如 cmets 中所建议的,我将提供更多细节,因为我们似乎都关心“最直接的代码”:

我正在开发一个 Django 应用程序,我希望 枚举 值与使用它们的 Models 分开。在我看来,一个自然的解决方案是创建一个 enumeration.py 模块,然后我可以在其中为每个枚举定义一个 class(该类至少有一个类方法来 generate 值的集合)。这是示例中的get_choices()

现在,由于业务逻辑,我需要将这些选择映射到一个键。这种映射在逻辑上与枚举耦合,将它们放在同一个类中似乎是一个很好的结构(给客户端代码提供统一和显式的访问,以类名为前缀)。

【问题讨论】:

  • 你不能在类中这样做,@classmethods 在类定义完成之前实际上是不可调用的。
  • @jonrsharpe 谁会猜到 C++ 正在用 Python 中没有的便利来破坏它的用户;)
  • 这似乎不太可能!我昨天回答了一个与切线相关的问题,您可能会发现我的回答很有用:stackoverflow.com/a/32069776/3001761

标签: python class python-3.x


【解决方案1】:

您不能在类定义中这样做,因为尚未创建类对象。 在类定义之后,你绝对可以做这样的事情 -

Custom.mapping['key'] = Custom.get_choices()

虽然推荐的方法是使用元类。

class CustomMetaClass(type):

      def __init__(cls, classname, bases, attrs):
          super(CustomMetaClass, cls).__init__(classname, bases, attrs)

          cls.mapping['key'] = cls.get_choices()

class Custom(metaclass = CustomMetaClass): # assuming you are using python 3

      mapping = {}

      @classmethod
      def get_choices(cls):
          # add your custom code here
          pass

话虽如此,这是一个面向对象的解决方案。你当然可以使用一些函数来生成选择,从而结束使用元类的需要。

对于已编辑的问题:-

在这种情况下,我认为您应该按照自己的建议维护一个名为“choices.py”的文件,并在映射中使用它们,而不是 get_choices 类方法。如果您只想存储选择,则无需为每个模型创建类。只需使用字典和常量。

如果您的类需要动态生成,例如从 db,那么您需要创建单独的模型来存储选择。

class CustomModelChoices(models.Model):

      model_name = models.StringField(db_field = 'mn')
      choices = models.DictField(db_field = 'ch')

class CustomModel(models.Model):

     _choice_generator = CustomModelChoices

     mapping = {
         'key': CustomModelChoices.objects.get(model_name = 'CustomModel').choices
     }

这只是一个原始设计,可能需要大量改进,但在这些方面有所改进。

【讨论】:

  • 请不要将元类用于这样的事情。那里已经有很多了。
  • 更详细地说,使用元类会导致非常冗长且有些不透明的代码做一些非常简单的事情。在几乎所有情况下,您都可以通过稍微重新安排一些事情来找到一个非常简单直接的解决方案,从而产生更具可读性的代码。如何做到这一点取决于许多因素,我们需要更多的背景来建议替代设计。可以肯定的是,元类有一些有效的用例,但在我看来,我所看到的绝大多数实际用途都被过度设计了。
  • 我知道元类的所有优点和缺点。如果您没有注意到,我也指定了一个单行解决方案。但是恕我直言,像这样在类之外修改类也不是一个太优雅的解决方案。我认为用户需要找到另一种解决方法来生成映射,但是如果他只想存储类方法的结果,那还不错
  • @SvenMarnach 抱歉,我没有提供上下文(我本以为它在这里无关紧要)。我编辑了这个问题来解释一下我的意图,这是由清晰的代码驱动的,对我来说结构良好的代码同样重要:)
  • 关于您的编辑:我推断“他们”是指选择的集合?如果是这样的话,这个集合可能是由 get_choices() 在运行时生成的,不一定是静态列表。
【解决方案2】:

如果您需要运行的逻辑是微不足道的,那么只需将逻辑直接放在类之后即可:

class Custom1:
    value = 1
    @classmethod
    def get_choices(cls):
        return [cls.value]
Custom1.mapping = {"key": Custom1.get_choices()}

如果逻辑更复杂,特别是如果多个类需要,那么装饰器可能会很有用。例如

def add_mapping_choices(cls):
    cls.mapping = {"key": cls.get_choices()}
    return cls

@add_mapping_choices
class Custom2:
    value = 2
    @classmethod
    def get_choices(cls):
        return [cls.value]

【讨论】:

    猜你喜欢
    • 2014-02-02
    • 2021-07-28
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-18
    • 2020-09-01
    • 1970-01-01
    相关资源
    最近更新 更多