Keywords: easytut

Далее: Строки возвращаются Наверх: Курс языка Python для начинающих Назад: Использование модулей Содержание

# Возвращаемся к спискам

Вы уже видели списки и различные способы их применения. Теперь, когда вы с ними немного поработали, я хочу взглянуть на эту тему глубже. Сначала мы рассмотрим дополнительные возможности доступа к элементам списка, а затем мы поговорим об их копировании.

Примеры использования индексов для доступа к одному элементу списка:

    >>> list = ['zero','one','two','three','four','five']
    >>> list[0]
    'zero'
    >>> list[4]
    'four'
    >>> list[5]
    'five'

Все эти примеры вы уже встречали. Если вы хотите увидеть первый элемент списка, посмотрите на элемент с индексом 0. Второй элемент стоит под индексом 1, и так далее. А что делать, если вы хотите последний элемент списка? Один способ – использовать функцию `len`, вроде `list[len(list) – 1]`. Это работает, потому что функция `len` вычисляет последний индекс плюс 1. Поэтому предпоследний элемент будет `list[len(list) – 2]`. Существует более простой способ делать это. В Python последний элемент всегда идет с индексом index – 1, предпоследний – index – 2, и т.д. Примеры:

    >>> list[len(list)-1]
    'five'
    >>> list[len(list)-2]
    'four'
    >>> list[-1]
    'five'
    >>> list[-2]
    'four'
    >>> list[-6]
    'zero'

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

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

    >>> list = [0,'Fred',2,'S.P.A.M.','Stocking',42,"Jack","Jill"]
    >>> list[0]
    0
    >>> list[7]
    'Jill'
    >>> list[0:8]
    [0, 'Fred', 2, 'S.P.A.M.', 'Stocking', 42, 'Jack', 'Jill']
    >>> list[2:4]
    [2, 'S.P.A.M.']
    >>> list[4:7]
    ['Stocking', 42, 'Jack']
    >>> list[1:5]
    ['Fred', 2, 'S.P.A.M.', 'Stocking']

Интервалы используют, чтобы найти часть списка. Оператор интервала имеет вид `list[first_index:following_index]`. Интервал отсчитывается, начиная с `first_index`, и не доходит на один до `following_index`. Можно использовать оба варианта индексирования:

    >>> list[-4:-2]
    ['Stocking', 42]
    >>> list[-4]
    'Stocking'
    >>> list[-4:6]
    ['Stocking', 42]

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

    >>> list[:2]
    [0, 'Fred']
    >>> list[-2:]
    ['Jack', 'Jill']
    >>> list[:3]
    [0, 'Fred', 2]
    >>> list[:-5]
    [0, 'Fred', 2]

Пример программы (скопируйте и вставьте сам стих, если хотите):

    poem = ["<B>","Jack","and","Jill","</B>","went","up","the","hill","to","<B>",\
    "fetch","a","pail","of","</B>","water.","Jack","fell","<B>","down","and",\
    "broke","</B>","his","crown","and","<B>","Jill","came","</B>","tumbling",\
    "after"]

    def get_bolds(list):
            true = 1
            false = 0
            ## is_bold показывает находимся ли мы внутри выделенного
            ## текста.
            is_bold = false
            ## start_block -индекс начала или выделенной или
            ## невыделенной части текста.
            start_block = 0
            for index in range(len(list)):
                    ##Обработка начала выделенного текста
                    if list[index] == "<B>":
                            if is_bold:
                                    print "Error:  Extra Bold"
                            ##print "Not Bold:",list[start_block:index]
                            is_bold = true
                            start_block = index+1
                    ##Обработка конца выделенного текста
                    ##Помните, что последнее число интервала - индекс,
                    ## идущий после использованного индекса
                    if list[index] == "</B>":
                            if not is_bold:
                                    print "Error: Extra Close Bold"
                            print "Bold [",start_block,":",index,"] ",\
                            list[start_block:index]
                            is_bold = false
                            start_block = index+1

    get_bolds(poem)

Результат:

    Bold [ 1 : 4 ]  ['Jack', 'and', 'Jill']
    Bold [ 11 : 15 ]  ['fetch', 'a', 'pail', 'of']
    Bold [ 20 : 23 ]  ['down', 'and', 'broke']
    Bold [ 28 : 30 ]  ['Jill', 'came']

Функция `get_bolds` использует список, состоящий из обычных и ключевых слов. Ключевые слова состоят из `<B>`, которое начинает жирный текст и `</B>`, которое его заканчивает. Функция `get_bolds` просматривает список и находит ключевые слова.

Следующее особенность списков – их копирование. Попробуйте что-нибудь простое вроде:

    >>> a = [1,2,3]
    >>> b = a
    >>> print b
    [1, 2, 3]
    >>> b[1] = 10
    >>> print b
    [1, 10, 3]
    >>> print a
    [1, 10, 3]

Возможно, это выглядит неожиданно, поскольку изменения `b` привели к изменению `a`. Это происходит потому, что команда `b = a` делает `b` ссылкой на `a`. Что означает – `b` просто другое имя для `a`. Поэтому изменения `b` также меняют `a`. Тем не менее, некоторые присваивания не создают два имени для одного и того же списка:

    >>> a = [1,2,3]
    >>> b = a*2
    >>> print a
    [1, 2, 3]
    >>> print b
    [1, 2, 3, 1, 2, 3]
    >>> a[1] = 10
    >>> print a
    [1, 10, 3]
    >>> print b
    [1, 2, 3, 1, 2, 3]

В этом случае `b` не является ссылкой, поскольку выражение `a*2` создает новый список. Таким образом, команда `b = a*2` создает ссылку на `a*2`, а не на `a`. Все операции присваивания создают ссылки. Когда вы используете список как аргумент функции, вы также создаете ссылку. Обычно вам не надо беспокоиться о том, что вы создаете ссылки, а не копии. Однако когда вам нужно изменить один список, не меняя другой, вам необходимо быть уверенным, что вы действительно работаете с копией.

Существует несколько способов создать копию списка. Простейший, который работает почти всегда – это интервал, поскольку он всегда создает новый список, даже если вы запрашивайте интервал всего списка:

    >>> a = [1,2,3]
    >>> b = a[:]
    >>> b[1] = 10
    >>> print a
    [1, 2, 3]
    >>> print b
    [1, 10, 3]

Интервал `[:]` создает копию списка. Однако он копирует только внешний список. Все списки внутри него остаются ссылками на соответствующие списки первоначального списка. Таким образом, когда список содержит другие списки, их также нужно копировать. Вы можете делать это вручную, но Python содержит модуль, который делает это. Используйте функцию `deepcopy` модуля `copy`:

    >>> import copy
    >>> a = [[1,2,3],[4,5,6]]
    >>> b = a[:]
    >>> c = copy.deepcopy(a)
    >>> b[0][1] = 10
    >>> c[1][1] = 12
    >>> print a
    [[1, 10, 3], [4, 5, 6]]
    >>> print b
    [[1, 10, 3], [4, 5, 6]]
    >>> print c
    [[1, 2, 3], [4, 12, 6]]

Для начала, обратите внимание, что `a` – список, состоящий из списков. Также обратите внимание, что когда выполняется `b[0][1] = 10`, `b` и `a` меняются, но `c` остается. Это происходит, потому что на внутренние списки все равно ссылаются, когда используется оператор интервала. Однако с `deepcopy` список полностью копируется.

Так нужно ли мне беспокоиться о ссылках каждый раз, когда я использую функцию или оператор присваивания? Хорошая новость – о ссылках нужно беспокоиться только тогда, когда я работаю со списками и словарями. Числа и строки создают ссылки при присвоении, но любая операция, которая их меняет, создает новую копию, поэтому вы не сможете нечаянно их изменить. Вам нужно беспокоиться о ссылках, если вы работаете со словарями или списками.

Возможно, к этому моменту вы задаете вопрос, зачем вообще использовать ссылки? Основная причина – скорость. Гораздо быстрее создать ссылку на список с тысячью элементами, чем копировать все элементы. Другая причина – это позволяет функции изменять их, если они используются как ее аргументы. Просто помните о ссылках, если у вас появляются странные проблемы, когда данные меняются там, где они не должны.

Далее: Строки возвращаются Наверх: Курс языка Python для начинающих Назад: Использование модулей Содержание