Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why the value of `*args` is strange?

Tags:

python

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

like image 493
BlackMamba Avatar asked Dec 24 '22 07:12

BlackMamba


1 Answers

*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.

like image 141
ShadowRanger Avatar answered Jan 08 '23 22:01

ShadowRanger