2016-10-15 8 views
0

Я изучаю python и пытаюсь понять, как работает стек в python. У меня есть некоторые сомнения. Я знаю, как работают стек, и я прочитал много статей и руководств об этом. Я прочитал много потоков в stackoverflow, они все хороши, но у меня есть некоторые сомнения.Как работает вызов стека аргументов в Python?

До сих пор я считывал стеки и возвращал значения функции. Он работает директором LIFO.

Только первое значение стека будет возвращено и сохранено сверху.

Так что мой вопрос - предположим, что есть четыре переменные:

a = 9 
b = 2 
c = 13 
d = 4 

Они хранятся в стеке в порядке, в котором функционируют называют значение, например:

sum = a + d 
sum = b + c 

a = 9 
d = 4 
b = 2 
c = 13 

и они вернутся сверху вниз. Теперь моя путаница - если какая-либо операция требует использования d и c, а значения возврата стека сверху, как стек получит значения d и c, если они находятся в середине стека. Будет ли стек сначала возвращать a, а затем возвращать d ??

Это путаница.

+2

При доступе к стековым переменным не задействовано 'push'ing или' pop'ing. – tkausl

+0

Я в замешательстве, вы имеете в виду «стек» как структуру данных или «стек» в качестве памяти? Если первый случай - не используйте его, если вам понадобятся значения NOT сверху, если последние, переменные находятся в «виртуальной» куче сложной функции, так что это нормально – RafazZ

+2

Почему вы отметили это с [переполнением стека]? –

ответ

0

Если вам интересно, как работает python под капотом, существует стандартная библиотека CPython dis. Вот выход для вашего тестового кода:

>>> import dis 
>>> def test(): 
...  a = 9 
...  b = 2 
...  c = 13 
...  d = 14 
...  sum1 = a + d 
...  sum2 = b + c 
... 
>>> dis.dis(test) 
    2   0 LOAD_CONST    1 (9) 
       3 STORE_FAST    0 (a) 

    3   6 LOAD_CONST    2 (2) 
       9 STORE_FAST    1 (b) 

    4   12 LOAD_CONST    3 (13) 
      15 STORE_FAST    2 (c) 

    5   18 LOAD_CONST    4 (14) 
      21 STORE_FAST    3 (d) 

    6   24 LOAD_FAST    0 (a) 
      27 LOAD_FAST    3 (d) 
      30 BINARY_ADD   
      31 STORE_FAST    4 (sum1) 

    7   34 LOAD_FAST    1 (b) 
      37 LOAD_FAST    2 (c) 
      40 BINARY_ADD   
      41 STORE_FAST    5 (sum2) 
      44 LOAD_CONST    0 (None) 
      47 RETURN_VALUE   

и это не имеет ничего общего со стеком, как вы можете видеть. Он не будет делать даже с исправленным кодом, как

>>> def test2(a,b,c,d): 
...  sum1 = a + d 
...  sum2 = b + c 
... 
>>> dis.dis(test2) 
    2   0 LOAD_FAST    0 (a) 
       3 LOAD_FAST    3 (d) 
       6 BINARY_ADD   
       7 STORE_FAST    4 (sum1) 

    3   10 LOAD_FAST    1 (b) 
      13 LOAD_FAST    2 (c) 
      16 BINARY_ADD   
      17 STORE_FAST    5 (sum2) 
      20 LOAD_CONST    0 (None) 
      23 RETURN_VALUE   

Если вы заинтересованы в концепции стеки вообще в вычислениях, вы должны переключиться на некоторые низкоуровневые (2.5 языки поколения, как и C), или пойдите еще глубже в языки ассемблера.

Чтение о различных соглашениях о вызовах может быть еще одна хорошей стартовой точкой а (x86 calling conventions, например)

+0

его значение звонка по ссылке? например, LOAD_FAST-вызов 0 (a), так что 0 адрес памяти? –

+0

Это индекс переменной в кадре стека Python. Несмотря на то, что это называется _stack_ frame, вы должны думать об этом больше как регистры общего назначения x86/ARM (например, eax, ebx/r0, r1, r2 et cetera). Кадр выделяется внутри стека виртуальной машины CPython (таким образом, _stack frame_), но данные не доступны в порядке LIFO. Вот ссылка [bytecodes reference] (https://docs.python.org/2/library/dis.html#bytecodes) для CPython – agg3l

+0

, когда переменная python (а не какое-либо значение const) помещается в стек, тогда да, вы можете говорят, что к нему обращаются по ссылке. – agg3l

0

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

В C, например, у меня могут быть локальные переменные a, b, c, d "в стеке", но это означает, что они хранятся с известным фиксированным смещением от начала кадра. Доступ к переменным можно получить в любом порядке, и они не перемещаются (кроме оптимизаций, которые может выполнять компилятор).

Python делает это немного сложнее. В CPython, по крайней мере, есть фрейм стека под обложками, но локальные переменные, которые вы видите, фактически хранятся в массиве (см. Примечание shadowranger ниже) на объекте экземпляра функции, который составляет локальное пространство имен функции. Все переменные могут быть доступны через dict в любое время.

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

+0

Локальные переменные фактически не хранятся в 'dict' автоматически. 'locals()' (и, возможно, закрытие вложенного доступа?) должен сделать 'dict' по требованию, если вы его никогда не называете, локальные переменные хранятся в массиве. Если вы используете 'dis', вы увидите разницу; locals доступны с помощью функции «LOAD_FAST' /' STORE_FAST », которая выглядит в массиве, индексированном по номеру (размер массива определяется компилятором с именами, присвоенными номерам). Использование 'dict' для локального доступа было бы слишком медленным. – ShadowRanger

+0

@ShadowRanger - спасибо! Я обновил ссылку на ваш комментарий. Я всегда предполагал, что местные жители и, скажем, методы поиска экземпляров класса были примерно эквивалентными. – tdelaney