Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pylint complains "no value for argument 'cls'"

I have defined the following class-method to define my object from a pandas.DataFrame instead of from a list like so:

class Container(object):
    @classmethod
    def from_df(cls, df):
        rows = [i for _, i in df.iterrows()]
        return cls(rows)

and pylint complains at the return line with the E1120 'code-smell':

No value for argument 'cls' in constructor call

I can't see anything wrong with it, and it seems to work. Does anybody else maybe have an idea what could be wrong with it?

Update: Ugh, user rogalski got it (I think): I confused myself by using the same variable name for a class that comes in as argument:

def __init__(self, iterable, cls):
    self.content = [cls(item) for item in iterable]

I do this because I have different kind of objects coming in and this Container class is the abstract version of this daughter:

class FanContainer(Container):
    def __init__(self, iterable):
        super().__init__(iterable, Fan)

with Fan being one of several classes that need to be 'contained'. Rogalski, want to write up an answer along the lines of saying that the error might reference a name of the __init__ constructor? Cheers! (Now I have to dig why my code isn't stumbling over this...)

Update2 Only realizing know how feeble I have coded this: I am using this basically like so:

fancontainer = FanContainer.from_df(df)

and because I am overwriting the __init__ in the FanContainer class, I guess that's why my code still worked? So, the abstract __init__ is never being called directly, because I never call Container.from_df(df) but only the daughter classes' classmethods. Guess that can be done prettier a different way.

like image 429
K.-Michael Aye Avatar asked Feb 04 '16 18:02

K.-Michael Aye


1 Answers

Typically this error is related to non-complaint function signatures.

Given your code:

class Container(object):
    def __init__(self, iterable, cls):
        self.content = [cls(item) for item in iterable]

    @classmethod
    def from_df(cls, df):
        rows = [i for _, i in df.iterrows()]
        return cls(rows)

Pylint resolves cls in from_df scope object to be Container. Class objects are callables (like functions) and they return new instance of given class. Pylint investigates constructor interface and checks if passed arguments are correct.

In your case passed arguments are incorrect - second required argument (which happens to have same name - cls - but it exists in different score) is missing. That's why Pylint yields error.

Follow up your edits: Pylint does not run your code. It statically analyzes it. Since it's possible to call it like Container.from_df PyLint will warn about possible misuse.

If constructor is never intended to use both arguments outside of your subclasses you may pass default argument and explicitly raise an exception:

class Container(object):
    def __init__(self, iterable, cls=None):
        if cls is None:
            raise NotImplementedError()
        self.content = [cls(item) for item in iterable]

    @classmethod
    def from_df(cls, df):
        rows = [i for _, i in df.iterrows()]
        return cls(rows)
like image 198
Łukasz Rogalski Avatar answered Nov 05 '22 13:11

Łukasz Rogalski