Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Suppose I have a file foo.py containing a class Foo:

class Foo(object):
   def __init__(self, data):
      ...

Now I want to add a function that creates a Foo object in a certain way from raw source data. Should I put it as a static method in Foo or as another separate function?

class Foo(object):
   def __init__(self, data):
      ...
# option 1:
   @staticmethod
   def fromSourceData(sourceData):
      return Foo(processData(sourceData))

# option 2:
def makeFoo(sourceData):
   return Foo(processData(sourceData))

I don't know whether it's more important to be convenient for users:

foo1 = foo.makeFoo(sourceData)

or whether it's more important to maintain clear coupling between the method and the class:

foo1 = foo.Foo.fromSourceData(sourceData)
share|improve this question
add comment

1 Answer 1

The choice should between a factory function and a third option instead; the class method:

class Foo(object):
   def __init__(self, data):
      ...

   @classmethod
   def fromSourceData(klass, sourceData):
      return klass(processData(sourceData))

Classmethod factories have the added advantage that they are inheritable. You can now create a subclass of Foo, and the inherited factory method would then produce instances of that subclass.

With a choice between a function and a class method, I'd choose the class method. It documents quite clearly what kind of object the factory is going to produce, without having to make this explicit in the function name. This becomes much clearer when you not only have multiple factory methods, but also multiple classes.

Compare the following two alternatives:

foo.Foo.fromFrobnar(...)
foo.Foo.fromSpamNEggs(...)
foo.Foo.someComputedVersion()
foo.Bar.fromFrobnar(...)
foo.Bar.someComputedVersion()

vs.

foo.createFooFromFrobnar()
foo.createFooFromSpamNEggs()
foo.createSomeComputedVersionFoo()
foo.createBarFromFrobnar()
foo.createSomeComputedVersionBar()

Even better, your end-user could import just the Foo class and use it for the various ways you can create it, as you have all the factory methods in one place:

from foo import Foo
Foo()
Foo.fromFrobnar(...)
Foo.fromSpamNEggs(...)
Foo.someComputedVersion()

The stdlib datetime module uses class method factories extensively, and it's API is much the clearer for it.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.