Search code examples
pythonpython-attrs

How can i define an object of arrays using attrs?


Consider the following data set:

{
    'name': 'somecat',
    'lives': [
        {'number': 1, 'did': 'nothing'},
        {'number': 2, 'did': 'saved the world'}
    ]
}

What I am trying to do is use attrs to define a dataclass so that I can get autocompletion even when using an index number

import attr

@attr.s
class Cat(object):
    name = attr.ib()
    lives: list = [
        {'number': int, 'did': str} # trying to get autocompletion here
    ]


c = Cat('somecat')
print(c)
c.lives[0].number # trying to get autocompletion here

The above code is not valid, but this is what I am trying to accomplish.

How can I go about doing this? I am aware of metadata, but that is immutable. I am open to using dataclasses also if that makes more sense.


Solution

  • Admittedly I never really use the attr module but in order to make minimal changes to your code. I think using typing.List is also useful here. I personally would've used dataclasses but this seems to work as well

    import attr
    import typing
    from collections import namedtuple
    
    live = namedtuple('live', ['number', 'did'])
    
    
    @attr.s
    class Cat(object):
        name = attr.ib()
        lives: typing.List[live] = attr.ib()
    
    
    c = Cat('somecat', lives=[live(**{'number': 1, 'did': 'smile'})])
    print(c)
    c.lives[0].number  # auto completes
    

    With just dataclasses

    import typing
    from dataclasses import dataclass
    
    
    @dataclass
    class live:
        number: int
        did: str
    
    
    @dataclass
    class Cat:
        name: str
        lives: typing.List[live]
    
    
    c = Cat('somecat', lives=[live(**{'number': 1, 'did': 'smile'})])
    print(c)
    c.lives[0].number  # autocompletes
    

    But for nested dictionaries, these dataclasses can be tough. Like so

    data = {
        'name': 'somecat',
        'lives': [
            {'number': 1, 'did': 'nothing'},
            {'number': 2, 'did': 'saved the world'}
        ]
    }
    
    new_c = Cat(**data)
    new_c.lives = [live(**data) for data in new_c.lives]
    

    If I may, I would suggest looking into pydantic.

    Thanks