Метка: global

Python: все про del

КДПВ

Инструкция del (от англ. delete), как можно понять из названия, нужна чтобы что-то удалять, а именно имена переменных, атрибуты объектов, элементы списков и ключи словарей.

1. Удаление элемента из списка по индексу:

>>> x = [1, 2, 3, 4, 5]
>>> del x[2]
>>> x
[1, 2, 4, 5]

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

>>> x = [1, 2, 3, 4, 5]
>>> del x[:2]
>>> x
[3, 4, 5]

Удаление последних n элементов: del x[n:].

Удаление элементов с четными индексами: del x[::2], нечетными: del x[1::2].

Удаление произвольного среза: del x[i:j:k].

Не путайте del x[2] и x.remove(2). Первый удаляет по индексу (нумерация с 0), а второй по значению, то есть находит в списке первую двойку и удаляет ее.

2. Удаление ключа из словаря. Просто:

>>> d = {"foo": 5, "bar": 8}
>>> del d["foo"]
>>> d
{'bar': 8}

А вот строки, байты и сеты del не поддерживают.

3. Удаление атрибута объекта.

class Foo:
    def __init__(self):
        self.var = 10

f = Foo()
del f.var
print(f.var)  # ошибка! 

Примечание: можно через del удалить метод у самого класса del Foo.method, но нельзя удалить метод у экземпляра класса del Foo().methodAttributeError.

4. Что значит удалить имя переменной? Это просто значит, что надо отвязать имя от объекта (при этом если на объект никто более не ссылается, то он будет освобожден сборщиком мусора), а само имя станет свободно. При попытке доступа к этому имени после удаления будет NameError, пока ему снова не будет что-то присвоено.

>>> a = 5
>>> del a
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined

Здесь кроется один нюанс. Если переменная была внутри функции помечена, как global, то после ее удаления глобальная переменная никуда не денется, а имя освободится лишь в зоне видимости функции. Причем если мы снова присвоим ей значение, то она опять окажется глобальной, т.е. del не очищает информацию о global!

g = 100
def f():
    global g
    g = 200
    del g  # g останется вне функции
    g = 300  # та же самая глобальная g

f()
print(g) # 300

Чтобы реально удалить глобальную переменную, можно сделать так: del globals()['g'].

В пунктах 1, 2, 3 в качестве имен могут фигурировать выражения и ссылки, так как операции идут над содержимым объектов, а в пункте 4 должно быть строго формальное имя удаляемого объекта.

>>> x = [1, 2, 3]
>>> y = x
>>> del y  # удаляет именно y, но x остается

Еще одна особенность del – она может удалить несколько вещей за раз, если передать в нее кортеж или список объектов на удаление.

x, y, z = 10, 20, [1, 2, 3]
del x, y, z[2]

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

x = [1, 2, 3, 4, 5]
del x[2], x[4]

Казалось бы, что будут удалены 2-й и 4-й элементы списка, но это не так! Удаления происходят один за одним, и сначала будет удален 2-й элемент, размер списка уменьшится на 1, а потом будет попытка удалить 4-й элемент, но она будет неудачна – вылетит исключение IndexError, потому что элемента с индексом 4 больше нет, а сам список будет равен [1, 2, 4, 5]. Так что будьте внимательны в подобных ситуациях!

Специально для канала @pyway. Подписывайтесь на мой канал в Телеграм @pyway 👈 

global и nonlocal

КДПВ: череп и плакат "второе лицо вашей глобализации"

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

def foo():
    print('x is', x)

x = 5
foo()  # напечает x is 5

Однако если в функции есть присваиваниие x после использования переменной x, то возникнет ошибка:

def foo():
    print('x is', x)
    x = 10

x = 5
foo()  # UnboundLocalError: local variable 'x' referenced before assignment

Обатите внимание, что присваивание бывает в следующих ситуациях:

x = …
x += …, x -= … и т.п.
for x in …:
with … as x:

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

def foo():
    global x  # <-- тут
    print('x is', x)
    x = 10
    print('x is now', x)

x = 5
foo()  # ошибок не будет

Подобная проблема возникает и для вложенных функций, когда во внутренней функции мы хотим поймать в замыкание переменную из внушней функции, чтобы далее присвоить ей другое значение. Вот пример – функция, создающее увеличивающийся на 1 счечтик:

def make_inc():  # внешняя ф-ция
    total = 0     # счетчик
    def helper():  # внутр. ф-ция 
        total += 1  # тут присваивание переменной
        return total 
    return helper  

f = make_inc()

print(f())  # UnboundLocalError: local variable 'total' referenced before assignment

Тут нужно другое ключевое слово – nonlocal, которое укажет, что нужно искать переменную во внешней области видимости. Такой пример будет работать, как задумано:

def make_inc():
    total = 0
    def helper():
        nonlocal total  # <- тут
        total += 1
        return total
    return helper

f = make_inc()
print(f())

Почему мы редко видим global и nonlocal?
nonlocal – специфичная вещь, обычно вместо нее создают класс.
global потакает плохим практикам программирования. Менять глобальные переменные внутри функций – плохая практика.

📎 Пример.

def foo():
    global x
    print('x is', x)
    for x in range(2):
        ...
x = 5
foo()  # x is 5
foo()  # x is 1 (испортили из-за for)

Нет ошибок выполнения, но есть логическая ошибка! После первого вызова foo() мы испортили глобальную переменную x, она стала 1 (последним значением в цикле). Надо было просто называть переменные разными именами, и global не понадобится!

Специально для канала @pyway. Подписывайтесь за новыми статьями. Будет интересно!