ypstruct: MATLAB-like Structure Data Type for 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:
- using
dict
(see here), the built-in dictionary type in Python, which is actually a key-value array of items; - using an empty class or a temporary class, and then adding required fields to it;
- using a hard-coded class, with all required fields and methods;
- and using
collections.namedtuple
class (see here), which is a tuple with keys for each item, and that is an immutable type.
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.yprint(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.
- Differential Evolution (DE) in Python, an evolutionary algorithm, which is a general-purpose optimization algorithm. GitHub repository is available here.
- Genetic Algorithm (GA) in Python, a well-known evolutionary algorithm inspired by natural evolution, which is a general-purpose optimization approach. I implemented this code in a video course on Genetic Algorithms, which is freely available on YouTube, via this link and its related parts.
Hope you enjoyed this post on ypstruct and structures in Python.