Vy.Iv
Vy.Iv

Reputation: 879

Introspecting arguments from the constructor function __init__ in Python

What is a way to extract arguments from __init__ without creating new instance. The code example:

class Super:
    def __init__(self, name):
        self.name = name

I am looking something like Super.__dict__.keys()type solution. Just to retrieve name argument information without adding any values. Is there such an option to do that?

Upvotes: 26

Views: 33131

Answers (2)

Voy
Voy

Reputation: 6284

A metaclass that stores the concrete __init__ parameters in a class field:

class ParameterReader(ABCMeta):
    def __init__(cls, *args, **kwargs):
        parameters = inspect.signature(cls.__init__).parameters
        parameters = {key: value for key, value in parameters.items() if key not in ['self', 'args', 'kwargs']}
        try:
            cls._init_parameters = cls.__bases__[0]._init_parameters.copy()
            cls._init_parameters.update(parameters)
        except AttributeError:
            cls._init_parameters = parameters

        super().__init__(*args, **kwargs)

_init_parameters can then be used within the class instance or outside of it:

class Fruit(metaclass=ParameterReader):
    def __init__(self, color):
        print(color)

class Corn(Fruit):
    def __init__(self, size, *args, **kwargs):
        super().__init__(*args, **kwargs)
        print(size)
        print(self._init_parameters)

print(Corn._init_parameters)

Producing:

{'color': <Parameter "color">, 'size': <Parameter "size">}

As well as in instantiation:

Corn(10, 'yellow')

Producing:

yellow
10
{'color': <Parameter "color">, 'size': <Parameter "size">}

Note how this handles using *args, **kwargs in Corn's __init__ parameters.


Also, note the naming difference between arguments and parameters.

Upvotes: 2

Bahrom
Bahrom

Reputation: 4862

Update for Python 3.3+ (as pointed out by beeb in the comments)

You can use inspect.signature introduced in Python 3.3:

class Super:
    def __init__(self, name, kwarg='default'):
        print('instantiated')
        self.name = name

>>> import inspect
>>> inspect.signature(Super.__init__)
<Signature (self, name, kwarg='default')>

Original answer below

You can use inspect

>>> import inspect
>>> inspect.getargspec(Super.__init__)
ArgSpec(args=['self', 'name'], varargs=None, keywords=None, defaults=None)
>>> 

Edit: inspect.getargspec doesn't actually create an instance of Super, see below:

import inspect

class Super:
    def __init__(self, name):
        print 'instantiated'
        self.name = name

print inspect.getargspec(Super.__init__)

This outputs:

### Run test.a ###
ArgSpec(args=['self', 'name'], varargs=None, keywords=None, defaults=None)
>>> 

Note that instantiated never got printed.

Upvotes: 45

Related Questions