从getter / setter到属性的自动转换

时间:2010-02-16 22:18:48

标签: python properties metaprogramming automatic-properties

我有一个用C ++编写的大型库,有人创建了一个以自动方式在python(2.6)中使用它的接口。现在我有很多带有getter和setter方法的类。真的:我恨他们。

我想使用属性重新实现具有更多pythonic接口的类。问题是每个班级都有数百个getter和setter,而且我有很多课程。如何自动创建属性?

例如,如果我有一个名为MyClass的课程,其中包含GetX()SetX(x)GetYSetY等方法,我可以使用属性MyPythonicClass自动创建派生类X(如果有getter则可读,如果有setter则可写),依此类推?我想要一种机制,让我可以选择跳过一些getter / setter夫妇,最好是手工完成工作。

4 个答案:

答案 0 :(得分:7)

这是使用类装饰器

的方法
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

答案 1 :(得分:2)

使用metaclass查找Get*Set*等所有属性,并为该类添加适当的属性。有一个class属性,您可以将其设置为包含将被跳过的属性的序列。有关在班级中设置属性的详细信息,请参阅this answer

答案 2 :(得分:1)

小心使用魔法,特别是神奇地改变其他人的绑定。这有缺点

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

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

答案 3 :(得分: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