在Python中,如何从类方法的返回值初始化类属性?

在Python中,如何从类方法的返回值初始化类属性?,python,class,python-3.x,Python,Class,Python 3.x,想象一下我们有这样一门课: class Custom: @classmethod def get_choices(cls): # use cls attributes to return a list mapping = {"key": ... } 我想将get_choices()返回的值与key关联。什么代码应该代替占位符… 编辑:我想保持问题的上下文不可知性(在我看来,这一要求已经足够普遍,但我可能会有偏见)。正如评论中所建议的,我将给出更多细节

想象一下我们有这样一门课:

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

    mapping = {"key": ... }
我想将
get_choices()
返回的值与
key
关联。什么代码应该代替占位符


编辑:我想保持问题的上下文不可知性(在我看来,这一要求已经足够普遍,但我可能会有偏见)。正如评论中所建议的,我将给出更多细节,因为我们似乎都关心“最简单的代码”:

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


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

您无法在类定义中执行此操作,因为尚未创建类对象。 在类定义之后,您肯定可以这样做-

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
话虽如此,这是一个面向对象的问题解决方案。当然,您可以使用一些函数来生成选择,从而不再需要使用元类

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\u choices
class方法。如果您只想存储选择,则不需要为每个模型创建不必要的类。只需使用dicts和常量

如果您的类需要动态生成,比如从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
     }

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

如果需要运行的逻辑很简单,那么直接将逻辑放在类之后就可以了:

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]

请不要将元类用于这样的事情。现在已经有很多了,更详细地说,使用元类会导致代码非常冗长,有些不透明,并且做一些非常简单的事情。在几乎所有情况下,您都可以通过稍微重新安排一些内容来找到一个非常简单直接的解决方案,从而产生可读性更高的代码。如何做到这一点取决于许多因素,我们需要更多的上下文来建议替代设计。当然,元类也有一些有效的使用案例,但在我看来,我所看到的绝大多数实际使用都是过度设计的。我知道元类的所有优点和缺点。如果您没有注意到,我还指定了一个单线解决方案。但依我看,像这样在类之外修改类也不是一个很好的解决方案。我认为用户需要找到另一种方法来生成映射,但如果他只想存储类方法的结果,那么就不太可能了bad@SvenMarnach对不起,我没有提供上下文(我会认为它在这里是不相关的)。我编辑这个问题是为了解释一下我的意图,这些意图是由清晰的代码驱动的,对我来说同样重要的是结构良好的代码:)关于你的编辑:我推断“他们”指的是选择的集合?如果是这种情况,则此集合可能是由get_choices()在运行时生成的,不一定存在静态列表。您不能在类中执行此操作,<代码> > CalpRead < /C++ >实际上直到该类定义完成后才可调用。这似乎不太可能!我昨天回答了一个无关紧要的问题,你可能会发现我的回答很有用: