views:

260

answers:

3

I have a big libray 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 reimplement 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, ... 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 let me to choose to skip some getter/setter couples where is better to do the work by hand.

+2  A: 

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.

Ignacio Vazquez-Abrams
I'm not very expert, can you give me an explict example for my case?
wiso
`for attr in (x[3:] for x in dir(cls) if x.startswith('Get') and x[3:] not in cls._ignoreaccs):`` accs = [getattr(cls, 'Get%s' % (attr,))]`` if hasattr(cls, 'Set%s' % (attr,)):`` accs.append(getattr(cls, 'Set%s' % (attr,)))`` setattr(cls, attr, property(*accs))`
Ignacio Vazquez-Abrams
You don't have to use a metaclass for this. It would probably be more understandable code to define `__getattr__` and `__setattr__`. You can bring these into your class through inheritance.
Mike Graham
+4  A: 

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
gnibbler
+1  A: 

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.

Mike Graham
You are right, but it's not a very big problem, I'm not developing for other people, I'm creating this interface because I don't like the getter/setter interface (also in C++). My interface extend the old interface, it's not a substitution, the getter/setter methods are still there.
wiso