BlackMamba
BlackMamba

Reputation: 10252

why the value of `*args` is strange?

Here is my code snappt:

class C(dict):
    def __init__(*args, **kwds):
        print 'args = {}'.format(args)
        print 'kwds = {}'.format(kwds)
        if not args:
            raise TypeError("descriptor '__init__' of 'Counter' object needs an argument")

        self = args[0]

        args = args[1:]

        if len(args) > 1:
            raise TypeError('expected at most 1 arguments, got %d' % len(args))

        super(C, self).__init__()


if __name__ == '__main__':
    c = C()

When i run this code, i am confused with the output:

C:\Users\elqstux\Desktop>python wy.py
args = ({},)
kwds = {}

For c = C(), i think the code will raise a Error, because args will be () in this case, but from the output, args is ({},), what't the reason?

self, args = args[0], args[1:] # allow the "self" keyword be passed

Upvotes: 4

Views: 114

Answers (3)

ShadowRanger
ShadowRanger

Reputation: 155438

*args is ({},) because you didn't declare an explicit self parameter. So when __init__ is called, the self parameter ends up becoming the first element in args. Since you inherit from dict, you get dict's repr, which is {}.

This trick is used intentionally in Python's dict subclasses nowadays, because otherwise code that initialized the dict subclass with mydict(self=123) would break the initializer. The fix is to do:

 def __init__(*args, **kwargs):
     self, *args = args
     # On Python 2 without unpacking generalizations you'd use the more repetitive:
     # self, args = args[0], args[1:]

which forces self to be accepted purely as a positional argument (which is passed implicitly by the instance construction machinery). For an example of real world usage, see the implementation of collections.Counter.

Upvotes: 6

Tom Karzes
Tom Karzes

Reputation: 24052

Your class __init__ function is being called with a single argument, the implicit self argument (i.e., the new class instance that's being created). Normally this would print as a class instance. But because the class subclasses dict, it prints as a dict, initially with no elements.

Upvotes: 1

Claudiu
Claudiu

Reputation: 229391

I was intrigued by your output! The reason is clear enough: you ommitted self from the __init__ function definition. Running the example with a regular class will make it clear what happened:

>>> class C():
...   def __init__(*args, **kwargs):
...     print args, kwargs
...
>>> C()
(<__main__.C instance at 0x103152368>,) {}
<__main__.C instance at 0x103152368>

Of course, self got stuck into args as the single element of the args tuple. self is just like any other argument, after all.

What made it more puzzling in your case is that since you subclassed dict, the repr() of your class at that point was {}, i.e. the repr() of an empty dict. Hence the ({},).

Upvotes: 3

Related Questions