Ruggero Turra
Ruggero Turra

Reputation: 17670

Automatic transformation from getter/setter to properties

I have a big library written in C++ and someone created an interface to use it in python (2.6) in an automatic way. Now I have a lot of classes with getter and setter methods. Really: I hate them.

I want to re-implement the classes with a more pythonic interface using properties. The problem is that every class has hundreds of getters and setters and I have a lot of classes. How can I automatically create properties?

For example, if I have a class called MyClass with a GetX() and SetX(x), GetY, SetY, etc... methods, how can I automatically create a derived class MyPythonicClass with the property X (readable if there is the getter and writable if there is the setter), and so on? I would like a mechanism that lets me to choose to skip some getter/setter couples where it is better to do the work by hand.

Upvotes: 4

Views: 2712

Answers (4)

Moran
Moran

Reputation: 11

class BaseObj:
    test = None
    def __init__(self, attributes_dict):
        self.attributes_dict = attributes_dict
        self.define_getters(attributes_dict.keys())
    def define_getters(self, attributes_names):
        for attribute_name in attributes_names:
            setattr(self, "get_"+attribute_name, self.getter_factory(attribute_name))
    def getter_factory(self, attribute_name):
        """Method for generating getter functions"""
        def getter():
            return self.attributes_dict[attribute_name]
        return getter

class DerivedObj(BaseObj):
    attributes_keys = ['name']
    def __init__(self, attributes_dict):
        BaseObj.__init__(self, attributes_dict)

a = DerivedObj({'name':'kuku'})
a.get_name()  # -> kuku

Upvotes: 1

Mike Graham
Mike Graham

Reputation: 76683

Be careful using magic, especially magically altering other people's bindings. This has the disadvantages that

  • Your code is incompatible with other people accessing the same library. Someone can't import one of your modules or copy and paste your code and have it work perfectly with their program accessing the same library, and
  • Your interface is different from the interface to the C++ code. This would make sense if your wrapper gave you a nicer, higher-level interface, but your changes are only trivial.

Consider whether it wouldn't make more sense just to deal with the library you are using as it came to you.

Upvotes: 1

John La Rooy
John La Rooy

Reputation: 304147

Here's a way to do it with a class decorator

def make_properties(c):
    from collections import defaultdict
    props=defaultdict(dict)
    for k,v in vars(c).items():
        if k.startswith("Get"):
            props[k[3:]]['getter']=v
        if k.startswith("Set"):
            props[k[3:]]['setter']=v
    for k,v in props.items():
        setattr(c,k,property(v.get('getter'),v.get('setter')))
    return c

@make_properties
class C(object):
    def GetX(self):
        print "GetX"
        return self._x

    def SetX(self, value):
        print "SetX"
        self._x = value

c=C()
c.X=5
c.X

Here is a slightly more complicated version that allows you to specify a list of items to skip

def make_properties(skip=None):
    if skip is None:
        skip=[]
    def f(c):
        from collections import defaultdict
        props=defaultdict(dict)
        for k,v in vars(c).items():
            if k.startswith("Get"):
                props[k[3:]]['getter']=v
            if k.startswith("Set"):
                props[k[3:]]['setter']=v
        for k,v in props.items():
            if k in skip:
                continue
            setattr(c,k,property(v.get('getter'),v.get('setter')))
        return c
    return f

@make_properties(skip=['Y'])
class C(object):
    def GetX(self):
        print "GetX"
        return self._x

    def SetX(self, value):
        print "SetX"
        self._x = value

    def GetY(self):
        print "GetY"
        return self._y

    def SetY(self, value):
        print "SetY"
        self._y = value

c=C()
c.X=5
c.X
c.Y=5
c.Y

Upvotes: 9

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798616

Use a metaclass that looks for all attributes like Get* and Set* and adds appropriate properties to the class. Have a class attribute that you can set to a sequence containing properties that will be skipped. See this answer for details about setting attributes in the class.

Upvotes: 2

Related Questions