In Python, how do I initialize a class attribute from the return value of a class method?

Imagine we have this class:

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

    mapping = {"key": ... }

      

I would like to bind the value returned get_choices()

to key

. What code should I use instead of a placeholder ...

?


EDIT: I wanted the question to be consistent (I thought this requirement was common enough, but I might be biased). As suggested in the comments, I'll provide more details, because it looks like we share the "simplest code" concern:

I am working on a Django application where I want to use an enum of values ​​separated Model

using them. It seemed to me a natural solution to create a module enumeration.py, where I could define class

for each enumeration (a class that has at least a class method for generating a collection of values). This is the example get_choices()

in this example.

Now, due to business logic, I need to map these options to a key. This mapping is logically related to the enum, it seemed like a good structure to keep them together in the same class (by providing consistent and explicit client code, prefixed by the class name).

+3


source to share


2 answers


You cannot do this in a class definition because the class object has not been created yet. After defining the class, you can definitely do something like this -

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

      

Although the recommended way to do this would be to use a metaclass.

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

      

With that said, this is an object oriented solution to the problem. you can use some function to generate the selection, thus ending the need for a metaclass.



Edited question: -

In this case, I think you should just save a file named "choice.py" as you suggested yourself and use them in your mapping instead of the get_choices

class method . You don't need to unnecessarily create classes for each model if all you want to do is select the store. Just use dicts and constants.

If your classes need to be generated dynamically from, say, db, then you need to create a separate model to store the options.

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
     }

      

It's just a raw design, might need a lot of improvement, but something along these lines.

+6


source


If the logic you need to run is trivial, just put the logic right after the class is fine:

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

      



If the logic is more complex, and especially if multiple classes are needed, then a decorator can be useful. eg,

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]

      

0


source







All Articles