Списки в Python

Введение

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

Синтаксис

  • [value1, value2, ...]
  • list([iterable])

Замечания

Список это конкретный тип итераций, но не единственный в Python. Иногда лучше использовать множество, кортеж, или словарь.

Списком называют динамические массивы в Python (схоже с  vector<void*> из C++ или Java  ArrayList<Object>). Это не связанный список.

Доступ к элементам осуществляется за константное время и очень быстро. Добавление элементов в конец списка происходит за амортизируемое постоянное время, но иногда включает выделение и копирование списка целиком. Списки включения связаны со списками.

Доступ к значениям списка

Списки Python имеют нулевую индексацию и действуют как массивы в других языках.

lst = [1, 2, 3, 4]
lst[0]

>>>Out: 1

lst[1]

>>>Out: 2

Попытка получить доступ к индексу за пределы списка возбудит IndexError .

lst[4]

>>>Out: IndexError: list index out of range

Отрицательные показатели интерпретируются как отсчет от конца списка.

lst[-1]

>>>Out: 4

lst[-2]

>>>Out: 3

lst[-5]

>>>Out: IndexError: list index out of range

Это функционально эквивалентно

lst[len(lst)-1]

>>>Out: 4

Списки позволяют использовать ломтик обозначение , как lst[start:end:step].Выход среза обозначений представляет собой новый список , содержащий элементы из индекса start до end-1.Если параметры опущены start значения по умолчанию для начала списка, end в конец списка и step 1:

lst[1:]

>>>Out: [2, 3, 4]

lst[:3]

>>>Out: [1, 2, 3]

lst[::2]

>>>Out: [1, 3]

lst[::-1]

>>>Out: [4, 3, 2, 1]

lst[-1:0:-1]

>>>Out: [4, 3, 2]

lst[5:8]     # так как стартовый индекс больше длины списка, то возвращается пустой список

>>>Out: []

lst[1:10] # то же самое, что опустить конечныый индекс

>>>Out: [2, 3, 4]

Имея это в виду, вы можете распечатать обратную версию списка, вызвав

lst[::-1]    

>>>Out: [4, 3, 2, 1]

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

lst[3:1:-1]

>>>Out: [4, 3]

Использование индексов отрицательных шагов эквивалентно следующему коду:

list(reversed(lst[0:2]))

>>>Out: [2, 1]

Используемые индексы на 1 меньше, чем используемые при отрицательной индексации, и обращены.

Продвинутый срез

Когда списки нарезали, то вызывается __getitem__() метод объекта списка со slice объектом. В Python есть встроенный метод среза для генерации объектов среза. Мы можем использовать это , чтобы сохранить кусочек и использовать его позже , как показано ниже,

data = 'chandan purohit    22 2000'  #предполагаем, что поля данных фиксированной длины 
name_slice = slice(0,19)
age_slice = slice(19,21)
salary_slice = slice(22,None)

#теперь мы можем получить более читабельные срезы
print(data[name_slice])

>>>Out: 'chandan purohit'     

print(data[age_slice])

>>>Out: 22     

print(data[salary_slice])

>>>Out: 2000 

Это может быть очень полезным, предоставляя функциональность для нарезки наших объектов путем переопределения __getitem__ в нашем классе.

Доступ к значениям во вложенном списке

Начиная с трехмерного списка:

alist = [[[1,2],[3,4]], [[5,6,7],[8,9,10], [12, 13, 14]]]

 

Доступ к элементам в списке:

print(alist[0][0][1]) #доступ ко второму элементу первого списка в первом списке

>>>Out: 2


print(alist[1][1][2]) #доступ к третьему элементу второго списка во втором списке

>>>Out: 10

Выполнение вспомогательных операций:

alist[0][0].append(11) #добавляем 11 в конец первого списка в первом списке

print(alist[0][0][2])

>>>Out: 11

Использование вложенных циклов for для печати списка:

#один из вариантов циклического перебора вложенных списков

for row in alist: 
    for col in row:
        print(col)

>>>Out:	[1, 2, 11]
	[3, 4]
        [5, 6, 7]
        [8, 9, 10]
        [12, 13, 14]

Обратите внимание, что эта операция может использоваться для списка включения или даже в качестве генератора для повышения эффективности, например:

[col for row in alist for col in row]

>>Out: [[1, 2, 11], [3, 4], [5, 6, 7], [8, 9, 10], [12, 13, 14]]

Не все элементы во внешних списках должны быть самими списками:

alist[1].insert(2, 15)
#вставляем 15 в третью позицию во втором списке

Еще один способ использовать вложенные циклы. Другой способ лучше, но мне нужно было использовать это иногда:

#менее характерный для Python способ перебора списка

for row in range(len(alist)): 
    for col in range(len(alist[row])):
       print(alist[row][col])

>>>Out: [1, 2]
	[3, 4]
	[5, 6, 7]
	[8, 9, 10]
	15
	[12, 13, 14]

Использование фрагментов во вложенном списке:

print(alist[1][1:])

>>>Out: [[8, 9, 10], 15, [12, 13, 14]]
#Срезы все равно работают

Финальный список:

print(alist)

>>>Out: [[[1, 2], [3, 4]], [[5, 6, 7], [8, 9, 10], 15, [12, 13, 14]]]

Any() и All()

Вы можете использовать all() , чтобы определить , если все значения итератора вычисляют значение True

nums = [1, 1, 0, 1]
all(nums)

>>>Out: False

chars = ['a', 'b', 'c', 'd']
all(chars)

>>>Out: True

Аналогично, any() определяет , является ли одно или более значений в качестве итератора оценки True

nums = [1, 1, 0, 1]
any(nums)

>>>Out: True

vals = [None, None, None, False]
any(vals)

>>>Out: False

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

vals = [1, 2, 3, 4]
any(val > 12 for val in vals)

>>>Out: False

any((val * 2) > 6 for val in vals)

>>>Out: True 

Проверка, если список пуст

Пустота списка связана с булевым значением False , так что вам не надо проверять len(lst) == 0 , а только lst или not lst

lst = []
if not lst:
    print("список пуст")

>>>Out: 'список пуст' 

Проверка наличия элемента в списке

Python позволяет очень просто проверить, есть ли элемент в списке. Просто используйте in операторе.

lst = ['test', 'twest', 'tweast', 'treast']

'test' in lst

>>>Out: True

'toast' in lst

>>>Out: False 

Примечание: in оператора на множествах асимптотически быстрее , чем в списках. Если вам нужно использовать его много раз на потенциально большие списках, вы можете превратить ваш list в set , и проверить наличие элементов на set .

slst = set(lst)
'test' in slst

>>>Out: True 

Сравнение списков

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

[1, 10, 100] < [2, 10, 100]
#Истина, поскольку 1 < 2

>>>Out: True  

[1, 10, 100] < [1, 10, 100]
#Ложь, потому что списки равны

>>>Out: False

[1, 10, 100] <= [1, 10, 100]
#Истина, потому что списки равны

>>>Out: True  

[1, 10, 100] < [1, 10, 101]
# Истина, поскольку 100 < 101

>>>Out: True  

[1, 10, 100] < [0, 10, 100]
# Ложь, потому что 0 < 1

 >>>Out: False  

Если один из списков содержится в начале другого, выигрывает самый короткий список.

[1, 10] < [1, 10, 100]

>>>Out: True

Объединение и слияние списков

Самый простой способ конкатенации list1 и list2 :

 merged = list1 + list2 

zip возвращает список кортежей, где I-й кортеж содержит I-й элемент из каждого из аргументов последовательностей или итерируемыми:

alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']

for a, b in zip(alist, blist):
    print(a, b)

>>>Out: a1 b1
	a2 b2
	a3 b3

 

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

alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3', 'b4']
for a, b in zip(alist, blist):
    print(a, b)

>>>Out: a1 b1
	a2 b2
	a3 b3


alist = []
len(list(zip(alist, blist)))

>>>Out: 0

 

Для заполнения списков неравной длины до самого длинного с использованием None из itertools.zip_longest (не забудьте в python 3 импортировать библиотеку)

import itertools

alist = ['a1', 'a2', 'a3']
blist = ['b1']
clist = ['c1', 'c2', 'c3', 'c4']

for a,b,c in itertools.zip_longest(alist, blist, clist):
    print(a, b, c)

>>>Out: a1 b1 c1
	a2 None c2
	a3 None c3
	None None c4
Вставка значений в определенный индекс:
alist = [123, 'xyz', 'zara', 'abc']
alist.insert(3, [2009])
print("Финальный список :", alist)

Результат:

>>>Out: Финальный список: [123, 'xyz', 'zara', 2009, 'abc']

Инициализация списка с фиксированным числом элементов

Для неизменяемых элементов (например , None , строковые литералы и т.д.):

my_list = [None] * 10
my_list = ['test'] * 10

Для изменяемых элементов, та же конструкция , приведет все элементы списка со ссылкой на тот же объект, например, для набора:

my_list=[{1}] * 10
print(my_list)

>>>Out: [{1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}, {1}]

my_list[0].add(2)
print(my_list)

>>>Out: [{1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}]

 

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

 my_list=[{1} for _ in range(10)] 

Перебор списка

Python поддерживает использование цикла for непосредственно для  списка:

my_list = ['foo', 'bar', 'baz']
for item in my_list:
    print(item)

>>>Out: foo
	bar
	baz
 

Вы также можете получить позицию каждого элемента одновременно:

for (index, item) in enumerate(my_list):
    print('Элемент в позиции {}: {}'.format(index, item))

>>>Out: Элемент в позиции 0: foo
	Элемент в позиции 1: bar
	Элемент в позиции 2: baz
 

Другой способ итерации списка на основе значения индекса:

for i in range(0,len(my_list)):
    print(my_list[i])

>>>Out: foo
	bar
	baz

Обратите внимание, что изменение элементов в списке во время итерации может привести к неожиданным результатам:

for item in my_list:
    if item == 'foo':
        del my_list[0]
    print(item)

>>>Out: foo
	baz

 

В этом последнем примере, мы удалили первый элемент в первой итерации, это стало причиной пропуска bar.

Длина списка

Используйте len() , чтобы получить одномерный длину списка.

len(['one', 'two']) #возращает 2

>>>Out: 2

len(['one', [2, 3], 'four'])  # возращает 3, а 4

>>>Out: 3  

len() также работает на строках, словарях и других структурах данных, подобных списку.

Обратите внимание, что len() является встроенной функцией, а не методом объекта списка.

Также обратите внимание , что стоимость len() является O(1) , то есть он будет тратить такое же количество времени, чтобы получить длину списка, независимо от его длины.

Список методов и поддерживаемых операторов

Начиная с данным списком a :

a = [1, 2, 3, 4, 5]

 

append(value) - добавляет новый элемент в конец списка.

# Append добавляет 6, 7, and 7 к списку
a.append(6)
a.append(7)
a.append(7)
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7]

# Добавление к другому списку
b = [8, 9]
a.append(b)
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9]]

# Добавляем элемент другого типа, элементы списка не обязательно должны быть одного типа

my_string = "hello world"
a.append(my_string)
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9], "hello world"]

Обратите внимание , что append() метод только добавляет один новый элемент в конец списка. Если вы добавляете список в другой список, добавляемый вами список становится единым элементом в конце первого списка.

# добавляем список в другой список
a = [1, 2, 3, 4, 5, 6, 7, 7]
b = [8, 9]
a.append(b)
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, [8, 9]]

a[8] # возращается [8, 9]

>>>Out: [8,9] 

extend(enumerable) - расширяет список путем добавления элементов из другого перечисляемого.

a = [1, 2, 3, 4, 5, 6, 7, 7]
b = [8, 9, 10]

# расширяем список через добавление всех элементов из b

a.extend(b)
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10]

# расширяем список элементами из неспискового перечисляемого:
a.extend(range(3))
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 0, 1, 2]

Списки также могут быть объединены с + оператора. Обратите внимание, что это не изменяет ни один из исходных списков:

a = [1, 2, 3, 4, 5, 6] + [7, 7] + b
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10] 

insert(index, value) - вставляет value непосредственно перед указанным значение индекса index. Таким образом, после вставки нового элемента занимает позицию index .

a.insert(0, 0)  # вставляем 0 в позицию 0
a.insert(2, 5)  # вставляем 5 в позицию 2
a

>>>Out: [0, 1, 5, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10] 

remove(value) -  удаляет первое вхождение заданного значения. Если прилагаемое значение не может быть найдено, то вызывается ValueError.

a.remove(0)
a.remove(9)
a

>>>Out: [1, 5, 2, 3, 4, 5, 6, 7, 7, 8, 10]

a.remove(10) # [1, 5, 2, 3, 4, 5, 6, 7, 7, 8]

a.remove(10)
# числе 10 не входит в массив a

>>>Out: ValueError: list.remove(x): x not in list 

reverse() - изменяет список на месте и возвращает None .

a.reverse()
a

>>>Out: [8, 7, 7, 6, 5, 4, 3, 2, 1]

Есть также другие способы реверсирования списка .

count(value) - подсчитывает количество вхождений некоторого значения в списке.

a.count(7) #возращает 2

>>>Out: 2

sort() - сортирует список в числовом и лексикографическом порядке и возвращает None .

a.sort() # сортировка списка в числовом порядке
a

>>>Out: [1, 2, 3, 4, 5, 6, 7, 7, 8]

Списки также могут быть отменены при сортировке с помощью reverse=True , флага в своем sort() методе.

a.sort(reverse=True)
# a = [8, 7, 6, 5, 4, 3, 2, 1]

 

Если вы хотите сортировать по атрибутам элементов, вы можете использовать key ключевого слова аргумента:

import datetime

class Person(object):
    def __init__(self, name, birthday, height):
        self.name = name
        self.birthday = birthday
        self.height = height

    def __repr__(self):
        return self.name

l = [Person("John Cena", datetime.date(1992, 9, 12), 175),
     Person("Chuck Norris", datetime.date(1990, 8, 28), 180),
     Person("Jon Skeet", datetime.date(1991, 7, 6), 185)]

l.sort(key=lambda item: item.name)
l

>>>Out: [Chuck Norris, John Cena, Jon Skeet]

l.sort(key=lambda item: item.birthday)
l

>>>Out: [Chuck Norris, Jon Skeet, John Cena]

l.sort(key=lambda item: item.height)
l

>>>Out: [John Cena, Chuck Norris, Jon Skeet]

В случае списка словарей концепция одинакова:

import datetime

l = [{'name':'John Cena', 'birthday': datetime.date(1992, 9, 12),'height': 175},
 {'name': 'Chuck Norris', 'birthday': datetime.date(1990, 8, 28),'height': 180},
 {'name': 'Jon Skeet', 'birthday': datetime.date(1991, 7, 6), 'height': 185}]

l.sort(key=lambda item: item['name'])
l

>>>Out: [Chuck Norris, John Cena, Jon Skeet]

l.sort(key=lambda item: item['birthday'])
l

>>>Out: [Chuck Norris, Jon Skeet, John Cena]

l.sort(key=lambda item: item['height'])
l

>>>Out: [John Cena, Chuck Norris, Jon Skeet]

Сортировать по субсловарям:

import datetime

l = [{'name':'John Cena', 'birthday': datetime.date(1992, 9, 12),'size': {'height': 175, 'weight': 100}},
 {'name': 'Chuck Norris', 'birthday': datetime.date(1990, 8, 28),'size' : {'height': 180, 'weight': 90}},
 {'name': 'Jon Skeet', 'birthday': datetime.date(1991, 7, 6), 'size': {'height': 185, 'weight': 110}}]

l.sort(key=lambda item: item['size']['weight'])
l

>>>Out: [{'name': 'Chuck Norris',
  'birthday': datetime.date(1990, 8, 28),
  'size': {'height': 180, 'weight': 90}},
 {'name': 'John Cena',
  'birthday': datetime.date(1992, 9, 12),
  'size': {'height': 175, 'weight': 100}},
 {'name': 'Jon Skeet',
  'birthday': datetime.date(1991, 7, 6),
  'size': {'height': 185, 'weight': 110}}]

Лучший способ разобраться с помощью attrgetter и itemgetter

Списки также могут быть отсортированы с помощью функции attrgetter и itemgetter  из модуля оператора. Это может помочь улучшить читаемость и возможность повторного использования. Вот несколько примеров,

from operator import itemgetter,attrgetter

people = [{'name':'chandan','age':20,'salary':2000},
          {'name':'chetan','age':18,'salary':5000},
          {'name':'guru','age':30,'salary':3000}]
by_age = itemgetter('age')
by_salary = itemgetter('salary')

people.sort(key=by_age) #in-place sorting by age
people.sort(key=by_salary) #in-place sorting by salary

 

itemgetter также может быть дан индекс. Это полезно, если вы хотите сортировать на основе индексов кортежа.

list_of_tuples = [(1,2), (3,4), (5,0)]
list_of_tuples.sort(key=itemgetter(1))
print(list_of_tuples)

 >>>Out: [(5, 0), (1, 2), (3, 4)]

Используйте attrgetter, если вы хотите сортировать по атрибутам объекта:

persons = [Person("John Cena", datetime.date(1992, 9, 12), 175),
           Person("Chuck Norris", datetime.date(1990, 8, 28), 180),
           Person("Jon Skeet", datetime.date(1991, 7, 6), 185)] #повторное использования класса Person из примера выше


persons.sort(key=attrgetter('name')) #сортирует по 'name'
by_birthday = attrgetter('birthday')
persons.sort(key=by_birthday) #сортирует по 'birthday'
persons

>>>Out: [Chuck Norris, Jon Skeet, John Cena]

clear() - удаляет все элементы из списка

a.clear()
a

>>>Out: [] 

Репликация - умножая существующий список на целое число будет производить больший список , состоящий из того, что многие копии оригинала. Это может быть полезно, например, для инициализации списка:

a = ["blah"] * 3
a

>>>Out: ["blah", "blah", "blah"]

b = [1, 3, 5] * 5
b

>>>Out: [1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5, 1, 3, 5]

Будьте внимательны делая это , если список содержит ссылки на объекты (например , список списков).

Удаление элемента - можно удалить несколько элементов в списке , используя del ключевые слова и ломтик обозначение:

a = list(range(10))
del a[::2]
a 

>>>Out: [1, 3, 5, 7, 9]

del a[-1]
a

>>>Out: [1, 3, 5, 7]
del a[:]

>>>Out: [] 

копирование

Назначение по умолчанию "=" назначает ссылку на исходный список новому имени. Таким образом, исходное имя и новое имя указывают на один и тот же объект списка. Изменения, сделанные с помощью одного из них, будут отражены в другом Это часто не то, что вы хотели.

a = [1, 2, 3, 4, 5]
b = a
a.append(6)
b

>>>Out: [1, 2, 3, 4, 5, 6]

Если вы хотите создать копию списка, у вас есть варианты ниже.

Вы можете нарезать это:

old_list = [1,2,3,4]
new_list = old_list[:]

Вы можете использовать встроенную функцию list():

new_list = list(old_list)

 

Вы можете использовать универсальный copy.copy():

import copy
new_list = copy.copy(old_list) #вставляет ссылки на объекты найденные в оригинале

Это немного медленнее, чем list(), потому что он должен сначала выяснить тип данных old_list.

Если список содержит объекты, и вы также хотите скопировать их, используйте generic copy.deepcopy():

import copy
new_list = copy.deepcopy(old_list) #inserts copies of the objects found in the original.

Очевидно, самый медленный и самый требующий памяти метод, но иногда неизбежный.

copy() - возвращает неполную копию списка

 aa = a.copy()
 # aa = [1, 2, 3, 4, 5]

Удалить повторяющиеся значения в списке

Удаление повторяющихся значений в списке может быть сделано путем преобразования списка в set (то есть неупорядоченный набор различных объектов). Если необходим список, то множество может быть преобразовано обратно в список с помощью функции list():

names = ["aixk", "duke", "edik", "tofp", "duke"]
list(set(names))

>>>Out: ['duke', 'tofp', 'aixk', 'edik']

Обратите внимание, что при преобразовании списка в набор исходный порядок теряется.

Для того, чтобы сохранить порядок списка можно использовать OrderedDict

import collections
>>> collections.OrderedDict.fromkeys(names).keys()
# Out: ['aixk', 'duke', 'edik', 'tofp'] 

Реверсирование элементов списка

Вы можете использовать функцию reversed, которая возвращает итератор обращенного списка:

rev = reversed(numbers)
rev

>>>Out: [9, 8, 7, 6, 5, 4, 3, 2, 1]

 

Обратите внимание, что список «чисел» остается неизменным этой операцией и остается в том же порядке, в котором он был изначально.

Чтобы изменить на месте, вы можете также использовать в reverse метод .

Вы также можете изменить список (фактически получая копию, исходный список не затрагивается), используя синтаксис срезов, задав третий аргумент (шаг) как -1:

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers[::-1]

>>>Out: [9, 8, 7, 6, 5, 4, 3, 2, 1]