Which way is ideal for Python factory registration

2019-07-23 19:35发布

问题:

This is a question about which of these methods would be considered as the most Pythonic. I'm not looking for personal opinions, but, instead, what is idiomatic. My background is not in Python, so this will help me.

I'm working on a Python 3 project which is extensible. The idea is similar to the factory pattern, except it is based on functions.

Essentially, users will be able to create a custom function (across packages and projects) which my tool can locate and dynamically invoke. It will also be able to use currying to pass arguments down (but that code is not included here)

My goal is for this to follow good-Pythonic practice. I'm torn between two strategies. And, since Python is not my expertise, I would like to know the pros/cons of the following practices:

  1. Use a decorator

    registered = {}
    
    def factoried(name):
        def __inner_factory_function(fn):
            registered[name] = fn
            return fn
        return __inner_factory_function
    
    
    def get_function(name):
        return registered[name]
    

    Then, the following function is automatically registered...

    @factoried('foo')
    def build_foo():
      print('hi')
    

    This seems reasonable, but does appear slightly magical to those who are not familiar with decorators.

  2. Force sub-classing of an abstract class and use __subclasses__()

    If subclasses are used, there's no need for registration. However, I feel like this forces classes to be defined when a full class may be unnecessary. Also, the use of .__subclasses__() under the hood could seem magical to consumers as well. However, even Java can be used to search for classes with annotations.

  3. Explicit registration

    Forget all of the above and force explicit registration. No decorators. No subclasses. Just something like this:

    def build_foo():
      # ...
    
    factory.register('foo', build_foo)
    

回答1:

There is no answer to this question.

The only standard practices promoted by the Python Foundation are PEP 8.

PEP 8 has very little related to higher-level "design-pattern" questions like this, and, in particular, nothing related to your specific question.

And, even if it did, PEP 8 is explicitly only a guideline for "code comprising the standard library in the main Python distribution", and Guido has rejected suggestions to make it some kind of wide-ranging standard that should be enforced on every Python project.

Plus, it hammers home the point that it's only a guideline, not a rigid recommendation.


Of course there are subjective reasons to prefer one design over another.

Ideally, these subjective reasons will usually be driven by some community consensus on what's "idiomatic" or "pythonic". But that community consensus isn't written down anywhere as some objective source you can cite.

There may be arguments that appeal to The Zen of Python, but that itself is just Tim Peters' attempts to distill Guido's own subjective guidelines into a collection of pithy sound bites, not an objective source. (And anyone who takes a brief look at, e.g., the python-ideas list can see that both sides of almost any question can appeal to the Zen…)