inducer / pymbolic

A simple package to do symbolic math (focus on code gen and DSLs)
http://mathema.tician.de/software/pymbolic
Other
106 stars 25 forks source link

Method for removing the `__getinitargs__` and `init_arg_names` boilerplate. #46

Open Erotemic opened 3 years ago

Erotemic commented 3 years ago

I was playing with the library, and I noticed that expressions seem to require these boilerplate __getinitargs__ and init_arg_names methods. Given that this is Python 3.6+ it should be easy to automate that via introspection. Here is a proof of concpet:

from pymbolic.primitives import Expression

class AutoInspectable(object):
    """
    Helper to provide automatic defaults for pymbolic expressions
    """

    def init_arg_names(self):
        return tuple(self._initkw().keys())

    def __getinitargs__(self):
        return tuple(self._initkw().values())

    def _initkw(self):
        import inspect
        from collections import OrderedDict
        sig = inspect.signature(self.__class__)
        initkw = OrderedDict()
        for name, info in sig.parameters.items():
            if not hasattr(self, name):
                raise NotImplementedError((
                    'Unable to introspect init args because the class '
                    'did not have attributes with the same names as the '
                    'constructor arguments'))
            initkw[name] = getattr(self, name)
        return initkw

class AutoExpression(AutoInspectable, Expression):
    pass

Now classes that inherit from AutoInspectable will introspect their __init__ functions and as long a there are no *args **kwargs and all arguments have instance attributes with the same name, it provides a function to return an "initkw" dictionary (i.e. keyword arguments that you could pass to the class to construct a new instance).

There are edge cases where this doesn't work. Namely *args, **kwargs, and when the instance doesn't exactly register the constructor arguments with the same names, but in those cases it raises a NotImplementedError indicating that the user can fill these out manually.

Just thought I'd throw that out there.

inducer commented 3 years ago

Thanks for the suggestion! With time, this might be something to look into. The name is a bit misleading though: __getinitargs__ is mainly used to find field names that matter, e.g. for __eq__ comparison and hashing. If I were starting the library today, I'd probably rely on dataclasses mechanisms for most of this.