2010-02-16 4 views
3

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

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

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

ответ

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 
2

Используйте metaclass, который ищет все атрибуты, такие как Get* и Set*, и добавляет соответствующие свойства классу. Имейте атрибут класса, который вы можете установить в последовательность, содержащую свойства, которые будут пропущены. Подробнее об установке атрибутов в классе см. В разделе this answer.

+0

Я не очень эксперт, вы можете дать мне пример для моего случая? –

+0

Для этого вам не нужно использовать метакласс. Вероятно, более понятным будет код для определения '__getattr__' и' __setattr__'. Вы можете привести их в свой класс через наследование. –

1

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

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

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

+0

Вы правы, но это не очень большая проблема, я не разрабатываю для других людей, я создаю этот интерфейс, потому что мне не нравится интерфейс getter/setter (также на C++). Мой интерфейс расширяет старый интерфейс, это не подстановка, методы getter/setter все еще существуют. –

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 
+0

Можете ли вы добавить некоторое объяснение о том, как работает этот код? – fvrghl

Смежные вопросы