ypstruct: MATLAB-like Structure Data Type for Python

ypstruct: MATLAB-like Structure Data Type for Python
Structures in MATLAB/Octave and Python

Structures are available in various languages, like C, C++, C# and MATLAB, and they are mainly used to organize data as composite types, containing a determined set of fields. In C, C++ and C#, structure are defined as new data types and then new variables of that type are declared. In C++ and C#, structures are classes and they can include methods and private members, too. In contrast, MATLAB structure is a general data type to organize and group several fields of various types, even of structure type.

In Python, there are several ways to have functionality like structure:

  1. using dict (see here), the built-in dictionary type in Python, which is actually a key-value array of items;

None of these solutions is exactly the same as MATLAB structures. To do this, I developed a subclass of dict, which uses . (dot) operator to access the fields, just like MATLAB and C/C++/C# structures. Also it is capable of getting list of fields, adding and removing fields, merging two or more structures, repeating and replication, and making shallow and deep copies, be means of its defined methods.

This project is named ypstruct, which stands for Yarpiz Structure, and its source code is available on GitHub (here) and it is published on PyPI, the Python Package Index (here), as well. In this article, we will see how ypstruct can be installed and used, to have an experience similar to MATLAB structures in Python.

Installation

The simplest way to install ypstruct on your machine is to use pip. Simply run this in terminal:

pip install ypstruct

to install the ypstruct package on your computer.

Additionally, you can get the source code from ypstruct GitHub repository (here) and include in your project or setup on your machine.

Basic Usage

The structure type in ypstruct package is defined by class struct. This class also has an alias name, structure. You can include struct or structure from this package, or simply include everything available using * symbol.

A simple usage of struct is given in following code block.

from ypstruct import *p = struct()
p.x = 3
p.y = 5
p.A = p.x * p.y
print(p)

The output of this code will be as follows:

struct({'x': 3, 'y': 4, 'A': 12})

In the code block above, after importing the ypstruct classes, an instance of struct is created and initialized without any fields and data in it. Then, fields x and y are set. Then, value of a new field A, is defined using values of two other fields. Finally, the string representation of the structure object is printed out.

In this code, it is possible to initialize the fields and their values using the struct class constructor. That is:

p = struct(x=3, y=4)
p.A = p.x * p.y

How struct and dict are related?

You can access the fields of a structure object using . (dot) operator, as well as the [] indexing operator; because the structure is a subclass of dict, the built-in dictionary type. So, p.x and p['x'] are equivalent and they can interchangeably used.

Also, a structure (struct) object can be converted to and created from a dictionary (dict) object. For example, you can convert a dict object into a struct as follows:

my_dict = {'x':3, 'y':4}
p = struct(my_dict)

Also, structure object can be converted to a dictionary as well:

p = struct(x=3, y=4)
p.z = 12
my_dict = dict(p)
print(my_dict)

The output of this code is shown below.

{'x': 3, 'y': 4, 'z': 12}

Operators and Methods

The struct (also its alias structure) class implements several operators and methods, which can be used to alter the data in structure and its fields, or perform other operations. Note that, any method or operator which is defined for dict class, is also available for these classes too.

The operators and methods defined for struct class are discussed in the following sections of this post.

Merging Structures Using + Operator

Using the + operator, it is possible to merge data from two instances of struct class to create a combined structure object. For example, let us define two structures as follows:

a = struct(x=1, y=2, z=3)
b = struct(y=5, t=20, u=30)

These two struct objects can be merged using + operator:

c = a + b
print(c)

This code will output:

struct({'x': 1, 'y': 5, 'z': 3, 't': 20, 'u': 30})

Note that values from second operand (that is b) are used for common field of two structure. So, we see the value 5 for field y in the merged structure object. The result of merge operation is an structure which contains all of fields defined in operands.

Repeating Structures Using * Operator and repeat() Method

Sometimes we need to repeat/replicate a structure. For example, assume that we are going to implement an Evolutionary Algorithm and we defined the individuals as struct objects. First we need to create a template:

empty_individual = struct(pos=None, fval=None)

Then we can initialize the population array using following code:

pop_size = 10
pop = empty_individual.repeat(pop_size)

This code uses the repeat() method to initialize a list of distinct struct objects with the same data fields.

Instead of using repeat() method, it is possible to use * operator to perform replication of structures. For example the second line of previous code block can be rewritten using * operator as follows:

pop = empty_individual * pop_size

Getting List of Defined Fields Using fields() Method

The fields() method returns a list of fields defined in structure. An example usage follows:

p = struct(x=3, y=4)
print(p.fields())

The output will be:

['x', 'y']

Having an iterable list of fields can be useful when we are going to perform operations on the structure fields. For example, in practical optimization problems we deal with different set of decision variables of various types. One may encapsulate the decision variables inside a struct object. Performing operations, like modification, parsing, pre-processing or post-processing of these decision variables, may be much easier by iterating over the list of decision variables, e.g. defined fields of the structure object.

Adding New Fields Using add_field() Method

A new field can be added using add_field() method. This method accepts two input arguments: field name and its value. The value is optional and if it is ignored, then value is assumed to be None. A sample code follows:

p = struct(x=3, y=4)
p.add_field('z', 12)
p.add_field('L')
print(p)

This code will print out this text as output:

struct({'x': 3, 'y': 4, 'z': 12, 'L': None})

Instead of using the add_field() method, it is possible to simply use . (dot) and = (assignment) operators. For example, the above-mentioned code can be simplified as this:

p = struct(x=3, y=4)
p.z = 12
p.L = None
print(p)

The results of these two code blocks will be the same.

Removing Fields Using remove_field() Method

A field can be removed from a struct object using remove_field() method. This method gets a field name and removes (deletes) the specified field from structure object. An example is given below:

p = struct(x=3, y=4, z=12)
print('Before remove_field: {}'.format(p))
p.remove_field('z')
print('After remove_field: {}'.format(p))

The output will be this:

Before remove_field: struct({'x': 3, 'y': 4, 'z': 12})
After remove_field: struct({'x': 3, 'y': 4})

Making Copies Using copy() and deepcopy() Methods

The struct is a reference type. To have a copy of a struct object, you cannot simply use assignment operator. For example let us create a structure object:

p = struct(x=3, y=4)

Now we assign this value to a new variable and then try to change the value of a field:

q = p
q.x = 5

This will also change the structure object p. Because p and q are referencing to the same object.

To create copies of structure objects, two methods are implemented in struct class: copy() and deepcopy(). The first one gives us a shallow copy of the struct object. But using deepcopy(), as the name of the method says, we can create deep copies of structure objects.

The correct way of creating a copy of p is as follows:

q = p.deepcopy()
q.x = 5

This time, q is referencing to another struct object (event with the same values) and modifying it will not affect p.

Conclusion

Using struct from ypstruct can be very helpful in implementing projects and applications using Python. It saves a lot time in projects which are dealing with class-like data types with fields and without methods. Also it will be useful for MATLAB users who are trying to reimplement their programs in Python, which are using structures.

Previously, I used ypstruct to implement various algorithms and projects. Some example are listed below.

  • Neural Gas (NG) and Growing Neural Gas (GNG) in Python, an unsupervised learning algorithm, inspired by Self-organizing Map (SOM), which is mainly used to perform clustering. GitHub repository is available here.

Hope you enjoyed this post on ypstruct and structures in Python.

Educator, Programmer, AI Enthusiast, and Systems and Control Engineering PhD.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store