Skip to content

groupby isinstance(f, types.MethodType) in py3 #17608

Closed
@jbrockmendel

Description

@jbrockmendel

in core.groupby._whitelist_method_generator (https://github.com/pandas-dev/pandas/blob/master/pandas/core/groupby.py#L2801)

    method_wrapper_template = \
        """def %(name)s(%(sig)s) :
    \"""
    %(doc)s
    \"""
    f = %(self)s.__getattr__('%(name)s')
    return f(%(args)s)"""
    property_wrapper_template = \
        """@property
def %(name)s(self) :
    \"""
    %(doc)s
    \"""
    return self.__getattr__('%(name)s')"""
    for name in whitelist:
        # don't override anything that was explicitly defined
        # in the base class
        if hasattr(GroupBy, name):
            continue
        # ugly, but we need the name string itself in the method.
        f = getattr(klass, name)
        doc = f.__doc__
        doc = doc if type(doc) == str else ''
        if isinstance(f, types.MethodType):
            wrapper_template = method_wrapper_template
            decl, args = make_signature(f)
            # pass args by name to f because otherwise
            # GroupBy._make_wrapper won't know whether
            # we passed in an axis parameter.
            args_by_name = ['{0}={0}'.format(arg) for arg in args[1:]]
            params = {'name': name,
                      'doc': doc,
                      'sig': ','.join(decl),
                      'self': args[0],
                      'args': ','.join(args_by_name)}
        else:
            wrapper_template = property_wrapper_template
            params = {'name': name, 'doc': doc}
        yield wrapper_template % params

It looks like isinstance(f, types.MethodType) is never evaluating True in py3, so these are all coming out as properties.

PY2:

In [1]: import pandas as pd
In [2]: gb = pd.Series(range(10)).groupby(lambda x: x % 2)
In [3]: gb.mad
Out[3]: <bound method SeriesGroupBy.mad of <pandas.core.groupby.SeriesGroupBy object at 0x10f394f10>>
In [4]: gb.mad.__doc__
Out[4]: '\n    \n\nReturn the mean absolute deviation of the values for the requested axis\n\nParameters\n----------\naxis : {index (0)}\nskipna : boolean, default True\n    Exclude NA/null values. If an entire row/column is NA, the result\n    will be NA\nlevel : int or level name, default None\n    If the axis is a MultiIndex (hierarchical), count along a\n    particular level, collapsing into a scalar\nnumeric_only : boolean, default None\n    Include only float, int, boolean columns. If None, will attempt to use\n    everything, then use only numeric data. Not implemented for Series.\n\nReturns\n-------\nmad : scalar or Series (if level specified)\n\n    '

PY3:

In [1]: import pandas as pd
In [2]: gb = pd.Series(range(10)).groupby(lambda x: x % 2)
In [3]: gb.mad
Out[3]: <function pandas.core.groupby._GroupBy._make_wrapper.<locals>.wrapper>
In [4]: gb.mad.__doc__
In [5]:

If we change the condition from isinstance(f, types.MethodType) to not isinstance(f, property), a TypeError is raised (py3 only):

  File "[...]/pandas/core/groupby.py", line 2796, in _whitelist_method_generator
    decl, args = make_signature(f)
  File "[...]/pandas/util/_decorators.py", line 245, in make_signature
    defaults = ('',) * n_wo_defaults + spec.defaults
TypeError: can only concatenate tuple (not "list") to tuple

#17600 should make this problem irrelevant eventually, but someone might want to look at it more short-term.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions