0

I am looking for fixed keys, like a namedtuple, but mutable values like a dict. I would probably have used a struct in C. Is there such a data structure in Python?

Thomas Arildsen
  • 1,079
  • 2
  • 14
  • 31
  • Just write a custom class. For a simple, "record-like" type, you can use the `dataclasses.dataclass` code generator to avoid boilerplate – juanpa.arrivillaga Dec 16 '21 at 21:50
  • It is important to understand, Python is a *purely object oriented language* (and no, Java fans, that doesn't mean that everything is a class definition!). So, when you think of a C struct definition , you probably want some sort of class definition. Note, `namedtuple` is just a *class factory*. It generates a new type, a subclass of `tuple` – juanpa.arrivillaga Dec 16 '21 at 21:54

2 Answers2

5

You may use a dataclass.

Dataclasses are close to mutable namedtuples. They're not a tuple, but I believe they will satisfy your needs.

Bharel
  • 23,672
  • 5
  • 40
  • 80
  • It is important to note, a data class isnt a *type of object*, that is a decorator which generates code to avoid boilerplate. But yeah, this is probably a good solution for the OP – juanpa.arrivillaga Dec 16 '21 at 21:50
  • @juanpa.arrivillaga while it's technically true, it's much more than a simple decorator, considering there is even a check for [is_dataclass](https://docs.python.org/3/library/dataclasses.html#dataclasses.is_dataclass), so I will tend to disagree with the statement that it isn't a type of an object, as much as an iterator isn't technically a type but rather a protocol. – Bharel Dec 16 '21 at 21:53
  • Well, no. All that does is check if the type of object you passed in has the attribute `__dataclass_fields__`. Like, go ahead and define `class Foo: pass` then `Foo.__dataclass_fields__ = None` then check `is_dataclass(Foo())` – juanpa.arrivillaga Dec 16 '21 at 21:57
  • Note, while this sounds nitpicky, I think it is an important thing to keep in mind. I up voted, for the record – juanpa.arrivillaga Dec 16 '21 at 22:00
  • @juanpa.arrivillaga and all `isinstance(a, collections.abc.Iterator)` does is check `__iter__` and `__next__`. Does that mean `a` isn't an iterator or that iterator isn't a type? – Bharel Dec 16 '21 at 22:00
  • yes it isn't a type. An iterator is any object that implements the iterator protocol. Understanding what a type is is important. It is basically one of the central aspects of the language – juanpa.arrivillaga Dec 16 '21 at 22:01
  • @juanpa.arrivillaga it is a little nitpicky, as when people say a certain class is a type of something, they can mean it inherits from, it implements a certain interface, it implements a certain protocol. They don't necessarily mean it inherits from a specific class. A list is a type of a container. – Bharel Dec 16 '21 at 22:01
  • 1
    For sure, I understand what you are saying. Python is very much a language that embraces "duck typing" anyway. But this is programming, it is hard to be *too* pedantic. ;) And I just want to be precise for someone coming from a language like C, which is weakly typed, indeed, types are basically something the compiler sees. – juanpa.arrivillaga Dec 16 '21 at 22:03
  • `dataclass` does look useful. When I take https://stackoverflow.com/a/45426493/865169 for a quick spin, I get a `Point` `p` where I can still say `p.a = 1`. Although `p` does not immediately show me the property `a` when I display it (only `x`, `y`, and `z`), it is still there; I can get it as `p.a`. The ability to just add additional properties/keys at will is kind of what I was trying to avoid. Is there no property-/key-wise immutable solution? – Thomas Arildsen Dec 17 '21 at 22:26
  • 1
    @Thomas Add `@dataclass(slots=True)` – Bharel Dec 18 '21 at 06:53
  • Thanks @Bharel. I didn't understand what that option meant. – Thomas Arildsen Dec 19 '21 at 18:44
1

Check the dataclass decorator:

https://docs.python.org/3/library/dataclasses.html#dataclasses.asdict

from dataclasses import dataclass

@dataclass
class Point:
  x: int
  y: int

p = Point(10,20)
# or: Point(x=10, y=20)

print(p)
# Output: Point(x=10, y=20)
print(p.x)
# Output: 10
albert
  • 4,290
  • 1
  • 21
  • 42