2014-10-16 5 views
1

Интересно, если какой-либо питона люди могут просветить меня о том, как/почему происходит следующее:Python локальная переменная изменяется другим методом

# define a list and send it as an arg to another function 
def foo(): 
    nums=[1,2,3] 
    bar(nums) 
    print(nums) 

# Take the list as an arg and pop the last element off 
def bar(numbrs): 
    numbrs.pop() 

# Call the first function 
foo() 

# The nums local variable has been modified 
[1, 2] 

В rubyist я считаю, это действительно странно, что локально определенные переменной (nums) в функции foo может быть изменено действием, выполняемым в функции бара! Является ли такая запутанность нормальной? Есть ли имя для этого?

Эти две функции не используют одно и то же имя для ссылки на список. Это очень странно. Мне вроде как нравится.

+0

это ставит странное чувство в моем желудок – TehTris

ответ

1

Список - это объект, а вызов метода на объект - например pop() - влияет на его состояние.

Чтобы быть более точным, давайте пройдем через код и посмотреть, что происходит:

def foo(): 
    nums=[1,2,3] # create a list and give it a name. nums contains a reference to the list 
    bar(nums)  # call the method bar, passing it the reference to that list 
    print(nums) # print the list. 

Хорошо, так что это довольно ясно. Что происходит в баре это то, что в отношении к вам:

# Take the list as an arg and pop the last element off 
def bar(numbrs): # method gets a value. In this case the value is a reference to a list 
    numbrs.pop() # call a method on that object 

Так что же происходит, когда вы называете numbrs.pop()? Предположительно, в определении списка, вы найдете определение метода:

def pop(self): 

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

Что такое «я» в этом случае? Это ссылка на список. В частности, это ссылка, которая была вызвана numbrs, когда вы выполнили операцию pop(), которая является той же ссылкой, которую вы сохранили как nums, когда вы ее создали.

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

+0

Большое спасибо, я понятия не имел, что это произошло в Ruby, а также в Python. Ваше объяснение действительно помогает мне понять это. Приветствия. – stephenmurdoch

+1

Да, это довольно стандартный для объектно-ориентированных языков. Здесь действительно существует тройная направленность.Имя «nums» указывает на значение в стеке, которое указывает на объект в куче. Где рубин будет другим, так это то, что это больше: в java и python значение 5 будет в стеке всего 5, но я верю в ruby, это также будет ссылкой на объект в куче. (Возможно, я ошибаюсь, мой рубин слаб ... исправления будут с радостью восприняты!) –

+1

@JonKiparsky: Номера неизменяемы в Ruby. Поскольку нет способа изменить число, вы не можете определить разницу между тем, будет ли он выделен как объект в куче или напрямую выделен в стеке. Фактическая правда: ни! Большинство реализаций Ruby оптимизируют малые целые числа (для реализаций C, которые были бы целыми числами, которые вписываются в одно машинное слово - 1 бит, для 64-разрядного JRuby) полностью, так что они представлены * внутри * указателем (также называемым указателем указателя) , –

1

поведение точно такое же с Ruby:

def foo() 
    nums = [1,2,3] 
    bar(nums) 
    p(nums) 
end 

def bar(numbers) 
    numbers.pop 
end 

foo() 
# prints [1, 2] 

DEMO

Вы не можете изменить ссылку локальной переменной для ссылки на другую переменную. Но вы можете вызвать метод, который изменяет состояние на месте. list.pop является одним из таких методов. (То же самое для Array#pop в Ruby)

+0

Христос, вы правы! Я просто ожидал, что Ruby будет другим, но достаточно, чтобы оба языка сделали это. – stephenmurdoch

+1

@marflar, до/после вызова 'bar (nums)', попробуйте напечатать идентификатор 'nums':' print (id (nums)) '. Это не изменится. – falsetru

+2

Да. Мне нужно лечь, все, что я думал, что я знаю, неправильно – stephenmurdoch

0

это своего рода странно, и я не уверен, если это ответ на вопрос, но если вы запустите это:

def foo(): 
    nums=[1,2,3] 
    print "foo" 
    print locals() 
    print globals() 
    bar(nums) 
    print "foo-after" 
    print locals() 
    print globals() 
    print(nums) 


def bar(numbrs): 
    print "bar" 
    print locals() 
    print globals() 
    numbrs.pop() 
    print "bar-after" 
    print locals() 
    print globals() 

foo() 

выход:

>>> foo() 
foo 
{'nums': [1, 2, 3]} 
{'bar': <function bar at 0x024759B0>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x024593B0>, '__doc__': None} 
bar 
{'numbrs': [1, 2, 3]} 
{'bar': <function bar at 0x024759B0>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x024593B0>, '__doc__': None} 
bar-after 
{'numbrs': [1, 2]} 
{'bar': <function bar at 0x024759B0>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x024593B0>, '__doc__': None} 
foo-after 
{'nums': [1, 2]} 
{'bar': <function bar at 0x024759B0>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', 'foo': <function foo at 0x024593B0>, '__doc__': None} 
[1, 2] 
+0

Спасибо за это! – stephenmurdoch

Смежные вопросы