Функция для задания свойств объекта классовой композиции - PullRequest
0 голосов
/ 10 февраля 2019

Я хотел бы создать композицию класса, которая включает функцию set_props для установки переменных экземпляра компонентов.Приложение для этого в определении новых объектов для рисования в matplotlib.Одним из примеров является то, что я хотел бы иметь функцию drawMyArrow, которая рисует стрелку, возможно, разных цветов (и других спецификаций) для ее головы, хвоста и дуги.Я хотел бы иметь возможность передавать различные спецификации для головы, хвоста и дуги через аргументы ключевых слов в drawMyArrow.Раньше я не работал с классами, но, читая об этом в Интернете, я считаю, что лучший способ решить мою проблему - определить класс MyArrow, который представляет собой композицию некоторых классов ArrowHead и ArrowArc.

Чтобы проиллюстрировать мою проблему, я использую игрушечный пример (который я также использовал для предыдущего вопроса здесь ).Давайте определим класс Room, который является композицией классов wall, window и door.

class Door:
    def __init__(self, color='white', height=2.3, width=1.0, locked=True):
        self.color = color
        self.height = height
        self.width = width
        self.locked=locked

class Window:
    def __init__(self, color='white', height=1.0, width=0.8):
        self.color = color
        self.height = height
        self.width = width

class Wall:
    def __init__(self, color='white', height=2.5, width=4.0):
        self.color = color
        self.height = height
        self.width = width

class Room:
    def __init__(self):
        self.door = Door()
        self.window = Window()
        self.wall = Wall()

Если я хочу иметь функцию для Room, которая может устанавливатьсвойства его компонентов, я могу сделать что-то вроде этого:

def set_windowprops(r, color=None, width=None, height=None): 
    if not color==None: r.window.color=color
    if not width==None: r.window.widht=width
    if not height==None: r.window.height=height
    return r

Но если я решу добавить дополнительные переменные экземпляра в Window, мне придется вернуться к этой функции и добавить новые переменные экземпляра,Могу ли я написать set_windowprops, чтобы он автоматически принимал все переменные экземпляра Window в качестве ключевых слов?

В идеале я хотел бы написать такую ​​функцию:

def set_props(r, windowcolor=None, windowwidth=None, windowheight=None,
              doorcolor=None, doorwidth=None, doorheight=None, doorlocked=None,
              wallcolor=None, wallwidth=None, wallheight=None): 
    if not windowcolor==None: r.window.color=windowcolor
    if not windowwidth==None: r.window.widht=windowwidth
    if not windowheight==None: r.window.height=windowheight
    if not doorcolor==None: r.door.color=doorcolor
    if not doorwidth==None: r.door.widht=doorwidth
    if not doorheight==None: r.door.height=dooorheight
    if not doorlocked==None: r.door.locked=doorlocked
    if not wallcolor==None: r.wall.color=wallcolor
    if not wallwidth==None: r.wall.widht=wallwidth
    if not wallheight==None: r.wall.height=wallheight
    return r

, но безнеобходимость жесткого кодирования всех переменных экземпляра компонентов в функцию.

Я искал словари ключевых слов, например, так:

window_vars = getNamesOfInstanceVariables(Window) #TODO
window_kwargs = {}
for v in window_vars:
    window_kwargs[v] = None

def set_windowprops(r, **kwargs):
    for kw in kwargs:
        if not kwargs[kw]==None:
            r["window"][kw] = kwargs[kw] #TODO
    return r

Две проблемы не позволяют мне заставить это работать:

(1) В последней строке я притворяюсь, что r является словарем (словарей), и использую синтаксис словаря для присвоения значения r.window.kw.Но это не работает, потому что r является экземпляром Room, а не словарем.Какой будет синтаксис для установки переменных экземпляра, если имя класса компонента и имя переменной экземпляра заданы в виде строк?

(2) Я пытался использовать inspect для записи getNamesOfInstanceVariables,но я не могу заставить его работать надежно.Многие классы в matplotlib наследуются от базовых классов.Я бы хотел, чтобы getNamesOfInstanceVariables вернул все переменные экземпляра, которые пользователь может установить для объекта этого класса.Например, класс FancyArrow в matplotlib имеет Patch в качестве базового класса и переменных экземпляра head_length и head_width.Поэтому я бы getNamesOfInstanceVariables(FancyArrow) вернул ['head_length','head_width', *listOfInstanceVarsForPatch].

РЕДАКТИРОВАТЬ

Позвольте мне добавить немного фона, почему я прошу динамический способ написания этихфункции.Допустим, я закончил свой сценарий, и он включает в себя классы Window, Door, Wall и множество композиций классов этих трех.Одна из этих классных композиций - Room.Этот класс Room имеет десять жестко закодированных set_ функций, которые выглядят следующим образом:

def set_windowcolor(r, color):
    r.window.color = color
    return r

Теперь я решаю, что хочу добавить еще одну переменную экземпляра в класс Window.Например,

class Window:
    def __init__(self, color='white', height=1.0, width=0.8, open=False):
        self.color = color
        self.height = height
        self.width = width
        self.open = open # new attribute of Window

Подобно всем другим переменным экземпляра окна, этот новый атрибут Window должен быть настраиваемым во всех классовых композициях, которые содержат Window.Так что я бы просмотрел свой код, нашел класс Room и добавил бы функцию

def set_windowopen(r, open):
    r.window.open = open
    return r

. Мне также пришлось бы искать все другие композиции классов, содержащие Window, и обновлять их вручную.,Я не хочу этого делать, потому что это большая работа, и я, вероятно, собираюсь пропустить некоторые классовые зависимости в процессе и внести ошибки в мой код.Я ищу решение, которое либо

  • генерирует set функций для отдельных свойств (например, set_windowcolor) автоматически в Room для всех переменных экземпляра Window или

  • автоматически корректирует список аргументов ключевых слов в set_windowprops или set_props.

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Вот что я бы сделал

class Room:
    def __init__(self, kw_door=None, kw_window=None, kw_wall=None):
        if kw_door:
            self.door = Door(**kw_door)
        else:
            self.door = Door()
        if kw_window:
            self.window = Window(**kw_window)
        else:
            self.window = Window()
        if kw_wall:
            self.wall = Wall(**kw_wall)
        else:
            self.wall = Wall()

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

0 голосов
/ 10 февраля 2019

Чтобы ответить на первый вопрос.Если вы хотите установить атрибут window атрибута r и kw этого окна, как в вашем примере, вы можете сделать следующее:

setattr(getattr(r, "window"), kw, kwargs[kw])

Вы получите атрибут с именем window изr.И для этого windows атрибуты устанавливают свое собственное имя, имя которого находится в переменной kw, в новое значение из kwargs[kw].Как и пыталась ваша строка.

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

Что касается вас, второй вопрос.Вы также должны иметь возможность просто использовать dir(), чтобы получить список атрибутов экземпляра, но да, унаследованные атрибуты будут мешать вам, и я не сразу уверен, существует ли элегантный способ обойти это (то есть не обходить дерево наследования).и сделайте обрезку самостоятельно).Но если вы действительно хотите динамически обходить атрибуты экземпляра, которые, например, устанавливаются через функцию, я бы сказал, добавить атрибут (class), определяющий, какими они должны быть для каждого типа, полагаться на него как определенный интерфейс.


примечание: есть только один экземпляр NoneTrue и False), и он может быть проверен на идентичность (не только равенство), и так как он читается немного лучшевы бы обычно видели if somevar is not None вместо if not somevar == None.

...