Локальные переменные (опять не понятно)

Vutmuk

Новичок
Пользователь
Май 21, 2020
7
0
1
Находясь в процессе решения одной задачки, понял, что опять не понимаю как работают локальные и глобальные переменные :(
вот код:
Код:
def quatro(c):
    for i in range(n):
        M[0][i]=c
        c+=1
    for i in range(1,n):
        M[i][-1]=c
        c+=1
    for i in range(n-2,-1,-1):
        M[-1][i]=c
        c+=1
    for i in range(n-2,0,-1):
        M[i][0]=c
        c+=1
    return c

n=int(input())
c=1
M=[[0 for i in range(n)] for i in range(n)]
c=quatro(c)
for i in M:
    print (*i)

Почему 'n' и 'М' передаются в функцию как глобальные, а 'с' надо указывать как локальную? Потому что если ее не указать, и вызвать просто quatro(), то будет ошибка...
Насчет самой задачи прошу не помогать, решаю сам)
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Почему 'n' и 'М' передаются в функцию как глобальные, а 'с' надо указывать как локальную? Потому что если ее не указать, и вызвать просто quatro(), то будет ошибка...
Потому что у вас локальная переменная c совпадает по имени с глобальной переменной c и когда в функции требуется получить значение переменной c интерпретатор сначала ищет ее в локальной области видимости, а потом если не находит то в глобальной. В данном случае переменная c есть в локальной области видимости c+=1, но у нее не задано значение, поэтому в строке M[0][i]=c возникает ошибка (интерпретатор не понимает какое значение нужно записать в переменную M[0][i] ). Переменная n не определена в локальной области видимости, поэтому ее значение берется из глобальной области видимости. Значение переменной M передается по ссылке, поэтому ее можно не указывать локально, но если в локальной области определить переменную с таким же именем будет использоваться локальная переменная (или если она определена после того как запрошено ее значение - будет ошибка).
Вот несколько примеров:
Python:
# пример с переменной ссылочного типа
def quatro():
    for i in range(n):
        M[0][i] = c[0]
        c[0] += 1
    for i in range(1, n):
        M[i][-1] = c[0]
        c[0] += 1
    for i in range(n - 2, -1, -1):
        M[-1][i] = c[0]
        c[0] += 1
    for i in range(n - 2, 0, -1):
        M[i][0] = c[0]
        c[0] += 1
    return c

n = int(input())
c = [1]
M = [[0 for i in range(n)] for i in range(n)]
c = quatro()
for i in M:
    print(*i)
Python:
# пример с локальной переменной n

def quatro():
    for i in range(n): # в этой строке будет ошибка
        M[0][i] = c
        c += 1
    n += 1 # локальная переменная n
    for i in range(1, n):
        M[i][-1] = c
        c += 1
    for i in range(n - 2, -1, -1):
        M[-1][i] = c
        c += 1
    for i in range(n - 2, 0, -1):
        M[i][0] = c
        c += 1
    return c

n = int(input())
c = 1
M = [[0 for i in range(n)] for i in range(n)]
c = quatro()
for i in M:
    print(*i)

Python:
# пример с локальной переменной ссылочного типа
def quatro():
    for i in range(n):
        M[0][i] = c[0]
        c[0] += 1
    M = [[1 for i in range(n)] for i in range(n)] # тут будет ошибка
    for i in range(1, n):
        M[i][-1] = c[0]
        c[0] += 1
    for i in range(n - 2, -1, -1):
        M[-1][i] = c[0]
        c[0] += 1
    for i in range(n - 2, 0, -1):
        M[i][0] = c[0]
        c[0] += 1
    return c

n = int(input())
c = [1]
M = [[0 for i in range(n)] for i in range(n)]
c = quatro()
for i in M:
    print(*i)

Python:
# второй пример с локальной переменной ссылочного типа
def quatro():
    # ошибки не будет, но будет использоваться локальная переменная M
    # и результат работы функции изменится
    M = [[1 for i in range(n)] for i in range(n)]
    for i in range(n):
        M[0][i] = c[0]
        c[0] += 1
    
    for i in range(1, n):
        M[i][-1] = c[0]
        c[0] += 1
    for i in range(n - 2, -1, -1):
        M[-1][i] = c[0]
        c[0] += 1
    for i in range(n - 2, 0, -1):
        M[i][0] = c[0]
        c[0] += 1
    return c

n = int(input())
c = [1]
M = [[0 for i in range(n)] for i in range(n)]
c = quatro()
for i in M:
    print(*i)
 
  • Мне нравится
Реакции: Vutmuk

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
Python:
def quatro():
    global c
    for i in range(n):
        M[0][i] = c
        c += 1
    for i in range(1, n):
        M[i][-1] = c
        c += 1
    for i in range(n-2, -1, -1):
        M[-1][i] = c
        c += 1
    for i in range(n-2, 0, -1):
        M[i][0] = c
        c += 1
    return

c = 1
n = int(input())
M = [[0 for i in range(n)] for i in range(n)]
quatro()
for i in M:
    print(*i)
 

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
P.S. Ошибка возникает при ОПЕРАЦИИ ПРИСВАИВАНИЯ c += внутри функции, а переменным n и M ничего не присваивается внутри функции. Если переменная c берется из уровня модуля, то надо перед блоком использования прописать global c, если из внешней функции (enclosed), то nonlocal c.
 
  • Мне нравится
Реакции: Vutmuk

Vutmuk

Новичок
Пользователь
Май 21, 2020
7
0
1
Спасибо за ответы! Вроде бы оно понятно, а вроде и нет....
значит присваивание идет в строчке c += 1
а ошибка возникает в строчке 'M[0] = c ', которая расположена раньше! Разве интерпретатор не должен идя по порядку, взять значение с из глобальной области?
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Спасибо за ответы! Вроде бы оно понятно, а вроде и нет....
значит присваивание идет в строчке c += 1
а ошибка возникает в строчке 'M[0] = c ', которая расположена раньше! Разве интерпретатор не должен идя по порядку, взять значение с из глобальной области?
Ошибка возникает в строке M[0][i]=c потому что интерпретатор как раз выполняет код последовательно и дойдя до этой строки он проверяет есть ли в локальной области видимости переменная c, а она есть, но у нее нет значения, вот и возникает ошибка. Чтобы интерпретатор сразу искал в глобальной области видимости нужно указать global c в начале функции.
 

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
Ошибка внутри функции возникает, если после использования глобальной переменной этой переменной что-нибудь присваивается, что и устраняется ключевым словом global (значение глобальной переменной внутри функции можно ИЗМЕНИТЬ, только описав ее как global).
 

Vutmuk

Новичок
Пользователь
Май 21, 2020
7
0
1
Видимо эту фишку просто придется принять как данность)
Ошибка возникает в строке M[0][i]=c потому что интерпретатор как раз выполняет код последовательно и дойдя до этой строки он проверяет есть ли в локальной области видимости переменная c, а она есть... global c
Откуда же она есть если с+=1 идет после,т.е. локальная с еще не объявлена? (сорян что туплю, хочется докопаться до сути)
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Видимо эту фишку просто придется принять как данность)
Откуда же она есть если с+=1 идет после,т.е. локальная с еще не объявлена? (сорян что туплю, хочется докопаться до сути)
Когда запускается функция quatro() формируется локальная область видимости, в которую попадает переменная c, так как в функции есть строка с+=1 . Когда интерпретатор доходит до строки M[0][i]=c он видит что переменная c определена локально и пытается получить ее значение, а поскольку значения у нее нет - возникает ошибка.
 
  • Мне нравится
Реакции: Vutmuk

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
Коллеги, остаюсь при своем мнении:
1. Из пространства функции все глобальные переменные прекрасно видны.
2. Хотя они видны, ИЗМЕНИТЬ их НЕЛЬЗЯ. Если global c, то можно.
3. Относительно ошибки в строке M[ 0 ][ i ] = c. Закомментируйте в функции quatro() без параметров все присвоения #c += 1. Запустите. Результат вас несколько озадачит.

Python:
def quatro():
    for i in range(n):
        M[0][i]=c
        #c+=1
    for i in range(1,n):
        M[i][-1]=c
        #c+=1
    for i in range(n-2,-1,-1):
        M[-1][i]=c
        #c+=1
    for i in range(n-2,0,-1):
        M[i][0]=c
        #c+=1
    return

n=int(input())
c=1
M=[[0 for i in range(n)] for i in range(n)]
quatro()
for i in M:
    print (*i)
 

stud_55

Модератор
Команда форума
Модератор
Апр 3, 2020
1 522
672
113
Коллеги, остаюсь при своем мнении:
1. Из пространства функции все глобальные переменные прекрасно видны.
2. Хотя они видны, ИЗМЕНИТЬ их НЕЛЬЗЯ. Если global c, то можно.
3. Относительно ошибки в строке M[ 0 ][ i ] = c. Закомментируйте в функции quatro() без параметров все присвоения #c += 1. Запустите. Результат вас несколько озадачит.
С первыми двумя пунктами никто и не спорит - все так. Я просто пытался объяснить про области видимости и почему возникает ошибка. Насчет 3-го пункта там тоже все логично: так как переменная c больше не определена локально, ее значение берется из глобальной области видимости, но поскольку оно не изменяется в результате получается что в переменную M записываются только 1.
 
Последнее редактирование:

Vutmuk

Новичок
Пользователь
Май 21, 2020
7
0
1
Коллеги, остаюсь при своем мнении:
1. Из пространства функции все глобальные переменные прекрасно видны.
2. Хотя они видны, ИЗМЕНИТЬ их НЕЛЬЗЯ. Если global c, то можно.
3. Относительно ошибки в строке M[ 0 ][ i ] = c. Закомментируйте в функции quatro() без параметров все присвоения #c += 1. Запустите. Результат вас несколько озадачит.
получается так) А вот изменить элемент в списке, который объявлен глобально-можно)) (пытаюсь переварить ссылочную модель)))
 

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
получается так) А вот изменить элемент в списке, который объявлен глобально-можно)) (пытаюсь переварить ссылочную модель)))
С вложенными списками не так все просто. Я, например, не знаю в стандартном Python ни одного инструмента, который бы позволил с гарантией сделать копию (а не получить ссылку!) структур данных (в т.ч. списков) с вложенностью не менее 2-х. Так, deepcopy() гарантирует только 2-й уровень вложенности, и то есть примеры, когда и он не справляется.
Т.е. не совсем понятно, если глубокая структура данных находится гарантированно в global, то чем в представлении самого Python является локальная ссылка на нее - это local или global? Наверное, надо бы потестировать это поведение, используя инструментарий самого Python.
Кстати, а какая версия Python у Вас?
 

vs2007

Пользователь
Пользователь
Май 24, 2020
16
5
3
С первыми двумя пунктами никто и не спорит - все так. Я просто пытался объяснить про области видимости и почему возникает ошибка. Насчет 3-го пункта там тоже все логично: так как переменная c больше не определена локально, ее значение берется из глобальной области видимости, но поскольку оно не изменяется в результате получается что в переменную M записываются только 1.
Да, конечно, Ваши объяснения логичные и понятные.
На мой взгляд, тут дело еще может оказаться в конкретном поведении самого интерпретатора (на этапе построения байт-кода при первом проходе) и в поведении PVM.
 

Vutmuk

Новичок
Пользователь
Май 21, 2020
7
0
1
С вложенными списками не так все просто. Я, например, не знаю в стандартном Python ни одного инструмента, который бы позволил с гарантией сделать копию (а не получить ссылку!) структур данных (в т.ч. списков) с вложенностью не менее 2-х. Так, deepcopy() гарантирует только 2-й уровень вложенности, и то есть примеры, когда и он не справляется.
Т.е. не совсем понятно, если глубокая структура данных находится гарантированно в global, то чем в представлении самого Python является локальная ссылка на нее - это local или global? Наверное, надо бы потестировать это поведение, используя инструментарий самого Python.
Кстати, а какая версия Python у Вас?
у меня 3.8.0 насчет копирования я конечно не знаю, никогда не приходилось еще работать с модулем copy, всегда делал поэлементно for i in...for j in... :)
 

Форум IT Специалистов