32

Is there a data type in Python similar to structs in C++? I like the struct feature myStruct.someName. I know that classes have this, but I don't want to write a class everytime I need a "container" for some data.

skrx
  • 19,980
  • 5
  • 34
  • 48
Aufwind
  • 25,310
  • 38
  • 109
  • 154
  • 3
    Since in C++ `struct`s and `class`es are the same thing (they only differ in default members' and inheritance access specifiers) you could just write a `class`... – Matteo Italia May 09 '11 at 23:00
  • 2
    Generally we use a dict, where the syntax is `mydict["someName"]`. But if you define your own class once, you can then add any attribute you want to instances. – Thomas K May 09 '11 at 23:00
  • @Thomas K: You mean I can create an instance `foo` of a class `bar` and add attributes to it dynamically? Like first `foo` it is empty, then I add an attribute and then another one and so on? – Aufwind May 09 '11 at 23:04
  • 3
    Yes. `class Struct(object): pass` `s = Struct()` `s.a = 5` `print(s.a)` prints 5. – Ponkadoodle May 09 '11 at 23:07
  • @All: Thank you all for the advices! – Aufwind May 10 '11 at 02:56

8 Answers8

37

Why not? Classes are fine for that.

If you want to save some memory, you might also want to use __slots__ so the objects don't have a __dict__. See http://docs.python.org/reference/datamodel.html#slots for details and Usage of __slots__? for some useful information.

For example, a class holding only two values (a and b) could looks like this:

class AB(object):
    __slots__ = ('a', 'b')

If you actually want a dict but with obj.item access instead of obj['item'], you could subclass dict and implement __getattr__ and __setattr__ to behave like __getitem__ and __setitem__.

Community
  • 1
  • 1
ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • 1
    Thanks. I will stick with classes! :) Your suggestions of "modifying" dict is really cool! – Aufwind May 09 '11 at 23:21
  • 1
    This is a seriously underrated answer in an obscure topic. A dict with `__getattr__` and `__setattr__` functions seems to me like a very elegant way to implement struct-like behaviour in python. – kadu Sep 03 '14 at 15:54
17

In addition to the dict type, there is a namedtuple type that behaves somewhat like a struct.

MyStruct = namedtuple('MyStruct', ['someName', 'anotherName'])
aStruct = MyStruct('aValue', 'anotherValue')

print aStruct.someName, aStruct.anotherName
sock
  • 1,054
  • 2
  • 8
  • 17
  • This reminds me of "user defined types" from Visual Basic 6.0. They were VB's analog to C++'s struct. Even the way it is declared is similar. http://www.vb6.us/tutorials/user-defined-types-udt-vb – Neamerjell May 29 '16 at 08:04
  • 2
    Note that a `namedtuple` is read-only, i.e. you cannot change values like `aStruct.someName = "foo"` (of course this is consistent with normal tuples). – luator Jun 28 '17 at 12:10
15

dataclass is now built-in to Python as of Python 3.7!

from dataclasses import dataclass

@dataclass
class EZClass:
    name: str='default'
    qty: int

Test:

classy = EZClass('cars', 3)
print(classy)

Output:

EZClass(name='cars', qty=3)

Besides for the automatic initialization and __repr__ methods which it generates, it also automatically creates an __eq__ method to make it simple and intuitive to compare two instances of the class.

See PEP 557.

Backported to Python 3.6 using the dataclasses package.

ChaimG
  • 7,024
  • 4
  • 38
  • 46
6

Please realise that in C++, the only difference between a class and a struct is that the elements of a class are by default private, as is the inheritance. The following are equivalent:

class D : public B {
    public:
    ...
}; 

struct D {
    ...
};

In Python, it would make most sense to use a class if you want to use the dot operator to access elements. In fact, it's even easier, as you only have to initialise the members you currently want, and can add/remove members later. Therefore, the following would work:

class D(object):
    pass

You can then add as many members as you want simply by assigning to them.

Cactus Golov
  • 3,474
  • 1
  • 21
  • 41
5

You can always go for the dynamic approach:

class foo(object):
    def __init__(self,**kwargs):
        self.__dict__.update(kwargs)

This'll make a class with the same methods that you pass in as a dict:

bar = foo(bill="yo",heather="hi",sam="piss off")

leaving you with the following perfectly valid calls on bar:

bar.bill
>> "yo"
bar.heater
>> "hi"

you get the idea...

wheaties
  • 35,646
  • 15
  • 94
  • 131
3

I believe you're looking for a dict.

d = dict({
        'name': 'myname',
        'val': 'myval'
        })

print d
print d['name']
Demian Brecht
  • 21,135
  • 5
  • 42
  • 46
3

Try using a dict.

Here's a simplistic demonstration.

>>> something = {}
>>> something['x'] = 42
>>> something['y'] = 'cheese'
>>> something
{'y': 'cheese', 'x': 42}
>>> something['x']
42
>>> something['y']
'cheese'
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
1

This may be taking the idea a bit too far, but here's a way to create "structs" using a syntax that's kinda similar to C++ structs that also does some type checking. First, here's an example of using it:

>>> MyStruct = Struct({
...     'i': int,
...     's': str,
...     'x': float,
... }, 'MyStruct')
>>> print(MyStruct)
MyStruct {
    i: int,
    s: str,
    x: float,
}

>>> instance = MyStruct(i=1, s='s', x=1.0)
>>> print(instance)
MyStruct(i: 1, s: 's', x: 1.0)

And here's the implementation. It's a variation of the __slots__ idea where the class with slots (i.e., the "struct" type) is generated dynamically. This could of course be fleshed out in various ways, but this is just a proof of concept.

class Struct:

    class StructInstance:

        __slots__ = ()

        def __str__(self):
            values = []
            for name in self.__slots__:
                value = getattr(self, name)
                values.append('{name}: {value!r}'.format(name=name, value=value))
            type_name = self.__class__.__name__
            values = ', '.join(values)
            return '{type_name}({values})'.format(type_name=type_name, values=values)

    def __init__(self, fields, name=None):
        for field_name, field_type in fields.items():
            assert isinstance(field_name, str), 'Expected str for field name'
            assert isinstance(field_type, type), 'Expected type for field type'
        self.fields = fields
        self.name = name or 'Struct'
        self.type = type(
            self.name, (self.StructInstance,), {'__slots__': tuple(fields)})

    def __call__(self, **values):
        instance = self.type()
        for name in instance.__slots__:
            value = values[name]
            expected_type = self.fields[name]
            assert isinstance(value, expected_type), 'Expected %s for %s' % (expected_type, name)
            setattr(instance, name, value)
        return instance

    def __str__(self):
        fields = ['    {n}: {t.__name__},'.format(n=n, t=t) for n, t in self.fields.items()]
        return '{name} {{\n{fields}\n}}'.format(name=self.name, fields='\n'.join(fields))