Вызовите метод для всех объектов определенного типа - PullRequest
0 голосов
/ 27 апреля 2018

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

В настоящее время я делаю это так:

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

Есть ли лучший способ?

Редактировать: Для выяснения проблемы

Я постараюсь проиллюстрировать проблему простым способом, и я надеюсь, что я не завалю вас подробностями.

У меня есть приложение, которое взаимодействует со встроенными станциями, подключенными к Ethernet, в моей сети. Каждая из этих станций представлена ​​отдельным классом, и каждый класс предоставляет различные функции для управления этой станцией.

Во-первых, необходимо выполнить сканирование, чтобы я знал, какие станции фактически доступны в сети. Поэтому создается базовый объект «Станция» и вызывается «сканирование». Сканирование - это запрос, который ожидает ответа от станции. Если ответ есть, в нем будет указан точный тип подключенной станции.

Во-вторых, базовая станция должна быть расширена на «режим», который был только что обнаружен этим первым шагом сканирования. «Базовый» режим заменяется конкретным режимом, в котором находится станция. «Базовый» режим позволяет только что выполнить это сканирование.

Это фактический класс базовой станции. __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

Это пример класса «mode», такого как «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

Я сделал так называемые «Интерфейсы», которые присоединяются к объектам «Режима» и предлагают функции для управления станцией определенным образом. Я могу подключить несколько интерфейсов к одной станции, поэтому она получает функции всех из них.

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

Извините, если вам не хватает информации или ее слишком много.

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

ИМХО ваш тест на знание того, принадлежит ли объект к классу, слишком ограничен и неправильно обрабатывает подтипы. Например, я добавляю это в ваш код:

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)
0 голосов
/ 27 апреля 2018

Одним из решений будет реализация всех возможных операций (как безоперационных) в базовом классе Mode, чтобы вы могли просто вызывать любую операцию, не беспокоясь о текущем режиме объекта.

Другое решение заключается в использовании getattr() и имени метода:

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

FWIW, то, что вы имеете здесь с вашими Mode классами, на самом деле является неполной моделью "State". Вы можете прочитать об этом ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...