Skip to main content

Object Oriented Currying

Project description


Factory is an object-oriented approach to partial function application, also known as currying. The Factory module is a more powerful implementation of this pattern. Some improvements include:

  • safer, as invalid arguments are detected immediately, instead of at call time
  • intelligent support for classes, instance methods & all other callables
  • bound arguments can be inspected and modified as attributes
  • several convenient methods for (re)binding arguments
  • no “Russian dolls” of nested lambdas

Using Factories can:

  • simplify writing callbacks
  • reduce bugs in concurrent applications
  • provide easy lazy evaluation


The Factory module is available from the Cheeseshop. The source code is available from the Google Code project page.

The Factory module can be installed like any other pure Python module. Setuptools is supported but not required. You may also include the file directly in your project’s source tree, but you must retain the copyright notice and version and attribution information.

To run tests for the module, execute the following commands in the Factory/ directory:

  • python
  • nosetests

About Currying

Currying creates a new function from an existing one by binding some of the original’s arguments:

>>> def adder(x, y):
...     return x + y
>>> add_lambda = lambda y: adder(1, y)
>>> add_lambda(10)

As of Python 2.5, this pattern is built in with the partial function.

>>> add_partial = functools.partial(adder, 1)
>>> add_partial(y=10)


Factories are better implementation of the currying pattern:

>>> from Factory import *
>>> add_factory = Factory(adder, x=1)
>>> add_factory #doctest: +ELLIPSIS
<Factory(<function adder at ...>) at ...>
>>> add_factory(y=10)

Unlike lambdas and partial, factories can be inspected and modified:

>>> add_factory.x
>>> add_factory.x = 2
>>> add_factory(y=10)

The arguments that would be passed to the function can be examined, which is sometimes helpful in debugging:

>>> import pprint
>>> args, kwargs = add_factory.generateArgs(y=10)
>>> pprint.pprint(kwargs)
{'x': 2, 'y': 10}
>>> args


In the following examples, we mix in FactoryMixin to provide a factory classmethod on the base class.

>>> class Foo(FactoryMixin):
...     def __init__(self, foo):
... = foo
>>> foo_factory = Foo.factory()
>>> = 66

This is equivalent to:

>>> Factory(Foo) #doctest:+ELLIPSIS
<Factory(<class 'Foo'>) at ...>

Using the mixin isn’t strictly necessary, but looks nice and is easier to spell.

Factories have a bind method that can be used to set several attributes at once and returns the factory. It’s useful for binding arguments without assigning the factory to a local variable.

>>> def doStuff(foo_factory):
...     return
>>> doStuff(foo_factory.bind(foo=11))
>>> foo_factory2 = foo_factory.bind(foo=42)
>>> foo_factory2 is foo_factory

You can also bind attributes when constructing the factory:

>>> foo_factory = Factory(Foo, foo=11)

Factories ensure that attributes match up with arguments; this makes finding errors easier (instead of raising a unexpected keyword argument later):

>>> = 42  #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'No such argument bar'

When calling the factory, arguments override attributes:

>>> foo = foo_factory(foo=1111)

Each call returns a new instance:

>>> foo2 = foo_factory()
>>> foo2 is foo

The set of valid attributes is the union of all __init__ arguments in the inheritance chain:

>>> class Bar(Foo):
...     def __init__(self, bar, **kwargs):
...         super(Bar, self).__init__(**kwargs)
... = bar
>>> bar_factory = Bar.factory()
>>> = 11
>>> = 42
>>> bar_factory.quux = 666  #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'No such argument quux'
>>> bar = bar_factory()

Be sure to pass Factory a callable object (a class, not an an instance):

>>> Factory(bar)  #doctest:+ELLIPSIS, +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must provide known callable type, not <Factory.Bar object at ...>

Callable objects are fine, of course:

>>> class CallMe(object):
...     def __init__(self, x):
...         self.x = x
...     def __call__(self, y):
...         return self.x + y
>>> Factory(CallMe(1))(1)

An existing factory can be passed as the callee of a new factory.

>>> bar_factory = Bar.factory(bar=2)
>>> bar_factory2 = Factory(bar_factory, foo = 1)
>>> bar_factory is not bar_factory2
>>> = 4

Unlike using lambdas, this does not create nested “Russian dolls”:

>>> bar_factory2.getCallable()
<class 'Bar'>


returnFactory is a decorator which replaces a function with its Factory-producing equivalent:

>>> @returnFactory
... def mult(x, y):
...     return x * y
>>> fac = mult(x=10, y=5)
>>> isinstance(fac, Factory)
>>> fac()

factoryAttribute adds a factory attribute to the decorated function:

>>> @factoryAttribute
... def adder(x, y):
...     return x + y
>>> fac = adder.factory(x=10)
>>> isinstance(fac, Factory)
>>> fac2 = adder.factory()
>>> fac is not fac2
>>> fac(y=42)

factoryDescriptor produces instance methods with a factory attribute. Inside classes, use this descriptor instead of factoryAttribute. This class may be used as a decorator:

>>> class Quux(object):
...     @factoryDescriptor
...     def doStuff(self, whatnot):
...          pass
>>> quux = Quux()
>>> fac = quux.doStuff.factory(whatnot=42)
>>> isinstance(fac, Factory)
>>> fac.whatnot


ObjectTemplates are a template for creating objects. They work well with Factories.

A Bunch is simply a bunch of attributes. Keyword arguments to a Bunch are turned into attributes:

>>> b = Bunch(pants=42, shirt=15)
>>> b.pants
>>> b.shirt

Calling a bunch returns a new copy:

>>> c = b()
>>> c.__dict__ == b.__dict__
>>> c is b

When called, an ObjectTemplate instance produces a new instance of bunchClass. Attributes on the template are passed as kwargs to the bunch. However, if an attribute is callable, it is called and the return value is used instead:

>>> counter = itertools.count(1).next # an incrementing counter
>>> def color():
...     return "blue"
>>> template = ObjectTemplate(size=42,
...                           color=color,
...                           count=counter,
...                           bunchClass=Bunch)
>>> bunch = template()
>>> isinstance(bunch, Bunch)
>>> bunch.size
>>> bunch.color
>>> bunch.count

Each call to the template produces a new bunch. Any functions will be called again:

>>> bunch2 = template()
>>> bunch2.count

If you want to pass a callable object to the bunch, wrap it in a lambda:

>>> template = ObjectTemplate()
>>> template.return_val = color
>>> template.a_function = lambda: color
>>> bunch = template()
>>> bunch.return_val
>>> bunch.a_function #doctest:+ELLIPSIS
<function color at ...>


Bugs, feature requests and praise may be sent directly to the author.

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for Factory, version 1.2
Filename, size File type Python version Upload date Hashes
Filename, size Factory-1.2.tar.gz (8.9 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring DigiCert DigiCert EV certificate Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page