Introspecting arguments from the constructor function __init__ in Python
Asked Answered
O

2

24

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?

Overripe answered 25/4, 2016 at 19:51 Comment(3)
Question is not clear. Do you mean you want to check value of name and decide if you want to create new instance or not?Circumscribe
I don't want to create instance at all. I want to introspect objects arguments. It is just like question "what arguments this object have". It is all about general introspection not about values. I changed question to make it a bit more understandable.Overripe
Instance is already created by the time __init__ gets called. See this answer https://mcmap.net/q/37833/-why-is-__init__-always-called-after-__new__Circumscribe
P
43

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.

Phira answered 25/4, 2016 at 19:57 Comment(6)
It looks like that it is what I was looking for. One additional question: Is there an option to get that result without "inspect" module. Something like Super.__dict__.keys solution if it is not, that means that inspect.getargspec() is like instance simulation that helps to retrieve arguments?Overripe
There are a bunch of answers for retrieving argument names out of a function - #582556 and it looks like inspect is the only way. Even using globals returns in a function call, which is not what you want. Incidentally, using inspect doesn't result in a function call, editing my answer for an example...Phira
@Phira anyway to do this for a hierarchy of inheriting classes?Bloodsucker
@VeltzerDoron I'll take a look when I get homePhira
Actually, I already implemented it by travelling up the class.mro() and getting the init signature along the way, I used it to write a custom JSON encoder that doesn't encode members with default values, works like a charm. :)Bloodsucker
DeprecationWarning: inspect.getargspec() is deprecated since Python 3.0, use inspect.signature() or inspect.getfullargspec()Saviour
A
1

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.

Anility answered 31/5, 2022 at 17:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.