Автоматическое преобразование из getter / setter в свойства - PullRequest
3 голосов
/ 17 февраля 2010

У меня есть большая библиотека, написанная на C ++, и кто-то создал интерфейс для автоматического использования ее в python (2.6). Сейчас у меня много классов с методами получения и установки. На самом деле: я ненавижу их.

Я хочу заново реализовать классы с более питоническим интерфейсом, используя свойства. Проблема в том, что в каждом классе есть сотни получателей и установщиков, а у меня много классов. Как я могу автоматически создавать свойства?

Например, если у меня есть класс с именем MyClass с методами GetX() и SetX(x), GetY, SetY и т. Д., Как я могу автоматически создать производный класс MyPythonicClass со свойством X (читаемое, если есть геттер, и доступное для записи, если есть установщик), и так далее? Мне бы хотелось, чтобы механизм, позволяющий мне пропустить некоторые пары получателей / установщиков, где лучше выполнять работу вручную.

Ответы [ 4 ]

7 голосов
/ 17 февраля 2010

Вот способ сделать это с помощью декоратора класса

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
2 голосов
/ 17 февраля 2010

Используйте метакласс , который ищет все атрибуты, такие как Get* и Set*, и добавляет соответствующие свойства в класс. Имейте атрибут класса, который вы можете установить для последовательности, содержащей свойства, которые будут пропущены. См. этот ответ для получения подробной информации об установке атрибутов в классе.

1 голос
/ 07 октября 2013
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
1 голос
/ 17 февраля 2010

Будьте осторожны, используя магию, особенно магически изменяя привязки других людей. Это имеет недостатки, которые

  • Ваш код несовместим с другими людьми, имеющими доступ к той же библиотеке. Кто-то не может импортировать один из ваших модулей или скопировать и вставить ваш код, и он прекрасно работает, когда его программа обращается к той же библиотеке, и
  • Ваш интерфейс отличается от интерфейса для кода C ++. Это имело бы смысл, если бы ваша оболочка дала вам более качественный, высокоуровневый интерфейс, но ваши изменения только тривиальны.

Подумайте, не имеет ли больше смысла иметь дело с библиотекой, которую вы используете, так как она пришла к вам.

...