2

I believe I know the answer to this, but wanted to double-check because I find this a bit confusing.

def outerFunc():
    mySet = set()
    a = 0
    def innerFunc():
        mySet.add(1)
        mySet.add(2)
        a = 7
    innerFunc()
    print(mySet) # {1, 2}
    print(a) # 0

Here, if I want to change the value of a, I need to use nonlocal. The fact that the set changes is just because sets are passed by reference? So, in an inner function we have access to the values of the outer function's variables, but cannot modify them unless they're references?

adinutzyc21
  • 1,538
  • 1
  • 11
  • 23
  • 2
    It's not really anything special about `set`s. You simply can't reassign the variable without using `nonlocal`. If you tried to do `mySet = {1, 2}`, that wouldn't work either without using `nonlocal`. – rchome Dec 17 '21 at 04:14
  • 2
    @GlennEdstrom no. Mutability is *irrelevant*. The relevant detail is assignment. – juanpa.arrivillaga Dec 17 '21 at 04:58
  • "The fact that the set changes is just because sets are passed by reference? " *no, nothing is ever passed by reference in Python*. The evaluation strategy is *always the same*, regardless of type. – juanpa.arrivillaga Dec 17 '21 at 04:59
  • Probably a good idea to read this: https://nedbatchelder.com/text/names.html – juanpa.arrivillaga Dec 17 '21 at 05:06
  • 1
    So in a nutshell, **assignment is always automatically local**. That means that an assignment statement (anywhere in a function) will mark that variable as local (by the compiler!) *unless you use `nonlocal` or `global`*. The types of the objects involved do not matter, as stated by @rchome you could use `mySet = {1, 2}` and you'll get the same behavior as with `a = 7`. However, you can *refer* to a variable from an enclosing scope, and the normal lookup rules are respected, without having to declare anything. – juanpa.arrivillaga Dec 17 '21 at 05:09
  • 1
    So `mySet.add(2)` works because there is no assignment, and it finds the closest enclosing scope and correctly resolves to the set you defined in `outerFunc`. And again, *the type of the object being referred to is irrelevant*. You can do `print(a.as_integer_ratio())` in `innerFunc` and it will correctly resolve to the `int` object in `outerFunc`. Of course, none of the `int` methods change an `int`'s value -- i.e. `int` objects are immutable (they *lack mutator methods*). – juanpa.arrivillaga Dec 17 '21 at 05:13
  • 1
    As for Python's evaluation strategy, it is neither call by reference nor call by value. The name is actually "call by sharing", although this is an obscure term. it was coined by Barbara Liskov (of the Liskov Substitution Principle!) for the language CLU, which was very influential in the development of OOP. Note, in the Java community, they say "Java is call by value but the value is an object reference", which is just confusing the implementation for the semantics, and also, it isn't purely object-oriented, since Java has a distinction between "primitive" types and "reference" types. – juanpa.arrivillaga Dec 17 '21 at 05:21

1 Answers1

3

You can check the python document

In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global.

So if you assigned a variable and the variable without global just affects the local. For example, if you assigned value to mySet, then it also does not change.

def outerFunc():
    mySet = set()   
    def innerFunc():
        mySet = {1}
        mySet.add(2)
    innerFunc()
    print(mySet)  # ''
Ben
  • 141
  • 8