That got de-indented.... so here it is again... :-/ """ ### NOTE: NOT FULLY TESTED!!! Especially with keywords. A PARTIAL PROTOCOL: Takes partial number of args and applies them to a callable. Calls the callable when all it's functions are present, else returns an updated partial object. THE RULES: * A partial is equivalent to the sum of it's partials. P(f, a, b, c) == P(f)(a)(b)(c) * A complete partial is equivalent to the function f When all the parts are provided. P(f, a, b, c) == f(a, b, c) * If f takes no arguments. (same rule) P(f) == f() * And empty partial evaluates to a partial. P() == P * If f requires at least one (not provided) argument. P(f)() == P(f) P()(f) == P(f) ERROR CASES: P(1)() # P(1) --> 1, 1 is not a callable. (* probably more) CONTROLS: It's possible to add controls for both the number of args, and number of Partial calls. Author: Ronald Adam (ron3200@gmail.com) """ class CollectMore: """ Collect more args and keywords. """ def __init__(self, f, *args, **kwds): self.f = f self.args = list(args) self.kwds = kwds def __call__(self, *args, **kwds): args = self.args + list(args) self.kwds.update(kwds) return self.f(*args, **self.kwds) def P(*args, **kwds): """ P - Partial function """ if len(args) == len(kwds) == 0: return P if len(args) > 0: f = args[0] if callable(f): a = args[1:] try: # No required args needed. # or all required args present. return f(*a, **kwds) except TypeError: # Better way to do this? pass elif len(args) == 1 and len(kwds) == 0: return f return CollectMore(P, *args, **kwds) # The implementation above also makes the functions that # use partial decorators become partials too. To avoid # that you can use an N-partial (vs impartial) partial # decorator. @P def NP(n, f, *args, **kwds): """ NP - N-Partial function Example: # Pass only 3 arguments for foo. @NP(3) def foo(*args): return args """ print("NP", n, f, *args, **kwds) if len(args) == 0: raise TypeError # Ready for rest, Partial catches this. elif len(args) < n: raise ValueError("%s is partial to %s values" % (f.__name__, n)) elif len(args) > n: raise ValueError("%s is partial to %s values" % (f.__name__, n)) else: return f(*args, **kwds)