python / cpython

The Python programming language
https://www.python.org
Other
63.51k stars 30.42k forks source link

Add custom __repr__ to functools.partial #48363

Closed cd67f734-ecc9-4b44-8426-0148ef4cbf1b closed 13 years ago

cd67f734-ecc9-4b44-8426-0148ef4cbf1b commented 16 years ago
BPO 4113
Nosy @malemburg, @birkenfeld, @rhettinger, @terryjreedy, @abalkin, @merwok, @durban
Files
  • issue4113-01.patch: Patch for issue 4113, revision 1
  • issue4113.diff: Patch (py3k branch)
  • issue4113b.diff: Patch 2 (py3k branch)
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = 'https://github.com/abalkin' closed_at = created_at = labels = ['easy', 'type-feature', 'library'] title = 'Add custom __repr__ to functools.partial' updated_at = user = 'https://bugs.python.org/stribb' ``` bugs.python.org fields: ```python activity = actor = 'belopolsky' assignee = 'belopolsky' closed = True closed_date = closer = 'belopolsky' components = ['Library (Lib)'] creation = creator = 'stribb' dependencies = [] files = ['11831', '19637', '19643'] hgrepos = [] issue_num = 4113 keywords = ['patch', 'easy'] message_count = 15.0 messages = ['74682', '74945', '74977', '74982', '74986', '107504', '121417', '121449', '121525', '121533', '121569', '123006', '123009', '123010', '123011'] nosy_count = 9.0 nosy_names = ['lemburg', 'georg.brandl', 'rhettinger', 'terry.reedy', 'belopolsky', 'thomaslee', 'eric.araujo', 'stribb', 'daniel.urban'] pr_nums = [] priority = 'low' resolution = 'accepted' stage = 'resolved' status = 'closed' superseder = None type = 'enhancement' url = 'https://bugs.python.org/issue4113' versions = ['Python 3.2'] ```

    cd67f734-ecc9-4b44-8426-0148ef4cbf1b commented 16 years ago

    When I partially apply a function using functools.partial(), the resultant function doesn't have the same name as the original function (it has no __name__) and its docstring is that of functools.partial() rather than that of the function that was partially applied.

    Transcript:

    Python 2.6 (r26:66714, Oct 13 2008, 10:32:02)
    [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >> import functools
    >>> def sum(a,b,c):
    ...   return a+b+c
    ...
    >>> functools.partial(sum,1)
    <functools.partial object at 0xf7efeb6c>
    >>> p = functools.partial(sum, 1)
    >>> p.__name__
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'functools.partial' object has no attribute '__name__'
    >>> p(2,3)
    6
    >>> sum.__name__
    'sum'
    >>> sum.__doc__
    >>> p.__doc__
    'partial(func, *args, **keywords) - new function with partial
    application\n\tof the given arguments and keywords.\n'
    terryjreedy commented 16 years ago

    Same in 3.0

    2ba56af2-62ea-4996-b0d1-725349dcf07d commented 16 years ago

    Here's a patch against the 2.x trunk. Basically just dispatches reads of a partial's __name and __doc attributes to the underlying function object.

    birkenfeld commented 16 years ago

    I do not think the partial object should have the original function's __name or __doc. It is after all not that function, but a callable that calls it. (This is different from e.g. a decorated function -- the decorator is taken as "modifying the function", even if it returns a wholly different one. This is why functools.wraps() exists.)

    In any case, this is not a bug but a feature request.

    2ba56af2-62ea-4996-b0d1-725349dcf07d commented 16 years ago

    I actually agree with the sentiment Georg.

    Would it instead be useful to maybe provide a __repr__ implementation that describes the state of the partially applied function? I guess this is an entirely different issue, maybe one for python-ideas.

    abalkin commented 14 years ago

    I understand that the latest RFE in this issue is to provide a custom __repr__ to functools.partial. Something along the lines of

    class partial(functools.partial):
        def __repr__(self):
            return "functools.partial(%r, %s)" % (self.func, 
                          ', '.join(repr(a) for a in self.args)
    >>> def f(x, y, z):
    ...   pass
    >>> partial(f, 1, 2)
    functools.partial(<function f at 0x10065b060>, 1, 2)

    Looks like a reasonable proposal, but coding this in C is a chore. (The prototype above does not process keywords, so complete implementation is more involved.)

    merwok commented 14 years ago

    One function in inspect can do everything we want, only not in C. How hard/inefficient would it be to have 99% of partial coded in C and one stub in functools.py?

    rhettinger commented 14 years ago

    Looks like a reasonable proposal, but coding this in C is a chore.

    It's not that bad. Most C code is a bit of a chore compared to Python but it really doesn't take much to write a C equivalent of: "functools.partial(%r, %s)" % (self.func, ', '.join(repr(a) for a in self.args)

    How hard/inefficient would it be to have 99% of partial coded in C and one stub in functools.py?

    Let's not do this. There is too little benefit to warrant going down the path of splitting the code across two langauges.

    7ad756fc-130f-4966-b479-145798a9f250 commented 14 years ago

    Here is a patch. It includes tests.

    abalkin commented 14 years ago

    There is an ongoing discussion about deprecating undocumented PyUnicode_AppendAndDel(). See Marc-Andre's comment in msg121371:

    """ +.. c:function:: void PyUnicode_Append(PyObject **pleft, PyObject *right) + + Concat two strings and put the result in *pleft. Sets *pleft to + NULL on error. + +.. c:function:: void PyUnicode_AppendAndDel(PyObject **pleft, PyObject *right) + + Concat two strings and put the result in *pleft and drop the right + object. Sets *pleft to NULL on error. + +

    Please don't document these two obscure APIs. Instead we should make them private functions by prepending them with an underscore. If you look at the implementations of those two APIs, they are little more than a macros around PyUnicode_Concat().

    3rd party extensions should use PyUnicode_Concat() to achieve the same effect. """

    While it is OK for Python library to use private APIs, please consider if PyUnicode_Concat() may be more appropriate. If not, please make a case at bpo-10435 for keeping it public.

    7ad756fc-130f-4966-b479-145798a9f250 commented 14 years ago

    Well, of course it can be done with PyUnicode_Concat (obviously, since PyUnicode_AppendAndDel uses that). I used PyUnicode_AppendAndDel because that function does exactly what I needed.

    I don't see why PyUnicode_AppendAndDel should be deprecated. Anyway, here is a new patch which uses PyUnicode_Concat.

    abalkin commented 13 years ago

    I simplified the partial_repr() code in issue4113b.diff and committed as r86916.

    I wonder, however, if for the common case of func being a named function, displaying func.__name or func.__module + '.' + func.__name__ in repr(partial) may be more apropriate than repr(f).

    For example,

    functools.partial(f, 1, 2, 3, a=5, b={}, c='7')

    instead of

    functools.partial(<function f at 0x100592d98>, 1, 2, 3, a=5, b={}, c='7')
    terryjreedy commented 13 years ago

    I would prefer the module.name without the repr decoration.

    merwok commented 13 years ago

    I think the main purpose of repr is debugging, so I’d favor the unambiguous form (with the id) to the nice-looking one (module.name).

    abalkin commented 13 years ago

    Let me close this issue before any serious bikeshedding begins. We can always reconsider when users complain that eval(repr(x)) does not work for their partial objects.