在 运行 时间内重新定义可调用对象

Redefining a callable object during Run Time

我使用 wx 库构建 GUI。我已经初始化了一个面板并初始化了一些按钮,并绑定了一个在按下时执行的函数。该函数接受一个参数列表,其中一个是回调函数。我想做的是在运行时重新定义回调函数,但我没有这样做。

我目前的尝试是:

    self.UpdateCurrent = None
    self.GetCurrent = None
    self.UpdateCellVoltage = None
    self.GetCellVoltage = None
    self.UpdateCellTemperature = None
    self.GetCellTemperature = None
    self.battery_control_d = OrderedDict([('Current',[self.UpdateCurrent, self.GetCurrent, None, 1]),
                                          ('Cell Voltage',[self.UpdateCellVoltage, self.GetCellVoltage, 0, 24]),
                                          ('Cell Temperature',[self.UpdateCellTemperature, self.GetCellTemperature, 0, 24])])
    .
    .
    .
    submit_btn_l[-1].Bind(wx.EVT_BUTTON,
                          lambda evt,
                          io_name = io_name,
                          callback_fn = self.battery_control_d[io_name][0],
                          ctrl_textbox = ctrl_textbox,
                          dim_combobox = self.dim_combobox_l[-1]:
                          self._send_control_value(evt,
                                                   io_name,
                                                   callback_fn,
                                                   ctrl_textbox,
                                                   dim_combobox))
    .
    .
    .
    def init_battery_intf(self):
        self.battery_intf = self.simulator_main_panel.battery_intf

        self.UpdateCurrent = self.battery_intf.UpdateCurrent
        self.GetCurrent = self.battery_intf.GetCurrent

        self.UpdateCellVoltage = self.battery_intf.UpdateCellVoltage
        self.GetCellVoltage = self.battery_intf.GetCellVoltage

        self.UpdateCellTemperature = self.battery_intf.UpdateCellTemperature
        self.GetCellTemperature = self.battery_intf.GetCellTemperature
    .
    .
    .
    def _send_control_value(self,
                            evt,
                            io_name,
                            callback_fn,
                            ctrl_textbox,
                            dim_combobox):
        io_value = float(ctrl_textbox.Value)
        if ("Temperature" in io_name):
            io_value -= self.simulator_main_gui.temperature_unit_converter
        callback_fn(io_value, int(dim_combobox.Value))
    def update( self,
                evt ):

        for io_name, io_info in self.battery_control_d.iteritems():
            io_value = float(io_info[1](io_info[2]))

            self.reading_text_l[self.io_indexer.index(io_name)].SetLabel(" %.4f " % (io_value))

我预定义了一些 update/get 个对象。

我将功能绑定到按钮

在运行时我调用 init_battery_intf 来初始化这些对象

出错的行是在我尝试调用回调函数时。好像还是要设置成None.

Traceback (most recent call last):
  File "C:\Users\Sam\Desktop\work\simulator\src\simulate.py", line 1185, in updat
    self.control_notebook.update(evt)
  File "C:\Users\Sam\Desktop\work\simulator\src\simulate.py", line 869, in update
    self.battery_control_panel.update(evt)
  File "C:\Users\Sam\Desktop\work\simulator\src\simulate.py", line 591, in update
    io_value = float(io_info[1](io_info[2]))
TypeError: 'NoneType' object is not callable

我知道我可以重新定义绑定并直接输入值,但我想保持我的代码简洁明了,我觉得 Python 有办法将重新定义的回调函数分发给所有人对象的实例。

我正在使用 Python 2.7.

您有几个选择:

A: self.UpdateCurrentself.battery_control_d 使用的其他方法已经初始化。 (从不初始化为 None)

B: 当self.UpdateCurrent and/or 其他更新时 recreate/update self.battery_control_d.

C:使用名称引用并在从 self.battery_control_d 访问时获取该属性的值。


前两个不言自明,但您似乎对选项 C 感兴趣,所以我将对其进行扩展:

您可以创建一个包含多个 property 的 class,在访问时从您的原始对象中检索属性。

class VarReference(object):
    def __init__(self, inst, update_name, get_name, value1, value2):
        self.inst = inst
        self.update_attr_name = update_name
        self.getter_attr_name = get_name
        self.value1 = value1
        self.value2 = value2

    @property
    def get(self):
        return getattr(self.inst, self.getter_attr_name)

    @property
    def update(self):
        return getattr(self.inst, self.update_attr_name)


class Test(object):pass #dummy class for my demo
self = Test()

self.UpdateCurrent = self.GetCurrent = None

ref_obj = VarReference(self,"UpdateCurrent","GetCurrent",None,1)

>>> print(ref_obj.get)
None
>>> self.GetCurrent = lambda:5
>>> ref_obj.get
<function <lambda> at 0x1057d5488>
>>> ref_obj.get()
5

如果您不熟悉 property 基本上当在实例上访问属性名称时调用它装饰的函数(因此检索原始实例的属性)

所以在这种情况下,您可以像这样为 self.battery_control_d 编写初始化:

self.battery_control_d = OrderedDict([('Current',VarReference(self, "UpdateCurrent", "GetCurrent", None, 1)),
                                      ('Cell Voltage',VarReference(self, "UpdateCellVoltage", "GetCellVoltage", 0, 24)),
                                      ('Cell Temperature',VarReference(self, "UpdateCellTemperature", "GetCellTemperature", 0, 24))
                                    ])

那么 self.battery_control_d["current"].get 将是 getattr(self,"GetCurrent") 的结果,动态地等同于 self.GetCurrent

如果你真的想使用 VALUE[0]VALUE[1] 而不是 VALUE.updateVALUE.get 那么你可以覆盖 [=52 的 __getitem__ =] 同样,尽管我强烈建议切换到更详细的解决方案:

class VarReference(object):
    ...   
    def __getitem__(self,item):
        if item==0:
            return self.update
        elif item == 1:
            return self.get
        elif item ==2:
            return self.value1
        elif item == 3:
            return self.value2
        else:
            raise IndexError("Can only get indices from 0 to 3")