Дыра в области видимости, мертвый код или почему такой вывод? - PullRequest
0 голосов
/ 13 августа 2011

Код

def change1(list1):
        list1[1] = list1[1] + 5

def change2(number):
        number = number + 2

def main():
        numbers = [4, 8, 12]
        change1(numbers)
        variable = 15
        change2(variable)
        i = 0
        while i < 3:
                print numbers[i]
                i += 1
        print variable

main()

Когда я читал его, я думал, что он выдаст 4 8 12 15, но выводит 4 13 12 15.Я вижу здесь, что Python по-разному работает с целыми числами и списками, я предположил, что последнее невозможно без глобального.Я не могу понять вывод, в таком случае, почему бы не выводить 4 13 12 17?

Здесь вы можете увидеть почти идентичный код с разными типами и разными ссылками:

$ python test2.py 
4
13
12
15
$ python test3.py 
4
13
12
17
$ cat test2.py test3.py 

Примеры передачи по ссылке

test2.py: тип передачи по ссылке и изменяемый тип -example.Таблицы / списка недостаточно, чтобы повлиять на локальную переменную в main, вам нужен Справочник!

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number = [x+2 for x in number]

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()

test3.py: пример передачи по ссылке, изменяющий изменяемый список / таблицу типов данных вне главнойfunction

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number[0] += 2

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    variable = [15]
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()

примеры передачи по значению

test4.py: пытаясь найти пример с передачей по значению, почему он не работает?

$ cat test4.py 

# Not yet a pass-by-value example!

global variable
variable = [15]

def change1(list1):
    list1[1] = list1[1] + 5

def change2(number):
    number = [x+2 for x in number] 

def main():
    numbers = [4, 8, 12]
    change1(numbers)
    #variable = 15
    change2(variable)
    i = 0
    while i < 3:
        print numbers[i]
        i += 1
    print variable[0]

main()
$ python test4.py 
4
13
12
15   # I expected 17! Why no 17?

Ответы [ 5 ]

2 голосов
/ 13 августа 2011
def change1(list1):
        # `list1[1] =` means you are changing the object passed in
        list1[1] = list1[1] + 5

def change2(number):
        # `number = ` means you create a **new local variable**, number, 
        # based on the `number`you passed in
        number = [x+2 for x in number]

Так что, если вы хотите изменить существующие объекты, вы должны каким-то образом изменить их, например, в

def change3(number):
    # `number[:]` is the whole existing list and you overwrite it
    number[:] = [x+2 for x in number]

Обратите внимание на [ .. ] при изменении списка.

1 голос
/ 12 января 2012

Окончательный ответ заключается в том, что Python на самом деле является «вызовом через общий доступ», также известным как «вызов по объекту» или «вызов по объекту».

Это было подробно обсуждалось до .Из этой статьи:

Время от времени люди, которые читали немного CS, но не много CS (или слишком много только одного вида CS), появляются на comp.lang.pythonи тратить много энергии, пытаясь сказать всем, что Python использует некоторую модель вызова, которую он на самом деле не использует.Всегда оказывается, что они на самом деле не понимают модель Python, и довольно часто они также не понимают свою любимую модель.

Но не важно, единственное, что вам нужно знать, это то, что модель Python не является ни«Вызов по значению» или «вызов по ссылке» (поскольку любая попытка использовать эти термины для Python требует использования нестандартных определений слов «-value» и «-reference»).Наиболее точное описание - это « вызов CLU по объекту » или « вызов посредством совместного использования ».Или, если вы предпочитаете, « вызов по ссылке на объект ».

Вы также должны прочитать this , если вы еще этого не сделали.

Семантика Python больше всего похожа на семантику языка CLU.Справочное руководство по CLU Лискова и др. описывает семантику следующим образом:

"Мы называем технику передачи аргументов вызовом, разделяя , потому что объекты аргументаявляются общими для вызывающей и вызываемой подпрограмм. Этот метод не соответствует большинству традиционных методов передачи аргументов (он аналогичен передаче аргументов в LISP). В частности, он не вызывается по значению из-за мутаций аргументоввыполненная вызываемой подпрограммой будет видима для вызывающего. И она не вызывается по ссылке , поскольку доступ предоставляется не к переменным вызывающего, а только к определенным объектам. "

1 голос
/ 13 августа 2011

Параметры Python передаются по ссылке. Вы поменяли только один объект в change1.

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

Подробнее: http://www.penzilla.net/tutorials/python/functions/

0 голосов
/ 13 августа 2011

Все примеры показывают вызов по значению. Python имеет только вызов по значению. Там нет вызова по ссылке. Все значения в python являются ссылками (невозможно иметь «объект» в качестве значения). Следовательно, это ссылки, которые копируются при передаче в функцию. Списки являются изменяемыми, поэтому можно изменять его содержимое с помощью общей ссылки. В change2 вы переназначаете локальную переменную так, чтобы она указывала на другой объект, что, как и все присвоения локальным переменным, не влияет на какую-либо область вызова, так как это вызов по значению.

0 голосов
/ 13 августа 2011

В change1 вы меняете значение в списке на value + 5.
В change2 вы добавляете 5 к number.Результатом является новый объект, который применяется не только к переданной переменной.Если вы пришли из C ++: Нет, в Python нет int& var.

При этом вы получите ожидаемый результат:

def change2(number):
    return number + 5

variable = 15
variable = change2(variable)

Если вы все еще не хотите возвращатьзначение, вы можете создать MutableInt класс.

class MutableInt(object):

    def __init__(self, value = 0):
        self._value = int(value)

    def __add__(self, other):
        self._value += int(other)
        return self

    def __sub__(self, other):
        self._value -= int(other)
        return self

    ...
...