Динамическое использование функций внутри __getitem__ метода

#python #reference #data-access

Вопрос:

Предположим, что у вас есть класс python MyObject , экземпляры которого содержат список данных и что MyObject класс как __getitem__ метод определен следующим образом:

 def __getitem__(self, index):
   return self.data[index]

 

Предположим, что мы хотим изменить приведенный выше __getitem__ метод на что-то вроде

 def __getitem__(self, index):
   return self.data[my_function(index)]
 

где my_function находится некоторая функция, которая преобразует альтернативную систему индексации (скажем, кортежи целых чисел в систему индексов списка — неотрицательные целые числа). Здесь my_function может быть self.map или OtherClass.map или что-то еще, что делает приведенный выше код допустимым кодом python. Мы хотим разрешить пользователю изменять эту функцию, в том числе с помощью функции идентификации (т. Е. Без преобразования). Это можно сделать достаточно легко.

Но вот что я хочу сделать. Предположим,что у меня есть куча экземпляров $p_1, p_2,.., p_k$ класса MyObject, использующего некоторую карту map_1 ,и у меня есть еще одна куча экземпляров $q_1, q_2,…, q_t$, использующих некоторую карту map_2 .

  1. Я хочу изменить карту,которую используют экземпляры $p_1, p_2,.., p_k$, с map_1 на map_2 . Я не хочу делать это,зацикливаясь на каждом экземпляре $p_1, p_2,.., p_k$ и устанавливая отображение индекса на map_2 , а только выполняя $O(1)$ работу (т. Е. Не $O(k)$ работу).
  2. Затем я хочу,чтобы все экземпляры $p_1, p_2,.., p_k,q_1,q_2,…,q_t$ использовали map_2 .
  3. Затем я хочу изменить эти экземпляры, чтобы использовать новую карту map_3 , где для изменения снова требуется столько же постоянного времени.

Я вижу, как это сделать на C/C , используя ссылки, но не на python. Пользователь предоставляет функции карты, и их может быть много. Лучшее, что я могу получить, это следующее:

 # A simple Map class to hold maps
class MapDict:
    def __init__(self):
       self.loaded_maps = dict()

    def make_map(self, fid):
        def index_funct(index):
            return self.loaded_maps[fid](index)
        return index_funct

# A simple class for data
class MyObject:
    def __init__(self,data):
        self.data = data
        self._map = self._identity_map

    def _identity_map(self, index):
        return index

    def set_map(self, input_map):
        self._map = input_map

    def __getitem__(self, index):
        return self.data[self._map(index)]

# Create some MyObjects with sample data
p1=MyObject(range(1000))
p2=MyObject(range(1000))
q1=MyObject(range(1000))
q2=MyObject(range(1000))

#Create a class to hold the maps
map_dict = MapDict()

# Define a few map functions to use in indexing
def map1(index):
    return index[0] index[1]

def map2(index):
    return index[0]*index[1]

def map3(index):
    return index[0] 10*index[1]

# Load the maps into the map dict class
map_dict.loaded_maps[1] = map1
map_dict.loaded_maps[2] = map2

# Set the maps for the MyObjects
p1.set_map(map_dict.make_map(1))
p2.set_map(map_dict.make_map(1))
q1.set_map(map_dict.make_map(2))
q2.set_map(map_dict.make_map(2))

# Now change maps for p1, p2
map_dict.loaded_maps[1] = map2

# Now change maps for p1 p2 q1 q2
map_dict.loaded_maps[1] = map3
map_dict.loaded_maps[2] = map3

 

Проблема с этим подходом заключается в том, что теперь у меня есть два экземпляра map3 в словаре, а не в одном месте. Поэтому, если я хочу перейти map3 на map4 использование для всех экземпляров map3 , я должен изменить два местоположения. Предположение, что я мог бы предоставить еще один список/словарь в дополнение к этому, чтобы отслеживать, но это кажется излишним. Кроме того, я хочу убедиться, что _getitem__ это остается очень быстрым (относительно выбранной функции карты), так как я ожидаю, что она будет часто использоваться.

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

Ответ №1:

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

 class MappedList:
    
    mappers = {}
    
    def __init__(self, data, group, mapper=None):
        self.data = data
        self.group = group
        # add mapping function to dict if creating new group
        if self.group not in type(self).mappers.keys():
            type(self).mappers[self.group] = mapper
        
    def __getitem__(self, item):
        return self.data[type(self).mappers[self.group](item)]
        
    @classmethod
    def edit_group_mapper(cls, group, mapper):
        # reset groups getter function
        cls.mappers[group] = mapper
 

Функции для переназначения индекса в данные:

 def map1(item):
    return item   1

def map2(item):
    # should catch if remapped item not in data here
    return item   2
 

Создание нескольких групп с помощью этого:

 p1 = MappedList(['a', 'b', 'c'], group=1, mapper=map1)
p2 = MappedList(['d', 'e', 'f'], group=1)
m1 = MappedList(['g', 'h', 'i'], group=2, mapper=map2)
m2 = MappedList(['j', 'k', 'l'], group=2)
 

Тестирование первой функции сопоставления и тестирование измененной функции сопоставления:

 print(p1[0], p2[0])

MappedList.edit_group_map(group=1, mapper=map2)

print(p1[0], p2[0])
 

ВОЗВРАТ:

 b e
c f