2015-11-28 2 views
3

Скажите, что у вас есть словарь, чьи ключи целые. Значения также являются словарями, ключи которых являются строками и значениями которых являются массивы numpy. Что-то вроде:Питон-эквивалент C++ begin() и end() для пользовательских классов

custom = {1: {'a': np.zeros(10), 'b': np.zeros(100)}, 2:{'c': np.zeros(20), 'd': np.zeros(200)}} 

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

for d, delem in custom.items(): 
    for k, v in delem.items(): 
     for row in v: 
      print(row) 

можно ли инкапсулировать это поведение в функции а-ля C++, где вы можете реализовать пользовательские begin() и end()? Кроме того, итератор должен также иметь информацию о ключах в соответствующих словарях. Я предвижу, что-то вроде:

for it in custom: 
    d, e, row = *it 
    # then do something with these 

ответ

1
import numpy as np 

custom = { 
    1: {'a': np.zeros(10), 'b': np.zeros(100)}, 
    2:{'c': np.zeros(20), 'd': np.zeros(200)} 
} 

my_gen = (
    (key, subkey, np_array) 
    for (key, a_dict) in custom.items() 
    for subkey, np_array in a_dict.items() 
) 

for key, subkey, np_array in my_gen: 
    print(key, subkey, np_array) 

--output:-- 
1 b [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] 
1 a [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.] 
2 d [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0.] 
2 c [ 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 
    0. 0.] 

Или вы могли бы восстановить структуру данных в нечто, что является более полезным для ваших целей:

import numpy as np 

custom = { 
    1: {'a': np.zeros(10), 'b': np.zeros(100)}, 
    2:{'c': np.zeros(20), 'd': np.zeros(200)} 
} 

#Create a *list* of tuples: 
converted_data = [ 
    (np_array, subkey, key) 
    for (key, a_dict) in custom.items() 
    for subkey, np_array in a_dict.items() 
] 

for np_array, subkey, key in converted_data: 
    print(key, subkey, np_array) 

Создание пользовательского итератора:

class Dog: 
    def __init__(self, data): 
     self.data = data 
     self.max = len(data) 
     self.index_pointer = 0 

    def __next__(self): 
     index = self.index_pointer 

     if index < self.max: 
      current_val = self.data[index] 
      self.index_pointer += 1 
      return current_val 
     else: 
      raise StopIteration 


class MyIter: 
    def __iter__(self): 
     return Dog([1, 2, 3]) 


for i in MyIter(): 
    print(i) 

--output:-- 
1 
2 
3 

__iter__() просто необходимо для возврата объекта, который реализует метод __next__(), поэтому вы можете комбинировать новления два класса, как это:

class MyIter: 
    def __init__(self, data): 
     self.data = data 
     self.max = len(data) 
     self.index_pointer = 0 

    def __iter__(self): 
     return self #I have a __next__() method, so let's return me! 

    def __next__(self): 
     index = self.index_pointer 

     if index < self.max: 
      current_val = self.data[index] 
      self.index_pointer += 1 
      return current_val 
     else: 
      raise StopIteration 

for i in MyIter([1, 2, 3]): 
    print(i) 

--output:-- 
1 
2 
3 

Более сложный __next__() метод:

import numpy as np 

class CustomIter: 
    def __init__(self, data): 
     self.data = data 
     self.count = 0 


    def __iter__(self): 
     return self 

    def __next__(self): 
     count = self.count 
     self.count += 1 

     if count == 0: #On first iteration, retun a sum of the keys 
      return sum(self.data.keys()) 

     elif count == 1: #On second iteration, return the subkeys in tuples 
      subkeys = [ 
       a_dict.keys() 
       for a_dict in self.data.values() 
      ] 

      return subkeys 

     elif count == 2: #On third iteration, return the count of np arrays 
      np_arrays = [ 
       np_array 
       for a_dict in self.data.values() 
       for np_array in a_dict.values() 
      ] 

      return len(np_arrays) 

     else: #Quit after three iterations 
      raise StopIteration 


custom = { 
    1: {'a': np.zeros(10), 'b': np.zeros(100)}, 
    2:{'c': np.zeros(20), 'd': np.zeros(200)} 
} 

for i in CustomIter(custom): 
    print(i) 


--output:-- 
3 
[dict_keys(['b', 'a']), dict_keys(['d', 'c'])] 
4 
+0

Спасибо, что ответили. В чем разница между наличием 'my_gen' в вашем решении и' custom_dict_iter' в решении @ tdelaney? – aaragon

+0

@aarogon, вы можете напрямую определять генераторы - без определения функции. Тем не менее, вы не можете перемотать генератор, поэтому, если вы хотите получить еще один генератор позже, я бы пошел с решением tdelaney – 7stud

+0

Doest, что означает, что ваше решение работает только один раз? – aaragon

2

просмотра iterator protocol - это больше похоже на в Java Iterable или С # IEnumerable, чем C++ начать/конец s. Вы можете определить его легче, указав метод __iter__ как generator.

Единственное, что вам нужно сделать, чтобы ваш custom имел свой класс с этими методами, а не простой словарь, но я предполагаю, что это также верно в C++.

+0

Ваше решение выглядит интересным. Поэтому, я думаю, вы имеете в виду, что я создаю собственный класс, который происходит из словаря, и что переопределяет метод '__iter__', правильно? – aaragon

+0

@aaragon Вы могли бы это сделать, я думаю. У меня, наверное, просто есть словарь как поле. – Random832

+0

@aaragon, вы не переопределяете: вам просто нужно определить как '__iter __()', так и '__next __()' в вашем пользовательском классе. '__iter __()' имеет только одну строку: 'return self'. Внутри '__next __()' находится где все действие происходит.И, внутри '__next __()', вам нужно «поднять StopIteration», когда вы решите, что ваш итератор попал в конец данных. Цикл for-in, применяемый к экземпляру вашего пользовательского класса, автоматически захватывает исключение StopIteration и завершается. Так заканчиваются все контуры in-in. – 7stud

4

Существует несколько способов сделать это. yield может быть самым простым, так как он тяжелый подъем для создания класса интернатора для вас.

def custom_dict_iter(custom): 
    for d, delem in custom.items(): 
     for k, v in delem.items(): 
      for row in v: 
       yield d, k, row 

for d, k, row in custom_dict_iter(my_custom_dict): 
    print(d, k, row) 
+0

Что вы имеете в виду, что вы будете давать d, k, row? Все еще не слишком многословно? Это намного лучше, чем у меня. – aaragon

+0

@aaragon Вы правы. Я был зафиксирован на 'print (row)'. Исправлена. – tdelaney

+0

@aaragon Я не считаю его слишком подробным, потому что вы хотите отслеживать и возвращать ключи, а также значения. И сравните его с перегрузкой итератора C++, это довольно хорошо. – tdelaney

0

Как более вещий образом, вы можете использовать вложенный список понимание, которое выполняет на скорости языка C внутри переводчика:

>>> [[(i,key,t) for t in value] for i,j in custom.items() for key,value in j.items()] 

И если вы хотите получить итератор, вы можете использовать выражение генератора вместо понимания списка.

>>> ([(i,key,t) for t in value] for i,j in custom.items() for key,value in j.items()) 
+0

Я думаю, что это близко к тому, что сделал @ 7stud, за исключением того, что вы построили его во внешний список. Мое решение вырывает петли ... и теперь мне интересно, медленнее ли это. – tdelaney

Смежные вопросы