1

This probably fits more a discussion group, but I'm not proficient in the innards of the language (or even the language itself). In any case, what's bugging me is:

If python is allowing interference (side effects) with outer scope using the nonlocal keyword, then why does it not allow a similar interference with function arguments by permitting passing arguments by reference:

Possible right now:

>>> def outer():
       x = 1
       def inner():
           nonlocal x
           x = 2
           print("inner:", x)
       inner()
       print("outer:", x)

>>> outer()
inner: 2
outer: 2

Why not - or what could go wrong if we had:

>>> def outer():
       x = 1
       def inner(byref x):
           x = 2
           print("inner:", x)
       inner(x)
       print("outer:", x)

>>> outer()
inner: 2
outer: 2

(using some keyword like 'byref' or 'nonlocal, just for illustration).

0

4 Answers 4

2

Python always passes parameters using reference value. Refer to this link here and especially rad the detailed response provided by the user pepr

Also this link has pretty detailed discussion on how exactly parameters are passed within python and also pass-by-reference can be simulated - refer to the EDIT of the accepted answer to this question.

In case, you want to delve deeper - please refer to this Effbot article that also contains some debate/discussions around the same topic.

Sign up to request clarification or add additional context in comments.

4 Comments

Why did Python designers consider it not a good idea to provide a standard mechanism to allow the function to change the reference target of the outer name.
That's a good question, and my guess is that it was whiners coming from Java(Script) who couldn't handle programming without access to namespace fudging that convinced them to go that route. Note that in real Python (2.7), it definitely is not allowed. I should mention that the lack of keywords in function args is probably aesthetic as much as anything. It's been avoided thus far, Guido's not going to put it in for something as trivial as symmetry across from something as dumb as nonlocal.
@ap Python is older than both Java and JavaScript: its first release was in 1991 (Java and JS were released in 1995). And its semantics are even older, dating back to ABC in the 1980s.
@Daniel That's true! And my suspicion is that, were it not for the relative rise in popularity of these and other languages since that original inception date, 3.x (which came decidedly after them) might not have incorporated the in-my-mind-ugly-and-mostly-useless nonlocal. edit I'm losing internet points over this! help!
1

This is not possible because Python does not have a concept of "variable" at all. All the names in a Python program are references to objects. So you can only pass a reference to an object when calling a function or method. This is called passing by reference value.

However, it is not possible to obtain a reference to an existing reference. In this respect, references are the only "non first class" citizens in the Python world.

There are two kinds of objects referenced by names in a program: immutable (e.g. string, int, tuple) an mutable (e.g. list, set, most user classes).

You can call methods on mutable objects that in fact modify the objects state, and this looks similar to passing a reference to the object in a language such as C++. Beyond that, the passing by reference does not make much sense in Python.

See Prahalad Deshpande's answer for some links.

3 Comments

"Python does not have a concept of "variable" at all." Of course it does. Same as in Java, C, Smalltalk, Ruby, pretty much all other languages.
@newacct No, it does not. It only has reference to objects on the heap. This is fundamental to Python's dynamic type system. If you write x=2 and later x='abc' the reference x will refer to two distinct objects of different types, which may exist independent of the reference and by be referred to by other references.
So? How does this make it not have variables? x is a variable. Its value is a reference, because all values in Python are references. In Java, you can also write Object x = 2; and later x = "abc"; and x will point to two distinct objects of different types. Does that make x in Java not a variable?
1

Suppose you do allow reference arguments. What happens, then, when you do the following?

def f(byref x):
    print x
    print x
    x = 3

class Foo(object):
    def __init__(self):
        self.count = 0
    @property
    def x(self):
        count += 1
        return count

f(Foo().x)

When is the getter called? How many times is it called? Does this print 1 twice, or does it print 1, then 2, or 2, then 3? Since there's no setter, what happens when you try to assign to x?

In another language, where variables are places to put things instead of names for values, this wouldn't be a problem. Foo().x would have a memory location once evaluated, and f would use that memory location for its x argument. In Python, it doesn't work like that. You can't pass in the memory location of 1 and change its contents, because then other code will find that 1 is magically 2 all of a sudden. Python's name semantics and attribute/item getter/setters make reference arguments even more confusing than in languages like C++, where they're already confusing and bug-prone.

3 Comments

Byrefs can be limited (in concept) to immutable types because that's where they are useful. This will cut the convoluted side effects and unnecessary semantics (e.g refs to lists and dicts). Can't say though if such a check is possible/reasonable according to Python internals.
@BaselShishani: The problem there is that variables don't have types. Values have types. If you pass 3 by reference, that offers no benefits over what Python already does, since you can't turn that 3 into a 4. If you pass an lvalue that evaluates to 3 by reference, then you either have to restrict all reference arguments to simple variables (and lose much of the power of reference arguments), or you have to deal with nasty getter/setter semantics.
"In another language, where variables are places to put things instead of names for values," There are other languages with both properties and pass-by-reference. For example, C#. C# disallows you to pass a property by reference. The difference between C# and Python is that in C#, attributes and properties are declared so you know when you see a dot syntax whether it's a property; whereas Python is much more dynamic and all attribute access is through methods like .__getattr__() and .__setattr__(), so it is much harder to check. I think that's really what you're trying to say.
0

In Python int objects are immutable. When you think you are changing the value of x, you are really creating a new int at a different memory location and getting x to reference that.

6 Comments

okay fine, so this is the internal working of it, and there are few questions here on SO discussing that. But what's preventing us from using the same technique to simulate passing by reference - i.e create a new object and let the inner function alter the reference of the outer name? Similar to what nonlocal is doing.
@BaselShishani, nothing. For example you can pass x = [1], and then say x[0] = 2, but you can't hide the fact that x isn't really an int anymore
@BaselShishani This is much more than an implementation detail. It's the fundamental concept of how Python treats references to its objects. This is very different from the concept in languages such as C++ or Java.
In java you also always pass references by value. In C too. My feeling is that the number of languages that allow pass by modifiable references are actually in minority
@downvoter. You can downvote all you like but it's not going to change how Python references work. I save my downvotes for answers that are wrong.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.