1

I've been switching from Matlab to NumPy/Scipy, and I think NumPy is great in many aspects.

But one thing that I don't feel comfortable is that I cannot find a data structure similar to struct in C/C++.

For example, I may want to do the following thing:

struct Parameters{
  double frame_size_sec;
  double frame_step_sec;
}

One simplest way is using a dictionary as follows.

  parameters = {"frame_size_sec" : 0.0, "frame_step_sec", 0.0}

But in case of a dictionary, unlike struct, any keys may be added. I'd like to restrict keys.

The other option might be using a class as follows. But it also has the same type of problems.

class Parameters:
  frame_size_sec = 0.0
  frame_step_sec = 0.0

From a thread, I saw that there is a data structure called named tuple, which looks great, but the biggest problem with it is that fields are immutable. So it is still different from what I want.

In sum, what would be the best way to use a struct-like object in python?

0

2 Answers 2

5

If you don't need actual memory layout guarantees, user-defined classes can restrict their set of instance members to a fixed list using __slots__. So for example:

class Parameters:  # On Python 2, class Parameters(object):, as __slots__ only applies to new-style classes
    __slots__ = 'frame_size_sec', 'frame_step_sec'
    def __init__(self, frame_size_sec=0., frame_step_sec=0.):
        self.frame_size_sec = float(frame_size_sec)
        self.frame_step_sec = float(frame_step_sec)

gets you a class where on initialization, it's guaranteed to assign two float members, and no one can add new instance attributes (accidentally or on purpose) to any instance of the class.

Please read the caveats at the __slots__ documentation; in inheritance cases for instance, if a superclass doesn't define __slots__, then the subclass will still have __dict__ and therefore can have arbitrary attributes defined on it.

If you need memory layout guarantees and stricter (C) types for variables, you'll want to look at ctypes Structures, but from what you're saying, it sounds like you're just trying to enforce a fixed, limited set of attributes, not specific types or memory layouts.

Sign up to request clarification or add additional context in comments.

1 Comment

FYI, using __slots__ does have some advantages. CPU-wise, it's basically irrelevant, but it reduces the per-instance memory cost significantly. On Python 3.4 (64 bit), for a 2 variable instance, the overhead of the object structure plus the structure for its __dict__ is 152 bytes, vs. 56 bytes for the object structure (and no __dict__) if it's defined with __slots__. The difference on Python 2 is even more extreme (because Python 2 lacks shared-key dictionaries; the base object structure in both cases uses 64 bytes, but the __dict__ required w/o __slots__ takes it up to 344 bytes).
1

While taking the risk of not being very Pythonic, you can create an immutable dictionary by subclassing the dict class and overwriting some of its methods:

def not_supported(*args, **kwargs):
        raise NotImplementedError('ImmutableDict is immutable')


class ImmutableDict(dict):
    __delitem__ = not_supported
    __setattr__ = not_supported
    update = not_supported
    clear = not_supported
    pop = not_supported
    popitem = not_supported

    def __getattr__(self, item):
        return self[item]

    def __setitem__(self, key, value):
        if key in self.keys():
            dict.__setitem__(self, key, value)
        else:
            raise NotImplementedError('ImmutableDict is immutable')

Some usage examples:

my_dict = ImmutableDict(a=1, b=2)
print my_dict['a']
>> 1
my_dict['a'] = 3 # will work, can modify existing key
my_dict['c'] = 1 # will raise an exception, can't add a new key
print my_dict.a # also works because we overwrote __getattr__ method
>> 3

3 Comments

That doesn't have a class wide limitation on the set of fields, it just limits you to defining the fields at instantiation time (and the OP explicitly said he didn't want immutable fields).
@ShadowRanger I've edited my answer to allow modifying existing keys but not allowing to add new keys, which is exactly what the OP wants.
Might want to have __setattr__ = __setitem__ so my_dict.a = 3 works without exposing the "it's really a dictionary, not an object" thing too obviously.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.