Set class variable value, the value returned by a class method

640 views Asked by At

I'm trying to create a class which maps to a mongoDB collection.

My code looks like this:

class Collection:
    _collection = get_collection()  # This seems not working

    @classmethod
    def get_collection(cls):
        collection_name = cls.Meta.collection_name if cls.Meta.collection_name \
        else cls.__name__.lower()
        collection = get_collection_by_name(collection_name) # Pseudo code, please ignore
        return collection

    class Meta:
        collection_name = 'my_collection'

I came across a situation where I need to assign the class variable _collection with the return value of get_collection. I also tried _collection = Collection.get_collection() which also seems not to be working

As a work-around, I subclassed Collection and set value of _collection in the child class.

Would like to know any simple solution for this.

Thanks in advance

2

There are 2 answers

1
bruno desthuilliers On BEST ANSWER

As DeepSpace mentions, here:

class Collection:
    _collection = get_collection()  # This seems not working

    @classmethod
    def get_collection(cls):
        # code that depends on `cls`

the get_collection method is not yet defined when you call it. But moving this line after the method definition won't work either, since the method depends on the Collection class (passed as cls to the method), which itself won't be defined before the end of the class Collection: statement's body.

The solution here is to wait until the class is defined to set this attribute. Since it looks like a base class meant to be subclassed, the better solution would be to use a metaclass:

class CollectionType(type):
    def __init__(cls, name, bases, attrs):
        super(CollectionType, cls).__init__(name, bases, attrs)
        cls._collection = cls.get_collection()


# py3
class Collection(metaclass=CollectionType):
    # your code here


# py2.7
class Collection(object):
    __metaclass__ = CollectionType
    # your code here

Note however that if Collection actually inherit from a another class already having a custom metaclass (ie Django Model class or equivalent) you will need to make CollectionType a subclass of this metaclass instead of a subclass of type.

3
DeepSpace On

There are some design/syntax errors in your code.

  • When the line _collection = get_collection() executes, get_collection is not yet defined. As a matter of fact, the whole Collection class is not yet defined.

  • get_collection_by_name is not defined anywhere.

EDIT OP updated the question so the below points may not be relevant anymore

  • collection = get_collection(collection_name) should be
    collection = cls.get_collection(collection_name)

  • Sometimes you are passing a parameter to get_collection and sometimes you don't, however get_collection's signature never accepts a parameter.

  • Calling get_collection will lead to an infinite recursion.


You have to take a step back and reconsider the design of your class.