4

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.

4

4 回答 4

9

这是一种使用类装饰器的方法

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

这是一个稍微复杂的版本,允许您指定要跳过的项目列表

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
于 2010-02-16T23:03:00.003 回答
2

使用一个元类来查找所有属性,例如Get*并向Set*类添加适当的属性。有一个类属性,您可以将其设置为包含将被跳过的属性的序列。有关在类中设置属性的详细信息,请参阅此答案

于 2010-02-16T22:30:25.533 回答
1

小心使用魔法,尤其是用魔法改变其他人的绑定。这样做的缺点是

  • 您的代码与访问同一库的其他人不兼容。有人无法导入您的模块之一或复制和粘贴您的代码并使其与访问同一库的程序完美配合,并且
  • 您的界面不同于 C++ 代码的界面。如果您的包装器为您提供了更好、更高级别的界面,这将是有意义的,但您的更改只是微不足道的。

考虑一下处理您正在使用的库是否更有意义。

于 2010-02-16T23:42:14.823 回答
1
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
于 2013-10-07T15:26:13.860 回答