【问题标题】:Call a method for all objects of a certain type为特定类型的所有对象调用方法
【发布时间】:2018-10-08 08:44:18
【问题描述】:

我有一个从不同类实例化的对象列表。我想对该列表中某个类的所有对象调用某个方法。我觉得我找到的方式不是很优雅,可能有更好的方式。

目前,我是这样做的:

def execute(fn, classname, objlist):
    '''
    The function that iterates over all objects, 
    finds the fitting ones, and executes the chosen "fn"
    '''
    for obj in objlist:
        # Checks for the class from which the object was instantiated
        if type(obj) is classname:
            # Execute the function.
            fn(obj)

class C():
    def myfn(self):
        print("foo")

class D():
    def otherfn(self):
        print("bar")

objlist = []

objlist.append(C())
objlist.append(D())

execute(C.myfn, C, objlist)
execute(D.otherfn, D, objlist)

输出是:

foo
bar

有没有更好的办法?

编辑:澄清问题

我会尽量用简单的方式来说明这个问题,希望我不会用细节来压倒你。

我有一个应用程序与我的网络上的以太网连接的嵌入式站接口。这些站点中的每一个都由不同的类表示,并且每个类都提供了不同的功能来控制该站点。

首先,必须执行扫描,以便我知道网络上实际可用的站点。因此,创建了一个基本的“Station”对象并调用了“scan”。扫描是一个请求,期望来自工作站的答复。如果有答案,它将指定连接站的确切类型。

其次,基本站需要通过第一个扫描步骤刚刚发现的“模式”进行扩展。 “基本”模式被工作站所处的特定模式取代。“基本”模式只允许刚刚执行的扫描。

这是实际的基本站类。 __LinkUI 包含一些关于输入到用户界面中的内容的信息,例如 IP 地址范围和端口:

class Station(__LinkUI):
    '''
    Generic class for creating a station. This is used first for
    stations that have no known type yet
    '''
    def __init__(self, ip, storage):
        super().__init__(storage)

        # IP Address of this station
        self.ip = ip

        # The handling object for the TCP connection to this station
        self.handler = Handler(self.storage.network.confport, self.pool)

        # Last response value to a request message
        self.response = None

        # mode of this station. This is an object that details 
        # some functions and variables for controlling a certain 
        # type of station. "Basic" is the default mode with 
        # no special functions
        self.mode = Basic(self)

        # Hardware architecture of a station, like DDS/PLL
        self.architecture = None

这是“模式”类的示例,例如“Boot”,它表示处于引导加载程序模式的工作站。这种“模式”扩展了“站”对象的功能。

class Boot(__Mode):
    '''
    Extends a station by the boot mode
    '''
    def __init__(self, station):
        self.name = "Boot"
        super().__init__(self, station)

        # The interface for using the flash mode of the boot station
        self.flashInterface = FlashInterface(station)
        self.healthInterface = HealthInterface(station)

    def flash(self, callback, data):
        self.flashInterface.flash(callback, data)

    def run(self, callback):
        self.flashInterface.run(callback)

__Mode 是连接一些变量并生成“Messenger”的基类,用于生成发送到站点的实际原始消息:

class __Mode():
    '''
    Base class for different station modes (Boot, Rx, Tx, Central)
    '''
    def __init__(self, child, station):
        # Generate a messenger object for this kind of station
        self.messenger = GetMessenger(child)

        # Connect variables
        self.station = station
        station.mode = child
        station.name = child.name
        station.messenger = child.messenger

我制作了所谓的“接口”,它附加到“模式”对象并提供以某种方式控制工作站的功能。我可以将多个接口连接到一个站点,因此它获得了所有接口的功能。

总之,站点可能非常不同,我不确定多态性是否适用。方法不会有相同的名称、功能或参数。

抱歉,如果这里缺少信息或提供的信息过多。

【问题讨论】:

  • 如果我理解你想要正确地做的事情,为什么不直接使用多态呢?
  • 这看起来确实像一个多态性问题,但是为了确保我们需要知道你的类实际上是什么。您可以编辑您的问题以显示您的实际课程而不是假人吗?
  • 这种方法对我来说似乎没有什么问题。它很简洁,可以做你想做的事。您可以使用map 函数和列表理解:map(fn, [obj for obj in objlist if type(obj) is classname]。这样做的问题是,您要从列表推导式构建一个列表,然后使用 map 对其进行迭代,因此您实际上是迭代了两次,而您的解决方案只循环了一次。
  • 我现在扩展了这个问题。您可能是对的,但我不想通过“类”以及“方法”。我宁愿只通过“Class.method”和“execute()”计算出“method”属于哪个类。

标签: python python-3.x iterator


【解决方案1】:

恕我直言,您对了解对象是否属于某个类的测试过于严格,无法正确处理子类型。例如,我将其添加到您的代码中:

class E(C):
   pass

objlist.append(E())

objlist 现在包含一个 C 实例、一个 D 实例和一个 E 实例。但 E 实例也是继承的 C,所以我希望输出:

execute(C.myfn, C, objlist)

成为:

foo
foo

所以我会重写你的函数:

def execute(fn, classname, objlist):
    '''
    The function that iterates over all objects, 
    finds the fitting ones, and executes the chosen "fn"
    '''
    for obj in objlist:
        # Checks for the class from which the object was instantiated
        if isinstance(obj, classname):
            # Execute the function.
            fn(obj)

【讨论】:

    【解决方案2】:

    一种解决方案是在Mode 基类中实现所有可能的操作(作为无操作),这样您就可以调用任何操作而不必担心对象的当前模式。

    另一种解决方案是使用getattr() 和方法名:

    def execute(objlist, methodname, *args, **kw):
        for obj in objlist:
            method = getattr(obj, methodname, None)
            if method:
                method(*args, **kw)
    

    FWIW,您的Mode 类实际上是一个不完整的“状态”模式。 You may want to read about it...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 2016-10-24
      相关资源
      最近更新 更多