Почему в списке нет безопасного метода get, такого как словарь?
>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'
>>> l = [1]
>>> l[10]
IndexError: list index out of range
Почему в списке нет безопасного метода get, такого как словарь?
>>> d = {'a':'b'}
>>> d['a']
'b'
>>> d['c']
KeyError: 'c'
>>> d.get('c', 'fail')
'fail'
>>> l = [1]
>>> l[10]
IndexError: list index out of range
В конечном счете он, вероятно, не имеет безопасного метода .get
, потому что dict
является ассоциативной коллекцией (значения связаны с именами), где неэффективно проверять наличие ключа (и вернуть его значение) без бросая исключение, в то время как супер-тривиально избегать исключений, обращающихся к элементам списка (поскольку метод len
очень быстрый). Метод .get
позволяет вам запрашивать значение, связанное с именем, а не напрямую обращаться к 37-му элементу в словаре (что больше похоже на то, что вы запрашиваете в своем списке).
Конечно, вы можете легко реализовать это самостоятельно:
def safe_list_get (l, idx, default):
try:
return l[idx]
except IndexError:
return default
Вы можете даже обезвредить его в конструкторе __builtins__.list
в __main__
, но это будет менее распространенное изменение, поскольку большинство кода его не использует. Если вы просто хотели использовать это со списками, созданными вашим собственным кодом, вы могли бы просто подклассом list
и добавить метод get
.
Это работает, если вы хотите, чтобы первый элемент, например my_list.get(0)
>>> my_list = [1,2,3]
>>> next(iter(my_list), 'fail')
1
>>> my_list = []
>>> next(iter(my_list), 'fail')
'fail'
Я знаю, что это не то, что вы просили, но может помочь другим.
Вместо использования .get, использование этого метода должно быть в порядке для списков. Просто разница в использовании.
>>> l = [1]
>>> l[10] if 10 < len(l) else 'fail'
'fail'
Возможно, потому, что это просто не имело смысла для семантики списка. Однако вы можете легко создать свой собственный подклассом.
class safelist(list):
def get(self, index, default=None):
try:
return self.__getitem__(index)
except IndexError:
return default
def _test():
l = safelist(range(10))
print l.get(20, "oops")
if __name__ == "__main__":
_test()
Попробуйте следующее:
>>> i = 3
>>> a = [1, 2, 3, 4]
>>> next(iter(a[i:]), 'fail')
4
>>> next(iter(a[i + 1:]), 'fail')
'fail'
Лучшее, что вы можете сделать, это преобразовать список в dict и затем получить доступ к нему с помощью метода get:
>>> my_list = ['a', 'b', 'c', 'd', 'e']
>>> my_dict = dict(enumerate(my_list))
>>> print my_dict
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> my_dict.get(2)
'c'
>>> my_dict.get(10, 'N/A')
Кредиты jose.angel.jimenez
Для поклонников "oneliner"...
Если вам нужен первый элемент списка или если вы хотите значение по умолчанию, если список пуст, попробуйте:
liste = ['a', 'b', 'c']
value = (liste[0:1] or ('default',))[0]
print(value)
возвращает a
и
liste = []
value = (liste[0:1] or ('default',))[0]
print(value)
возвращает default
Примеры для других элементов...
liste = ['a', 'b', 'c']
print(liste[0:1]) # returns ['a']
print(liste[1:2]) # returns ['b']
print(liste[2:3]) # returns ['c']
С отключением по умолчанию...
liste = ['a', 'b', 'c']
print((liste[0:1] or ('default',))[0]) # returns a
print((liste[1:2] or ('default',))[0]) # returns b
print((liste[2:3] or ('default',))[0]) # returns c
Протестировано с помощью Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
Итак, я сделал еще несколько исследований в этом, и, оказывается, нет ничего конкретного для этого. Я взволновался, когда нашел list.index(значение), он возвращает индекс указанного элемента, но нет ничего для получения значения по определенному индексу. Поэтому, если вы не хотите использовать решение safe_list_get, которое, я думаю, довольно хорошо. Вот некоторые 1 лайнер, если операторы, которые могут выполнить задание для вас, в зависимости от сценария:
>>> x = [1, 2, 3]
>>> el = x[4] if len(x) == 4 else 'No'
>>> el
'No'
Вы также можете использовать None вместо "No", что имеет больше смысла.:
>>> x = [1, 2, 3]
>>> i = 2
>>> el_i = x[i] if len(x) == i+1 else None
Также, если вы хотите просто получить первый или последний элемент в списке, это работает
end_el = x[-1] if x else None
Вы также можете включить их в функции, но мне все еще понравилось решение для исключения IndexError. Я экспериментировал с уменьшенной версией решения safe_list_get
и сделал его немного проще (без дефолта):
def list_get(l, i):
try:
return l[i]
except IndexError:
return None
Не проверяйте, что быстрее.
Словари предназначены для поиска. Имеет смысл спросить, существует ли запись или нет. Списки обычно повторяются. Не часто возникает вопрос, существует ли L [10], но если длина L равна 11.
Однострочные вентиляторы могут использовать %
если нужен круговой характер:
value = l[index] if abs(index)<len(l) else l[index%len(l)]
Лучший способ убедиться, что вы не получили ошибку:
for i in list:
do_stuff(i)
если список пуст, do_stuff не будет выполнен.
Ваш usecase, в основном, имеет значение только при создании массивов и матриц фиксированной длины, чтобы вы знали, как долго они находятся перед рукой. В этом случае вы обычно также создаете их перед тем, как вручную заполнить их None или 0, так что фактически любой индекс, который вы будете использовать, уже существует.
Вы могли бы сказать следующее: мне очень часто нужно .get() в словарях. После десяти лет работы программистом на полный рабочий день, я не думаю, что мне когда-либо понадобилось это в списке.:)