5 ms·
In some other Lisps, yes, but Python has its own idea of how function scoping works that generally isn't what you'd want with `let`. For example, in (let [
by Kodiologist 4y ago
In some other Lisps, yes, but Python has its own idea of how function scoping works that generally isn't what you'd want with `let`. For example, in
(let [a 1]
(defn foo []
(setv a 2))
(foo)
(print a))
you probably `2` to be printed, but the seeming equivalent Python code
a = 1
def foo():
a = 2
foo()
print(a)
prints `1`. Python interprets the assignment as declaring a new local variable. Frankly, Python got scoping wrong (in its quest to eliminate declarations), but here we are. So `let` needs to work around this somehow. The current implementation implicitly adds `global a` or `nonlocal a` as appropriate. (Actually, the name `a` is also mangled so that any references to `a` outside the `let` refer to a different variable.)
- ashtonbaker 4y agothis made me wonder - I know I can use global variables inside a function definition: a = 1 def foo(): x = a foo() so if this 'a' is a new variable, what happens when the name collides with a global variable? a = 1 def foo(): a = a foo() > UnboundLocalError: local variable 'a' referenced before assignment yuck!
- all2 4y agoI'm not sure this is so much of a problem? Changing what you name the variable fixes the issue. Python is generally quite good at having a verbose option to express things, so I'm surprised there's not an explicit way to reference the enclosing scope from an object.
- vegai_ 4y agoIs there any non-horror piece of code where supporting this latter case would be useful?
- m55au 4y agoIt is not a particularly good example. A better one would be a = 1 def foo(): x = a a = 2 foo() > UnboundLocalError: local variable 'a' referenced before assignment Basically a name cannot be both global and local inside the same function. Otherwise it would be a total mess. "a = a" just means assign the value of local variable "a" to a local variable "a", so the error makes sense.
- gilch 4y agoIt works if you disambiguate your namespaces: >>> a = 1 >>> def foo(): ... a = globals()['a'] ... print(a) ... >>> foo() 1 Still yuck?
- gilch 4y agoHere's another way to do it: >>> def foo(): ... import __main__ ... a = __main__.a ... print(a) ... >>> foo() 1
- m45t3r 4y agoFor me the example printing `1` makes sense because I think `let` as being something immutable, so I wouldn't expect `setv` to mutate that variable (would expect either a compiler error or the introduction of a new local variable). But maybe it is because I am familiar with Clojure (where `setv` or equivalents doesn't exist) and not other Lisps. This would be the equivalent Clojure code anyway: user=> (let [a 1] (defn foo [] (def a 2)) (foo) (println a)) 1 nil So yeah, makes sense for me. But can understand why some other people would expect the other way though.
- gilch 4y agoEquivalent in Common Lisp: * (let ((a 1)) (defun foo () (setq a 2)) (foo) (print a)) 2 2
- baq 4y agoit kinda sorta works when wrapped in a function def f(): a = 1 def g(): nonlocal a a = 2 g() print(a) though nonlocal can't be used when referring to global variables... it's kinda bad.
- gilch 4y agoYou can still use `global` though.