设置 class 组合对象属性的函数

Function to set properties of an object of a class composition

我想构造一个 class 组合,其中包含一个函数 set_props 用于设置组件的实例变量。 其应用是在 matplotlib 中定义用于绘图的新对象。一个例子是我想要一个函数 drawMyArrow 来绘制一个箭头,其头部、尾部和弧线可能具有不同的颜色(和其他规格)。我希望能够通过 drawMyArrow 中的关键字参数传递头部、尾部和圆弧的各种规范。我以前没有使用过 classes,但是在网上阅读这个,我相信解决我的问题的最好方法是定义一个 class MyArrow 的组合一些 classes ArrowHeadArrowArc.

为了说明我的问题,我使用了一个玩具示例(我也将其用于上一个问题 )。让我们定义一个 class Room,它是 classes wallwindowdoor.

的组合
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 赋值。但这不起作用,因为 rRoom 的实例而不是字典。如果组件名称 class 和实例变量名称以字符串形式给出,设置实例变量的语法是什么?

(2) 我曾尝试使用 inspect 编写 getNamesOfInstanceVariables,但我无法使其正常工作。 matplotlib 中的许多 classes 继承自基础 classes。我想要 getNamesOfInstanceVariables 到 return 所有 用户可以为此 class 的对象设置的实例变量。例如,matplotlib 中的 class FancyArrowPatch 作为基础 class 和实例变量 head_lengthhead_width。所以我会 getNamesOfInstanceVariables(FancyArrow) 到 return ['head_length','head_width', *listOfInstanceVarsForPatch].

编辑

让我补充一些背景知识,说明为什么我要求以动态方式编写这些函数。假设我已经完成了我的脚本,它包括 classes WindowDoorWall 和这三者的许多 class 组合。 class 的作品之一是 Room。这个 class Room 有十个硬编码的 set_ 函数,如下所示:

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

我现在决定要向 class 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 的这个新属性应该可以在包含 Window 的所有 classe 组合中自定义。所以我会检查我的代码,找到 class Room 并添加一个函数

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

我还必须寻找包含 Window 的所有其他 class 作品并手动更新它们。我不想这样做,因为它需要大量工作,而且我可能会忽略过程中的一些 class 依赖项并将错误引入我的代码中。我正在寻找一种解决方案

先回答你的问题。如果你想设置 rwindow 属性和那个 `windows 的 kw 属性,就像你的例子一样,你可以执行以下操作:

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

您获得 r 的名为 window 的属性。对于 windows 属性,将其名称在变量 kw 中的属性设置为来自 kwargs[kw] 的新值。正如您所尝试的那样。

但老实说,为什么具有许多可能参数的函数比 accessing/setting 属性本身更受欢迎?或者换句话说,从表面上看,它看起来比需要的更复杂。

关于你的第二个问题。您也应该能够只使用 dir() 来获取实例属性列表,但是是的,继承的属性会妨碍您,我不确定是否有一种优雅的方法(即不走继承树并自己进行修剪)。但是,如果您真的想动态遍历实例的属性,例如通过函数进行设置,我会说添加一个 (class) 属性,定义每种类型的属性依赖于它作为定义的接口。


旁注:None(以及 TrueFalse)只有一个实例,可以测试其身份(而不仅仅是相等性),因为它读取你通常会看到 if somevar is not None 而不是 if not somevar == None.

好一点

这是我会做的

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()

实际上,您正在接受一个将被解压到实例创建中的字典,并且当 class 定义获得新属性时,如果在传递的字典中找到它们,它们也会被解压。