functools puzzle

Peter Otten __peter__ at web.de
Wed Apr 6 14:20:30 EDT 2016


George Trojan - NOAA Federal wrote:

> Here is my test program:
> 
> ''' generic test '''
> 
> import functools
> import inspect
> 
> def f():
>     '''I am f'''
>     pass
> 
> g = functools.partial(f)
> g.__doc__ = '''I am g'''
> g.__name__ = 'g'
> 
> def partial(func, *args, **keywords):
>     def newfunc(*fargs, **fkeywords):
>         newkeywords = keywords.copy()
>         newkeywords.update(fkeywords)
>         return func(*(args + fargs), **newkeywords)
>     newfunc.func = func
>     newfunc.args = args
>     newfunc.keywords = keywords
>     return newfunc
> 
> h = partial(f)
> functools.update_wrapper(h, f)
> h.__doc__ = '''I am h'''
> h.__name__ = 'h'
> 
> print(type(g), g.__name__, inspect.getdoc(g))
> print(type(h), h.__name__, inspect.getdoc(h))
> help(g)
> help(h)
> 
> The output is what I expect:
> 
> (devenv-3.5.1) dilbert at gtrojan> python x.py
> <class 'functools.partial'> g I am g
> <class 'function'> h I am h
> 
> However the help screens for h and g are different. help(h) is again what
> I expect:
> 
> Help on function h in module __main__:
> 
> h()
>     I am h
> 
> But help(g) is (almost) identical to help(functools.partial), the doc
> string disappears:
> 
> Help on partial object:
> 
> g = class partial(builtins.object)
>  |  partial(func, *args, **keywords) - new function with partial
>  |  application of the given arguments and keywords.
> ...
> 
> The module functools has partial() defined as above, then overrides the
> definition by importing partial from _functools. That would explain the
> above behaviour. My question is why?

This is not specific to functools.partial.

partial(some_func, ...)

creates an instance of the partial class. Unfortunately the instance.__doc__ 
is generally ignored in favour of class.__doc__:

>>> class Foo:
...     "CLASS"
... 
>>> foo = Foo()
>>> foo.__doc__ = "INSTANCE"
>>> help(foo)
Help on Foo in module __main__ object:

class Foo(builtins.object)
 |  CLASS
[...]

Being looked up in the class is standard behaviour for __dunder__ 
attributes:

>>> class A:
...    def __len__(self): return 42
... 
>>> a = A()
>>> a.__len__ = lambda: 123
>>> a.__len__()
123
>>> len(a)
42

Most of the time it saves at least a dict lookup, but sometimes it bites 
you.




More information about the Python-list mailing list