constructor classmethods

Terry Reedy tjreedy at udel.edu
Wed Nov 2 10:34:07 EDT 2016


On 11/2/2016 9:46 AM, stestagg at gmail.com wrote:
> Hi
>
> I was hoping to canvas opinion on using classmethods as constructors over __init__.
>
> We've got a colleague who is very keen that __init__ methods don't contain any logic/implementation at all, and if there is any, then it should be moved to a create() classmethod.
>
> As a concrete example, one change proposed was to go from this:
>
> ```
> def __init__(self, ...):
>     self.queue = Queue.Queue()
>
> to this:
>
> def __init__(self, queue):
>     self.queue = queue
>
> @classmethod
> def create(cls, ...):
>     return cls(Queue.Queue())

Even with the above fixed to properly pass on the ... part, UGH!  It 
violate Occam's Razor and is harder to read. thi

> The rationale is that it decouples the class from the queue implementation (no evidence or suggestion that we would actually ever want to change impl in this case),

It just moves the dependency to a different method.

 > and makes testing easier.

If one wants to replace the the Queue with a MockQueue for testing, amd 
the class is named 'C', one can do

c = C(...)
c.queue = MockQueue()

or one can revise __init__ to allow for dependency injection

class C:
     def __init__(self, ..., queue=None):
         if queue is None:
             queue = Queue.Queue

and the test can have

c = C(..., queue=MockQueue)

> I get the underlying principal, and it's one that a strict OOp approach would suggest, but my gut feeling is that this is not a pythonic approach at all.

Perhaps your collegue uses languages that do not have default arguments, 
which make his alternative unnecessary for Python.

> What do people feel about this?

AFAIK, the stdlib only has alternate constructor classmethods as 
specialized alternatives to __init__, not as replacements. Examples are 
dict.fromkeys, int.from_bytes, and other .fromxyz methods.  These are 
used when multiplexing the .__init__ API to include the alternate 
constructor args is considered worse than adding an extra method.

 >>> int(30.3)
30
 >>> int('30')
30
 >>> int(b'30')
30
 >>> int(b'30', 12)
36
 >>> int.from_bytes(b'30', 'big')
13104
 >>> int.from_bytes(b'30', 'little')
12339


-- 
Terry Jan Reedy




More information about the Python-list mailing list