LondonRob
LondonRob

Reputation: 78923

Python factory method with external function

I've read this SO discussion about factory methods, and have an alternate constructor use case.

My class looks like this:

class Foo(object):
    def __init__(self, bar):
        self.bar = bar

    @classmethod
    def from_data(cls, datafile):
        bar = datafile.read_bar()
        # Now I want to process bar in some way
        bar = _process_bar(bar)
        return cls(bar)

    def _process_bar(self, bar)
        return bar + 1

My question is, if a @classmethod factory method wants to use a function in its code, should that function (_proces_bar) be:

  1. A @classmethod, which seems a bit weird because you won't ever call it like Foo._process_bar()
  2. A method outside of the class Foo but in the same .py file. I'd go with this, but it seems kind of weird. Will those methods always be available to an instance of Foo, regardless of how it was instantiated? (e.g. what if it's saved to a Pickle then reloaded? Presumably methods outside the class will then not be available!)
  3. A @staticmethod? (see 1. This seems weird)
  4. Something else? (but not this!)

Upvotes: 1

Views: 422

Answers (1)

bruno desthuilliers
bruno desthuilliers

Reputation: 77912

The "right solution" depends on your needs...

  • If the function (_process_bar) needs an access to class Foo (or the current subclass of...) then you want a classmethod - which should be then called as cls._process_bar(), not Foo._process_bar().

  • If the function doesn't need an access to the class itself but you still want to be able to override it in subclasses (IOW : you want class-based polymorphism), you want a staticmethod

  • Else you just want a plain function. Where this function's code lives is irrelevant, and your import problems are othogonal.

Also, you may (or not, depending on your concrete use case) want to allow for more flexiblity using a callback function (possibly with a default), ie:

def process_bar(bar):
   return bar + 1

class Foo(object):
    @classmethod
    def from_data(self, datafile, processor=process_bar):
        bar = datafile.read_bar()
        bar = processor(bar)
        return cls(bar)

Upvotes: 1

Related Questions