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 开发者_Python百科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.
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
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.
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.
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
精彩评论