[Python] subclassing Q_ in pint - why does the super call not take arguments?

Discussão em 'Python' iniciado por Stack, Março 3, 2025.

  1. Stack

    Stack Membro Participativo

    (Python 3.7)

    pint / Q_ behavior

    I want to subclass pint's quantity class and override __init__. The standard syntax fails because apparently the arguments bubble up to the object init method (which takes no argument):

    # file: test.py
    from pint import UnitRegistry

    ureg = UnitRegistry()
    Q_ = ureg.Quantity

    class Q_Child_(Q_):
    def __init__(self, *args, **kwargs):
    super(Q_, self).__init__(*args, **kwargs)
    self.foo = 'foo'

    a=Q_Child_(1.0, 's') # Raises an error:
    # Traceback(most recent call last):
    # File ".../test.py", line 12, in < module >
    # a = Q_Child_(1.0, 's')
    # File ".../test.py", line 9, in __init__
    # super(Q_, self).__init__(*args, **kwargs)
    # TypeError: object.__init__() takes exactly one argument(the instance to initialize)


    What seems to work is to not call super/init at all in the subclassing, but how does this even make sense?

    from pint import UnitRegistry

    ureg = UnitRegistry()
    Q_ = ureg.Quantity

    class Q_Child_(Q_):
    def __init__(self, *args, **kwargs):
    # not calling super or Q_.__init__ (or so it seems)
    self.foo = 'foo'

    a=Q_Child_(1.0, 's')
    print(a) # '1.0 second' -> how does it even know the passed arguments?
    print(a.foo) # 'foo'


    I believe the latter is what I want to use, but using code that works when you think it should not work seems a recipe for disaster. What is happening?

    I guess it is due to Q_ being generated dynamically (when ureg is instantiated, see https://github.com/hgrecco/pint/blob/master/pint/registry.py#L115 and https://github.com/hgrecco/pint/blob/master/pint/quantity.py#L1741) but fail to see why it should matter.

    That question is not a duplicate of correct way to use super (argument passing) (or similar) because the standard way does not work here:

    class NewDict(dict):
    def __init__(self, *args, foo='foo', **kwargs):
    super(NewDict, self).__init__(*args, **kwargs)
    self.foo = foo

    nd = NewDict({'a': 1}, foo='bar')
    print(nd) # {'a': 1}
    print(nd.foo) # 'bar'


    Without the super call, the parent class has no way to know the arguments that it is supposed to initialize with:

    class NewDictWithoutSuper(dict):
    def __init__(self, *args, foo='foo', **kwargs):
    self.foo = foo

    nd = NewDictWithoutSuper({'a': 1}, foo='bar')
    print(nd) # {} i.e. the default dict()
    print(nd.foo) # 'bar'

    Continue reading...

Compartilhe esta Página