First, most of the time you think you need something like this, you don't; it's a sign that you're trying to treat Python like Java, and the solution is to step back and ask why you need a factory.
Often, the simplest thing to do is to just have a constructor with defaulted/optional/keyword arguments. Even cases that you'd never write that way in Java—even cases where overloaded constructors would feel wrong in C++ or ObjC—may look perfectly natural in Python. For example, size = Size(bytes=20)
, or size = Size(20, Size.BYTES)
look reasonable. For that matter, a Bytes(20)
class that inherits from Size
and adds absolutely nothing but an __init__
overload looks reasonable. And these are trivial to define:
def __init__(self, *, bits=None, bytes=None, kilobits=None, kilobytes=None):
Or:
BITS, BYTES, KILOBITS, KILOBYTES = 1, 8, 1024, 8192 # or object(), object(), object(), object()
def __init__(self, count, unit=Size.BITS):
But, sometimes you do need factory functions. So, what do you do then? Well, there are two kinds of things that are often lumped together into "factories".
A @classmethod
is the idiomatic way to do an "alternate constructor"—there are examples all over the stdlib—itertools.chain.from_iterable
, datetime.datetime.fromordinal
, etc.
A function is the idiomatic way to do an "I don't care what the actual class is" factory. Look at, e.g., the built-in open
function. Do you know what it returns in 3.3? Do you care? Nope. That's why it's a function, not io.TextIOWrapper.open
or whatever.
Your given example seems like a perfectly legitimate use case, and fits pretty clearly into the "alternate constructor" bin (if it doesn't fit into the "constructor with extra arguments" bin).