webots icon indicating copy to clipboard operation
webots copied to clipboard

Use more setters and getters in the Python API

Open omichel opened this issue 5 years ago • 11 comments

Python users are used to using getters and setters to access properties of objects. See for example #1687. Such getters and setters are not implemented in the Webots Python API. They could be easily added. See https://www.geeksforgeeks.org/getter-and-setter-in-python for reference.

omichel avatar May 22 '20 07:05 omichel

This is, unfortunately, one more drawback of using Swig, our Python API comes from the C++ one and this is clearly visible (not pythonic at all) :disappointed:

DavidMansolino avatar May 25 '20 06:05 DavidMansolino

My suggestion is to add (and maintain) python getters and setters in addition to the functions derived from Swig (to keep backwards compatibility).

omichel avatar May 25 '20 07:05 omichel

Maybe, this could be a good opportunity to go from camel-case to underscore notation (recommended by PEP 8).

lukicdarkoo avatar May 25 '20 07:05 lukicdarkoo

it can be solved by replacing every setter and getter in the code with its structure attribute by a scripting file in webots api layer before generating c++ api with swig, it could be temporary solution to avoid backwards compatibility

AhmedMoamen62 avatar Jun 02 '20 00:06 AhmedMoamen62

@AhmedMoamen62: can you provide a simple example to explain what you mean?

omichel avatar Jun 02 '20 05:06 omichel

if i understand right, the problem is, camera_recognition_object struct for example

typedef struct {
 int      id;
 double   position[3];
 double   orientation[4];
 double   size[2];
 int      position_on_image[2];
 int      size_on_image[2];
 int      number_of_colors;
 double  *colors;
 char    *model;
} WbCameraRecognitionObject;

and in python we want to access attributes of the struct by setters and getters as

camera_recognition_object.get_id()

instead of

 camera_recognition_object.id

but If you wrap a C structure with SWIG , it is wrapped by a Python class so it will work with just

camera_recognition_object.id 

as in this swig tutorial http://www.swig.org/Doc1.3/Python.html#Python_nn19

so we can write a script to check if python file have any setter or getter function and replace it with the opposite attribute so SWIG can work and compile

AhmedMoamen62 avatar Jun 02 '20 06:06 AhmedMoamen62

OK, I see. But WbCameraRecognitionObject is a special case. In most cases, we have API functions like webots::Camera::getImage() (in C++) which we would like to translate in Python to Camera.image instead of Camera.getImage(). But we would like to keep the later option for backwards compatibility.

omichel avatar Jun 02 '20 06:06 omichel

For conversion from camel-case to underscore notation we can use SWIG regex: http://www.swig.org/Doc2.0/SWIG.html#SWIG_advanced_renaming

For getters and setters, we can implement generating implementation in Python with setattr() and getattr(): https://docs.python.org/3/library/functions.html#setattr

Just ideas, not sure if they are going to work. I can make a minimal proof-of-concept if needed

lukicdarkoo avatar Jun 02 '20 11:06 lukicdarkoo

Yes, if you can quickly make a minimal proof-of-concept, that would be helpful.

omichel avatar Jun 02 '20 12:06 omichel

import re
import random


def add_setter_getters(class_type):
    """Function for adding getters and setters."""

    for full_attr_name in dir(class_type):
        # Find getter and setter methods
        parsed_attr = re.findall(r'^(get|set)([A-Z].*?)$', full_attr_name)
        if parsed_attr:
            # Get relavant data getter and setter methods
            property_suffix = parsed_attr[0][1]
            attr_name = property_suffix.lower()

            # Get set[Something] and get[Something] methods
            fget = None
            fset = None
            if hasattr(class_type, 'get' + property_suffix):
                fget = getattr(class_type, 'get' + property_suffix)
            if hasattr(class_type, 'set' + property_suffix):
                fset = getattr(class_type, 'set' + property_suffix)

            # Create a property
            setattr(class_type, attr_name, property(
                fget=fget,
                fset=fset
            ))


# Our class
class DistanceSensor:
    def __init__(self):
        self.__name = 'distance_sensor'

    def getValue(self):
        return random.randint(1, 100)

    def getName(self):
        return self.__name

    def setName(self, name):
        self.__name = name


# Apply Pythonic setters and getters
add_setter_getters(DistanceSensor)

# Do a few tests
ds = DistanceSensor()
print(ds.value)
print(ds.value)
print(ds.value)
print(ds.name)
ds.name = 'new_distance_sensor'
print(ds.name)

add_setter_getters() is important part that we would put in controller.i, the rest is only for testing purposes. Also, it probably could be simplified.


Similar could be done for camel-case to underscore conversion, but that part could be also done with SWIG regex I believe.

lukicdarkoo avatar Jun 02 '20 14:06 lukicdarkoo

Now that we got rid of SWIG, the setters and getters could be implemented more easily.

ygoumaz avatar Nov 08 '22 10:11 ygoumaz