gin-config icon indicating copy to clipboard operation
gin-config copied to clipboard

inheriting configs with default parameters

Open inoryy opened this issue 5 years ago • 3 comments

Let's consider a simple two-class hierarchy:

@gin.configurable
class A:
  def __init__(self, a=1):
    self.a = a

@gin.configurable
class B(A):
  def __init__(self, a=1, b=2):
    super().__init__(a)
    self.b = b

Ideally I would like to replicate this structure in my .gin config files:

A.a = 3
B.b = 4

So that when I call obj = B() it would contain obj.a == 3 and obj.b == 4.
However, right now this does not happen - I get obj.a == 1 and obj.b == 4.
This is because gin detects that A constructor is manually called with a set as B's default.

The current workaround is to define full configuration for each class separately, however that becomes quite cumbersome when configuring many classes that inherit from one base class with majority of the parameters shared.


One possible fix would be to reuse existing macro syntax and explicitly replicate the hierarchy in .gin config, e.g.:

A.a = 3

B = #A
b.b = 4

inoryy avatar Dec 28 '18 21:12 inoryy

This a bit hard, since it will violate the Python behavior. But maybe Dan would have some insights in a few weeks.

sguada avatar Jan 17 '19 02:01 sguada

As a reference I've made a hacky work-around that implicitly does this by checking locals() and only passing arguments forward if they differ from defaults. Here's how it looks in the inheriting class. Then in the config file I can define the global configs for the parent class, only overriding relevant parts for children.

Of course when doing this from the outside you most likely can't just call locals(), but maybe the general idea could help.

inoryy avatar Jan 17 '19 09:01 inoryy

I don't know if this is a solution to your problem, but it works if you don't explicitly have the arguments of the superclass in the constructor of your subclass but rather use *args and **kwargs:

import gin


@gin.configurable
class A:
    def __init__(self, a=1):
        self.a = a


@gin.configurable
class B(A):
    def __init__(self, b=2, *args, **kwargs):
        super(B, self).__init__(*args, **kwargs)
        self.b = b


if __name__ == '__main__':
    gin.parse_config(["A.a = 3", "B.b = 4"])
    
    obj = B()
    print(obj.a)
    print(obj.b)

gregorgebhardt avatar Aug 02 '19 11:08 gregorgebhardt