Easy and safe C-struct-like, dot-accessed (my_struct.my_var) containers in Python
3 techniques:
- [BEST] Python
dataclasses (Python 3.7+)
- ie:
@dataclass decorator
- A class with predefined
self members
- An empty class
1. [BEST] Python dataclasses
As of Python 3.7, dataclasses are now built in. These are the most C-struct-like way to do this in Python. They are special classes wrapped with the @dataclass decorator in order to enforce "struct" member names and types at instance construction, and even provide default values. All members which do not have default values must be provided to the constructor when creating an instance of a class decorated with @dataclass. Dataclass "struct" members which have default values do not have to be provided at construction. Dataclass "struct" members are accessed with dot notation, just like in C/C++.
Reference the official Python documentation here: https://docs.python.org/3/library/dataclasses.html
Example usage:
dataclass_example.py from my eRCaGuy_hello_world repo:
#!/usr/bin/env python3
from dataclasses import dataclass
@dataclass
class MyStruct:
"""
A simple struct-like dataclass with some fields of various types.
2 fields have default values.
"""
name: str
x: int
y: int
z: float = 0.0 # default value
is_ready: bool = False # default value
# Create an instance of MyStruct
my_data = MyStruct(name="My struct", x=123, y=456)
my_data.z = 789.001 # set z later if desired
my_data.x = 999 # modify x later if desired
my_data2 = MyStruct(
name="Another struct",
x=10,
y=20,
z=30.0,
is_ready=True
)
print(my_data)
print(my_data2)
Output:
MyStruct(name='My struct', x=999, y=456, z=789.001, is_ready=False)
MyStruct(name='Another struct', x=10, y=20, z=30.0, is_ready=True)
Illegal operations
All specified members of a dataclass which do not have a default value must be provided at construction time, or a TypeError is raised:
# TypeError: MyStruct.__init__() missing 1 required positional argument: 'y'
my_data = MyStruct(name="Some useful name of this data", x=123)
Not recommended
Adding new field on the fly kind of works: you can do it, but printing the whole object won't show it. It is not recommended:
# Adding a new member `w` at run-time is allowed, but not recommended
my_data.w = 7
# will NOT print the newly-added `w` member; prints only:
# `MyStruct(name='My struct', x=999, y=456, z=789.001, is_ready=False)`
print(my_data)
# will print `7`
print(my_data.w)
2. A good balance between the simplest and the most self-documenting is a class with predefined self members
An easy and good way to do this is to define a class with predefined members, like this. This way the user of the class can easily see what you expect to put inside it!
If you want a constructor which prefills the members, you can do that too, but I'm explicitly avoiding that to not have to type it all out. Usually I don't need a "constructor", per se, so this is simpler and faster to write:
class MyStruct():
def __init__(self):
# predefined members expected to be used in this class
self.name = None
self.x = None
self.y = None
self.z = None
self.is_ready = False
Then, you can use it like this:
# Construct an instance of the class, `MyStruct`
my_data = MyStruct()
# Add any variables or values you want to it
my_data.name = "Some useful name of this data"
my_data.x = 123
my_data.y = 456
my_data.z = 789.001
my_data.is_ready = True
# You are not limited to those members, you can actually arbitrarily add any
# members you want to it at run-time
my_data.some_other_var = "Some other value"
my_data.whatever = 35
# etc.
3. The absolute simplest is an empty class
...like this:
class AnyStruct():
pass
I'm tired of writing stuff like:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
Technically, you don't have to write all that. The simplest class just contains pass. And, classes in Python are naturally C-struct-like and can have any data arbitrarily added to them at run-time. So, just do this:
# Create a universal C-struct-like class
class AnyStruct():
pass
# Make a universal print function to print all user-added members of any class
# like this:
def print_any_class(class_instance):
"""
Print all members of a class.
"""
for key, value in class_instance.__dict__.items():
print(f"{key}: {value}")
print()
# Construct an instance of the C-struct-like class, `AnyStruct`
my_data = AnyStruct()
# Add any variables or values you want to it, at run-time
my_data.name = "Some useful name of this data"
my_data.x = 123
my_data.y = 456
my_data.z = 789.001
my_data.is_ready = True
# Print it
print_any_class(my_data)
Output:
name: Some useful name of this data
x: 123
y: 456
z: 789.001
is_ready: True
References
- From my eRCaGuy_hello_world repo:
dataclass_example.py
struct_c_like.py
MyStruct = namedtuple("MyStruct", "field1 field2 field3")