5 ms·
It is funny to see Python in an example of 'good' variable scoping. How exactly would you refer to the outer 'a' in that python example?
by james2vegas 13y ago
It is funny to see Python in an example of 'good' variable scoping. How exactly would you refer to the outer 'a' in that python example?
- gvalkov 13y agoBy using Python3's nonlocal statement. def outer(): a, b = 1, 2 def inner(): nonlocal a a, b = 3, 4 print('inner a: ', a) print('inner b: ', b) inner() print('outer a: ', a) print('outer b: ', b) outer() # inner a: 3 # inner b: 4 # outer a: 3 # outer b: 2 I find Python's scoping rules to be quite sane.
- mattdeboard 13y ago``print a``? The inner `a` is the same reference as the outer `a`. a = 1; def foo(): b = a; return b foo() is a >> True This gets hairy when referring to mutable types: a = [] def foo(): b = a; return b foo().append(1) a >> [3] Compare to immutable types: c = () def bar(): d = c; return d bar() + (1,) c >> ()
- agumonkey 13y agoI understand the context, but isn't it unfair to compare `append` and `+` ? Python is not clojure or sml, I think people assume mutability, and using foo() + [1] will consistently build fresh objects in both cases.
- mattdeboard 13y agoNo, I don't think it's unfair because "fairness" has nothing to do with anything. I was simply illustrating how using a mutable data structure defined in an outer scope can bite you.
- agumonkey 13y agoFair enough. May I mention the default parameter bite too. It's hard to notice at first.
- picomancer 13y agoThe "global" keyword in Python binds a name to the scope of the top level of the enclosing module. The "nonlocal" keyword is a Python 3.x feature which binds a name to the next enclosing scope level. So you would do: a = 1 def contrived(): global a # I added this a = 2 return a contrived() # 2 a # 2 The "nonlocal" keyword would give the same result if the outer scope is the top level of the module. If the entire above code is itself enclosed in a function like so: def enclose(): a = 1 def contrived(): nonlocal a a = 2 return a contrived() # 2 a # 2 You need "nonlocal" to refer to the "a" that's a local variable in enclose(). I.e. "global" gets you the top level, "nonlocal" gets you the next enclosing level. The keyword is only necessary when you're assigning to an out-of-scope variable. If you remove the assignment statement "a = 2" from the function, the program will correctly read and return the value of a from the enclosing scope. In other words, by default the set of names considered to be local variables in a function is the set of names that are the target of an assignment statement. Usually the default behavior is what you want, but if you wish to override it, use the "global" or "nonlocal" keyword. From a design standpoint, I've learned over the years that global / nonlocal keywords are a "smell" that frequently indicates poor architecture, usually of the sort that the oft-cited "global variables are evil" doctrine is intended to protect against. Code that extensively uses these keywords should usually be refactored into a class, with the formerly global names instead being attributes (member variables).
- cursork 13y agoNot completely seriously.. This is why some of us prefer Perl ;) my $a = 1; my $contrived = sub { my $a = 2; # new lexical variable $a $a = 3; # if the my above wasn't there, outer $a would be 3 ... } say $a; # 1 Explicit declaration of the scope of a variable at initialisation is awesome. 'global' and 'nonlocal' feel weird/less consistent/like a workaround. People are used to creating variables 'on the fly' in Python / CoffeeScript - both break (for a certain value of 'break') certain assumptions in certain circumstances. I've never heard anyone ever complain about perl's lexical 'my'.. It even catches - with a warning - multiple my declarations that will clobber each other. You don't see that in other languages that often.