constructor classmethods

teppo.pera at gmail.com teppo.pera at gmail.com
Mon Nov 7 03:46:25 EST 2016


torstai 3. marraskuuta 2016 14.45.49 UTC Ethan Furman kirjoitti:
> On 11/03/2016 01:50 AM, teppo wrote:
> 
> > The guide is written in c++ in mind, yet the concepts stands for any
> >  programming language really. Read it through and think about it. If
> >  you come back to this topic and say: "yeah, but it's c++", then you
> >  haven't understood it.
> 
> The ideas (loose coupling, easy testing) are certainly applicable in Python -- the specific methods talked about in that paper, however, are not.

Please elaborate. Which ones explicitly?

> 
> To go back to the original example:
> 
> def __init__(self, ...):
>      self.queue = Queue()
> 
> we have several different (easy!) ways to do dependency injection:
> 
> * inject a mock Queue into the module
> * make queue a default parameter
> 
> If it's just testing, go with the first option:
> 
> import the_module_to_test
> the_module_to_test.Queue = MockQueue
> 
> and away you go.

This is doable, but how would you inject queue (if we use Queue as an example) with different variations, such as full, empty, half-full, half-empty. :) For different tests. 

> 
> If the class in question has legitimate, non-testing, reasons to specify different Queues, then make it a default argument instead:
> 
> def __init__(self, ..., queue=None):
>      if queue is None:
>          queue = Queue()
>      self.queue = queue

I already stated that this is is fine, as long as the number of arguments stays in manageable levels. Although I do think testing is good enough reason to have it injected anytime. For consistency, it makes sense to have same way to create all objects. I wouldn't suggested of using that mechanism in public API's, just in internal components.

> 
> or, if it's just for testing but you don't want to hassle injecting a MockQueue into the module itself:
> 
> def __init__(self, ..., _queue=None):
>      if _queue is None:
>          _queue = Queue()
>      self.queue = _queue
> 
> or, if the queue is only initialized (and not used) during __init__ (so you can replace it after construction with no worries):
> 
> class Example:
>      def __init__(self, ...):
>          self.queue = Queue()
> 
> ex = Example()
> ex.queue = MockQueue()
> # proceed with test

This I wouldn't recommend. It generates useless work when things start to change, especially in large code bases. Don't touch internal stuff of class in tests (or anywhere else).

> 
> The thing each of those possibilities have in common is that the normal use-case of just creating the thing and moving on is the very simple:
> 
> my_obj = Example(...)
> 
> To sum up:  your concerns are valid, but using c++ (and many other language) idioms in Python does not make good Python code.

This is not necessarily simply a c++ idiom, but a factory method design pattern 4nasm4I7%2D0s4XSH[8cS}12M)320?IqGu7_7JS$d0k+V0Dqb7. Although there are many other benefits the current pattern would offer, in this case it is primarily used just for convenience (although it seems it is not seen as such) and giving more flexibility in writing tests.

Br,
Teppo



More information about the Python-list mailing list