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