cdgriffith / Box

Python dictionaries with advanced dot notation access
https://github.com/cdgriffith/Box/wiki
MIT License
2.61k stars 106 forks source link

Inherit from Box #148

Closed aronchick closed 4 years ago

aronchick commented 4 years ago

I'd like to inherit from Box so that I can add some attributes during creation, particularly with init - are there any examples of doing this out there, or is this verboten?

When I do the following, I get the following errors:

class MLBox(Box):
    def __init__(self, semantic_version, schema_type: MLSchemaTypes, **kwargs):
        self.__version = SemVer.parse(semantic_version)
        self.__schema_type = schema_type

        super().__init__(**kwargs)

Error:

TypeError: __init__() missing 1 required positional argument: 'schema_type'
cdgriffith commented 4 years ago

That should be working if you are passing both required arguments in as listed:

from box import Box

class MLBox(Box):
    def __init__(self, semantic_version, schema_type, **kwargs):
        self.__version = semantic_version
        self.__schema_type = schema_type

        super().__init__(**kwargs)

a = MLBox('3.2.1', 'test')

print(a)
# {'_MLBox__version': '3.2.1', '_MLBox__schema_type': 'test'}

Are you sure you didn't mean to do schema_type=MLSchemaTypes ?

aronchick commented 4 years ago

Hmmm... I think I see the issue, but not sure how to deal with it. The problem actually comes when I merge_update, because it appears that box is attempting to do a new instantiation of a Box without the parameters. Here's the stack trace:

Traceback (most recent call last):
  File "/home/aronchick/mlspec-lib/tests/test_mlobject.py", line 18, in test_create_ml_object
    ml_object = MLObject(schema_version='0.0.1', schema_type=MLSchemaTypes.DATAPATH)
  File "/home/aronchick/mlspec-lib/mlspeclib/mlobject.py", line 37, in __init__
    self.create_stub_object()
  File "/home/aronchick/mlspec-lib/mlspeclib/mlobject.py", line 78, in create_stub_object
    self.merge_update(this_key_dict)
  File "/home/aronchick/mlspec-lib/env/lib/python3.6/site-packages/box/box.py", line 502, in merge_update
    convert_and_set(key, __m[key])
  File "/home/aronchick/mlspec-lib/env/lib/python3.6/site-packages/box/box.py", line 488, in convert_and_set
    v = self.__class__(v, **self.__box_config())
TypeError: __init__() missing 1 required positional argument: 'schema_type' 

Am I misreading it? If so, what are my options here?

Interestingly, i tried your code, and doing a merge_update with just mockup code, and didn't have any issues, so I'm obviously doing something wrong :(

from box import Box

class MLBox(Box):
    def __init__(self, semantic_version, **kwargs):
        self.__semantic_version = semantic_version

        super().__init__(**kwargs)

a = MLBox('3.2.1')
a.merge_update({'foo': 'bar'})

print(a)
# {'_MLBox__semantic_version': '3.2.1', 'foo': 'bar'}
cdgriffith commented 4 years ago

Ah the error you are running into is that Box has so far been designed to have sub-boxes be the same type of box as the parent. Which in this case means a MLBox would have children MLBoxes. Which would mean every sub box has to have the values schema_version and schema_type.

To make inheritance easier, I will have to replace the calls like v = self.__class__(v, **self.__box_config()) to take a variable of what the sub-class should be. That way you could set an argument such as box_class to Box so all sub-boxes would take that type instead of an MLBox.

This will kinda tie into #150 where I will need to take careful care of how such inheritance is handled.

aronchick commented 4 years ago

No worries then - it looks like i'll just need to leave init alone, and anywhere I instantiated it, I'll just have to have a partner function that I call with all the details. Not terribly Pythonic, but that's ok :)

cdgriffith commented 4 years ago

I am starting to cut pre-releases for 5.0.0, if you are feeling adventurous please test it out and let me know if it solves your issues!

pip install --upgrade "python-box[all]>=5.0.0a0"

Details on the new wiki