tags:

views:

197

answers:

4

Hi, I wonder if there is a good way to bind local variables in python. Most of my work involves cobbling together short data or text processing scripts with a series of expressions (when python permits), so defining object classes (to use as namespaces) and instantiating them seems a bit much.

So what I had in mind was something like in (common) lisp, where you could do something like

(setq data '(1 2 3))
(setq output 
      (let ( (x (nth 2 data)) )
       x + x))

In python, the best I could come up with is

data = [1,2,3]
output = ((lambda x: x + x)
          (data[2]))

These are, of course, very simple examples but might there be something that is as scalable as let or let* in lisp? Are defining classes the best way to go to create a local namespace?...(but feels a little less interactive that way)

Edit: So to further explain the intention (my apologies for vagueness), I want to reduce the use of global variables. So in the case above, I meant to use the extraction operator as a general case of any type of operation that might not want to be repeated. For instance, one might write either

output = data[2] + data[2]

or

x = data[2]
output = x + x
del x

to accomplish the same result. In essence, if the desired operation on 'data' is more complicated then getting the second item, I wouldn't want to type it out multiple times, or let the computer compute the value of the same expression more times than necessary. So in most cases one would assign the result of the operation, in this case, data[2], or operator.itemgetter(2)(data), to some variable in the global space, but I have an aversion to leaving variables around in the global space if they were only necessary to store intermediate values in a computation... hence the use of the 'del' command immediately afterwards. Defining a local environment or namespace and binding intermediate results to local variables would be an ideal alternative.

+1  A: 

Not really knowing Lisp, I can't see what you're trying to do here. But I would say that in general you should not try to write Python as if it were Lisp, or indeed any language as if it were any other language. I've been programming in Python for five years and I've never seen a need to do what you're trying above.

Can you give an example of a use case for the above - what are you actually trying to do, in terms of the end result? Maybe then we can advise you on the best way to do it in Python, rather than Lisp.

Daniel Roseman
+2  A: 

It's a bit unclear what you are asking, bit I'll try to answer anyway:

You bind variables to names with = in Python. So your data = [1,2,3] binds the list [1,2,3] to the name data.

You can create local namespaces with classes and functions/methods.

The closest you get so something as powerful as let is probably def and lambda. Python is (despite where some people try to tell you) not Lisp, and not particularly functional, so you will have to adapt your mindset a bit.

Update: Ah, I see what you mean now.

All variables are pretty much local in Python. The nearest you get to global variables are variables defined in module space, because you can access them with from <module> import <variable>. You also can access them from wherever in the module, but not modify them (unless you say that you want to modify them with the global keyword. Anything you define in a function/method or class definition, will only be accessible from that namespace.

So in short: you don't have to worry about the things you worry about now. Python takes care of it for you. :)

Lennart Regebro
I would even say that Python tries as hard as it can not to be a Lisp.
Svante
Hm, I didn't think about it that way! but I guess in the end a variable not enclosed in a function or module could be either overwritten or the wrong (unexpected) value can be called further down in the script? Tough one... Thank you very much though!
Stephen
+1  A: 

I can only second Lennart and Daniel - Python is not Lisp, and trying to write language X into language Y is usually inefficient and frustrating at best.

First point: your example code

data = [1,2,3]
output = ((lambda x: x + x)
          (data[2]))

would be much more readable as:

data = [1, 2, 3]
output = (lambda x=data[2] : x +x)()

but anyway, in this concrete case, using a lambda is total overkill, overcomplexificated, and mostly inefficient. A braindead

output = data[2] + data[2]

would JustWork(tm) !-)

Now wrt/ to local bindings / namespaces, the usual solution is to use... functions - eventually nested. While 100% object (as in "everything is an object"), Python is not pure object, and plain functions are just fine. FWIW, even for "scripts", you should put your logic in a function then call it - function's local namespace access is faster than "global" (really: module level) namespace access. The canonical pattern is

import whatever

def some_func(args):
    code_here

def some_other_func(args)
    code_here

def main(args):
    parse_args
    some_func(something)
    some_other_func(something_else)
    return some_exit_code

if __name__ == '__main__'
    import sys
    sys.exit(main(sys.argv))

Note also that nested functions can also access the enclosing namespace, ie

def main():
    data = [1, 2, 3]
    def foo():
       x = data[2]
       return x + x
    print foo()
    data = [4, 5, 6]
    print foo()
    # if you want the nested function to close over its arguments:
    def bar(data=data):
       x = data[2]
       return x + x
    print bar()
    data = [7, 8, 9]
    print bar()

HTH

bruno desthuilliers
Thanks! Yes - I know the lexical scoping rules of Python since 2.4(?)... but was hoping I could do a lot more without named functions. But thank you for the illustrations.
Stephen
Python's support for functional programming is indeed very restricted - but hey, Python doesn't pretend to be a functional language.
bruno desthuilliers
+1  A: 

You could combine a function decorator and default parameters to get something like let and block scoped variables:

def let(func):
    return func()


data = [1,2,3]
@let
def output(x=data[2]):
    return x + x
print(output) # 6

# or if a single expression is enough:
output = let(lambda x=data[2]: x+x)

But this isn't a popular idiom in Python so I advise you avoid it to make your code easier to understand for others. Just use regular local variables:

data = [1,2,3]
x = data[2]
output = x + x

If this becomes a real problem it's a good sign you are trying to do too much in a single function.

Ants Aasma
Thanks! ... but the "regular local variables" you mention aren't really local unless they are defined within the scope of a function or - something like a let() environment...
Stephen
Good point, I'm so accustomed to putting everything in functions that I assumed implicitly that the code is to be executed in a function. I find it to be good practice to have only declaration constructs at the top level. A good rule of thumb would be that if the scope of variables becomes a problem, create a helper function.
Ants Aasma