I learnt that mutability of an object is defined with respect to its state but not with its identity.
Below program changes state of a locally scoped object referred by name count within function hailstone().
def hailstone(n):
count = 1
"""Print the terms of the 'hailstone sequence' from n to 1."""
assert n > 0
print(n)
if n > 1:
if n % 2 == 0:
count += hailstone(n / 2)
else:
count += hailstone((n * 3) + 1)
return count
if (__name__ == '__main__'):
result = hailstone(10)
print(result)
After reading the below paragraph of this article, I see that the above code looks fine as regards changing the state of the locally scoped object that name count refers to (i.e. count += hailstone(n / 2)):
Ignore all that. Functional code is characterised by one thing: the absence of side effects. It doesn't rely on data outside the current function, and it doesn't change data that exists outside the current function. Every other “functional” thing can be derived from this property. Use it as a guide rope as you learn.
So, how do I understand the meaning of this statement from this answer:
In functional programming, it is not proper to ever change the value of a variable.
Does functional programming allow changing the state of the locally scoped object referred by name count in my above program?
There is no clearcut definition of functional programming. However, it typically is meant to imply the absence, or at least the minimisation, of side effects -- sometimes people speak of pure functional programming to emphasise their complete absence. But what is a side effect?
A common definition is that a side effect is anything that breaks referential transparency. Referential transparency in turn can be defined in various ways, but perhaps the most enlightening definition is that order of evaluation should be irrelevant. That is, a program is referentially transparent (or pure) if you can simplify any of its subexpressions, in any order, merely by substituting definitions, without changing the outcome of the program. In particular, you can always replace a variable with its (unique!) definition.
Clearly, an assignments to a mutable variable breaks this condition, so it has to be considered a side effect -- even if it is merely local. Likewise a print statement, by the way.
There are ways to have mutable state, or IO, without breaking referential transparency. That is the mystical concept of a monad. But I won't go into that here.
Not knowing Python, I assume count is a "local" variable, not visible from outside, and every invokation of hailstone gets its own instance of count. If this is so, then, as #jonrsharpe explained, it is immaterial what you do to count.
Observe, however, that your code is needlessly verbose. Why not simply:
if n > 1:
if n % 2 == 0:
return 1 + hailstone(n/2)
else:
return 1 + hailstone(n*3+1)
else:
return 1
It turns out that the variable is not needed at all, not to speak of the need to update it.
Your function has no side effects (aside from the print) at the function level. That's a necessary, but not sufficient, condition to be called functional. Functional programs consist of one big expression. Your example is expressions at the function level, interspersed with imperative statements in the function body. An if without an else is a statement, as is a variable reassignment.
That's not to say that an imperative function without side effects isn't better than an imperative function with side effects. There are certainly advantages to your example, even if it isn't considered purely functional.
This program mutates x:
x = 2
x = x * 3
x = x + 4
return x
In functional programming you don't use mutation. Your code should define each variable exactly once:
x1 = 2
x2 = x1 * 3
x3 = x2 + 4
return x3
It is possible that the compiler will allocate the same memory location for x1, x2 and x3, and mutate in place, as the original program does. However, this is an implementation detail and shouldn't be important.
Unlike the previous sequential program, you can think of the new one as a system of equations and read it in arbitrary order. In this case the computation still has to be done top-to-bottom due to data dependency, but this does not have to in general.
You can get rid of the "=" symbol altogether, by using functions. To get rid of "x1 = 2", parametrize the remaining lines by x1 and pass 2 to the function:
(function(x1) {
x2 = x1 * 3
x3 = x2 + 4
return x3
})(2)
and continue this process to remove "x2 = x1 * 3" and "x3 = x2 * 4".
Sometimes you assign to a variable in a loop to accumulate something (e.g. add items in an array for x in A: sum += x). In this case, instead of repeated assignment you can rewrite the program to use functions such as "sum", "reduce", "map", "filter", which are high-level abstractions of loops.
I was lying. It is possible to have local mutable state in a pure functional program, as long as the mutability does not "leak" outside of the scope. In Haskell, there is a library called "ST" for this purpose. It embeds a small imperative language with commands 'create variable', 'read variable', 'write variable', and if your program does not leak a mutable variable, you can retrieve its result in a pure function. However, it is more awkward to use (see this answer) and not used as much as you use assignment in imperative languages.
Related
Suppose we have a function with the following structure:
def f():
# ...
# some computations
# ...
if something_is_wrong_with_previous_computations:
return None
# ...
# some other computations
# ...
if something_is_wrong_with_previous_computations2:
return some_variable
#...
return result
As I see it, using return statement in the middle of a function is not functional at all. If we were in some lispy language we would have the let (then computations could be written with let*) statement, which would help us deal with these situations with ease. Unfortunately, we don't have it here. What should we do?
Simulate let with creating lots of nested functions and calling them in place?
Use something like Maybe monad or another complex stuff like that?
Don't waste our time and write it imperatively?
Something else?
A return statement anywhere in a function is not functional.
In Python, you have no choice.
The Lisp code
(defun sgnum (x)
(cond
((< x 0) -1)
((zerop x) 0)
(t 1)))
turns into Python as
def sgnum(x):
if x < 0:
return -1
elif x == 0:
return 0
else:
return 1
In Lisp, we know we have deviated from functional coding when we use variable assignment, or the "program feature": an explicit progn construct, or an implicit equivalent, or any of is cousins like prog or prog1. A functional function in Lisp always has a body which is made up of a single expression (or possibly no expressions at all).
You can redefine what you mean by "functional coding" in Python. How about these
rules:
Every statement in a "functional function" must be a single statement; it cannot be followed by another statement. Thus, the whole body of a function is a single statement, and in it are embedded single statements.
No statement in the function may allow control to fall through it. Every statement must return. Thus return is not only considered "functional" but essential to achieving this goal.
A variable may be defined, but not redefined. Parallel, mutually exclusive control flows may assign the same variable different values, but no variable can be assigned more than once in the same control flow.
With these kinds of rules, you can get the program to have a control flow graph resembling that a program in the pure Lisp style: a control graph that is basically a tree of decisions with embedded calculations and variable binding, at the leaves of which are values to be returned.
Speaking of variable binding, we should probably have a fourth rule:
A statement may be preceded by a sequence of fresh variable assignments that contain no side effects. Such a sequence, together with the statement which follows it, counts as one statement.
Arguably also a fifth one:
No statement must be used which evaluates any contained expression or statement more than once.
Otherwise we permit loops, which are not functional. This is tricky because some looping constructs are relatively well behaved, like implicitly stepping a dummy variable over the elements of a list. The only way you can tell it's not functional is that a lexical closure captured in the loop will easily reveal there is only one variable being mutated and not a fresh variable being bound for each iteration.
According to these rules, sgnum is "functional": it contains just one if/elif/else statement, which does not allow control to fall through it: every branch returns:
This version of sgnum is not "functional" any more:
def sgnum(x):
if x < 0:
return -1
if x == 0:
return 0
return 1
It contains three statements in sequence. Whereas the following is "functional" even though it also consists of three statements:
def distance(x0, y0, x1, y1):
xd = x1 - x0
yd = y1 - y0
return math.sqrt(xd * xd + yd * yd)
These meet the rules. The first two statements bind fresh variables, meeting rule 3, so are permitted by 4 to precede a statement. The return statement meets rules 1 and 2. This is very similar to:
(defun distance (x0 y0 x1 y1)
(let ((xd (- x1 x0))
(yd (- y1 y0)))
(sqrt (+ (* xd xd) (* yd yd)))))
Lastly, note how our rules are at odds with the ancient programming advice of "have only one exit point in a function". That little tidbit you may find in some coding conventions is quite anti-functional. To achieve a single point of return in a nontrivial function requires imperative style control flows through multiple statements and/or variable assignments. From the functional point of view, it is a myopic, silly rule; but it makes sense in those contexts where it is recommended, because it can help improve very poorly structured imperative code.
In a C program, inlining a function is a fairly intuitive optimization. If the inlined function's body is sufficiently small, you end up saving the jump to the function and creation of the stack frame, and you store the return value wherever the function's result would have been stored, jumping to the end of the inlined function's "body" rather than long-jumping to the return pointer.
I'm interested in doing the same thing in Python, converting two python functions into another valid python function where the first got "inlined" into the second. An ideal solution to this might look something like the following:
def g(x):
return x ** 2
def f(y):
return g(y + 3)
# ... Becomes ...
def inlined_f(y):
return (y + 3) ** 2
Clearly, in a language as dynamic as Python, this isn't trivial to do automatically. The best generic solution I have come up with is to use dict to capture the arguments passed to the function, wrap the function body in a one-iteration for loop, use break to jump to the end of the function, and replace uses of arguments with indexes into the argument dictionary. The result looks something like the following:
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
_g['return'] = _g['x'] ** 2
break
_g_return = _g.get('return', None)
del _g
return _g_return
I don't care that it's ugly, but I do care that it doesn't support returns from within loops. E.g.:
def g(x):
for i in range(x + 1):
if i == x:
return i ** 2
print("Woops, you shouldn't get here")
def inlined_f(y):
_g = dict(x=y + 3)
for ____ in [None]:
for _g['i'] in range(_g['x'] + 1):
if _g['i'] == _g['x']:
_g['return'] _g['i'] ** 2
break # <-- Doesn't exit function, just innermost loop
print("Woops, you shouldn't get here")
_g_return = _g.get('return', None)
del _g
return _g_return
What approach could I take to this problem that avoids needing to use break to "jump" out of the inlined function's body? I'd also be open to an overall better, generic approach could I take to inline one Python function into another.
For reference, I'm working at the AST (abstract syntax tree) level, so using parsed Python code; clearly, outside of literal values, I don't know what value or type anything will have while performing this transformation. The resulting inlined function must behave identically to the original functions, and must support all features typically available when calling a function. Is this even possible in Python?
EDIT: I should clarify since I used the tag "optimization", that I'm not actually interested in a performance boost. The resulting code does not need to be faster, it just must not call the inlined function while still behaving identically. You can assume that both functions' source code is available as valid Python.
The only reasonable way on source level I see, simplified:
Parse the source into some AST (or just use the built-in AST).
Copy a subtree representing the function's body.
Rename the variables in the subtree, e.g. by adding an unique prefix.
At the call site, replace all passed arguments with assignments using the function's new variable names.
Remove the call and replace it with the function body you've prepared.
Serialize the AST back to source.
What poses real problems:
Generator functions; just don't inline them.
Returns from under try/finally that need to run the finally part. Might be pretty hard to rewrite correctly; imho, best left in-unlined.
Returns from under context managers that need to run the __exit__ parts. While not impossible, it's also tricky to rewrite preserving the semantics; likely also best left un-inlined.
Mid-function returns, especially from within multiple loop constructs. You might need to replace them with an extra variable and thread it into every condition of every while statement, and likely to add a conditional break to for statements. Again, not impossible but likely best left un-inlined.
Probably the closest analog to a return would be raising an Exception, which would work to pop out of nested loops to the top of the "inlined function".
class ReturnException(Exception):
pass
g = dict(x=y + 3)
try:
for j in some_loop:
for _g['i'] in range(_g['x'] + 1):
if _g['i'] == _g['x']:
raise ReturnException(_g['i'] ** 2)
except ReturnException as e:
_g['return'] = e.message
else:
_g['return'] = None
I don't know how much overhead is associated with exceptions though or if that would be faster than simply calling the function.
I'm trying to write a python function in a functional way. The problem is I don't know, how to transform an if conditional into a functional style. I have two variables: A and C, which I want to check for the following conditions:
def function():
if(A==0): return 0
elif(C!=0): return 0
elif(A > 4): return 0
else: someOtherFunction()
I looked at the lambda shortcircuiting, but I couldn't get it to work.
I thank you in advance for your help!
From the link you posted:
FP either discourages or outright disallows statements,
and instead works with the evaluation of expressions
So instead of if-statements, you could use a conditional expression:
def function():
return (0 if ((A == 0) or (C != 0) or (A > 4)) else
someOtherFunction())
or, (especially useful if there were many different values):
def function():
return (0 if A == 0 else
0 if C != 0 else
0 if A > 4 else
someOtherFunction())
By the way, the linked article proposes
(<cond1> and func1()) or (<cond2> and func2()) or (func3())
as a short-curcuiting equivalent to
if <cond1>: func1()
elif <cond2>: func2()
else: func3()
The problem is they are not equivalent! The boolean expression fails to return the right value when <cond1> is Truish but func1() is Falsish (e.g. False or 0 or None). (Or similarly when <cond2> is Truish but func2 is Falsish.)
(<cond1> and func1())
is written with the intention of evaluating to func1() when <cond1> is Truish, but when func1() is Falsish, (<cond1> and func1()) evaluates to False, so the entire expression is passed over and Python goes on to evaluate (<cond2> and func2()) instead of short-circuiting.
So here is a bit of interesting history. In 2005,
Raymond Hettinger found a similar hard-to-find bug in type(z)==types.ComplexType and z.real or z when z = (0+4j) because z.real is Falsish. Motivated by a desire to save us from similar bugs, the idea of using a less error-prone syntax (conditional expressions) was born.
There's nothing non-"functional style" in your current code! who said conditionals are not functional anyway? Practically all functional languages have a conditional operator of some sort, for instance the cond special form in Lisp.
I'd take issue with the code if it were using the assignment operator, or mutating state in some way (say, appending to a list) but as it is, the function in the question is already in a "functional style" - there are no state changes.
Perhaps you meant something like this?
return A != 0 and C == 0 and A <= 4 and someOtherFunction()
The above will return False if either A == 0 or C != 0 or A > 4, in all other cases it will return the value of calling someOtherFunction(). And by the way, False can be assumed to evaluate to 0 (for example, 42 + False == 42), so the semantics in the code in the question will be preserved from the caller's point of view.
Notice that you're taking the information in the link out of context. There's absolutely no need to use a lambda for this, the article is only explaining how to get around an inherent limitation of lambdas in Python, which is that you can't return statements inside (like if-elif-else) - only expressions are allowed, but you can fake them with boolean operators. In the context of a normal function by all means, use conditionals.
Although Peter Norvig is a really great guy, his website is pretty hard to search.
I remember reading about Can I do the equivalent of (test ? result : alternative) in Python? on his site a while back during some research before a functional Python talk.
I'm not going to sway you one way or the other in light of my findings, but you should still go and read the section about ternary conditional operators in a functional style.
def if_(test, result, alternative=None):
"If test is true, 'do' result, else alternative. 'Do' means call if callable."
if test:
if callable(result): result = result()
return result
else:
if callable(alternative): alternative = alternative()
return alternative
Just use it as you have it.
Python does not have the syntax and library built-ins to make it easy to program all functions to directly return a single expression. That's not the most important part of functional style anyway, the most important part is making sure that your functions maintain referential integrity. Basically this means that whenever you supply them with the same input values they return the same output.
So when trying to program functionally in Python, I do not refrain from using statements entirely. I use a block of local variable assignments as an equivalent of let ... in ... from Haskell. I use an if/elif/else chain as an equivalent of a case expression from Haskell. And often there are built-in types which do not provide an adequate interface to create new "modified" versions of them rather than updating them in-place, so you instead have to implement such operations with an explicit copy operation and then using mutations on the new copy.
Python allows you to implement functional-style designs directly. You can easily structure your program as a whole bunch of functions which explicitly pass state around and don't have side effects, so you can design your high level algorithms in a very similar way that you would in a functional programming language. Nearly every programming language supports functional programming in this sense, if you're prepared to stomach the boilerplate necessary to fake first class functions. Since Python has first class functions, you don't even have to put up with boilerplate.
But that's as far as Python goes in supporting functional programming. It does't really support implementing functions as single referentially-transparent expressions. But that doesn't really matter. In a language that doesn't enforce or track purity, you get pretty much all the benefits of functional programming that you can by simply designing your program as a bunch of referentially transparent functions, and then how you implement those functions doesn't actually matter as long as the interface is kept referentially transparent.
I have a question about the map function in Python.
From what I understand, the function does not mutate the list it's operating on, but rather create a new one and return it. Is this correct ?
Additionally, I have the following piece of code
def reflect(p,dir):
if(dir == 'X'):
func = lambda (a,b) : (a * -1, b)
else:
func = lambda (a,b) : (a, b * -1)
p = map(func,p)
print 'got', p
points is an array of tuples such as: [(1, 1), (-1, 1), (-1, -1), (1, -1)]
If I call the above function in such a manner:
print points
reflect(points,'X')
print points
the list points does not change. Inside the function though, the print function properly prints what I want.
Could someone maybe point me in some direction where I could learn how all this passing by value / reference etc works in python, and how I could fix the above ? Or maybe I'm trying too hard to emulate Haskell in python...
Thanks
edit:
Say instead of p = map(func,p) I do
for i in range(len(p)):
p[i] = func(p[i])
The value of the list is updated outside of the function, as if working by reference. Ugh, hope this is clear :S
You misunderstand how references work in Python. Here, all names are references, there are no "values". Names are bound to objects. But = doesn't modify the object that's pointed to by the name — it rebinds the name to a different object:
x = 42
y = x
# now:
# 'is' is a identity operator — it checks whether two names point to the
# exact same object
print x is y # => True
print x, y # 42 42
y = 69
# now y has been rebound, but that does not change the '42' object, nor rebinds x
print x is y # => False
print x, y # 42 69
To modify the object itself, it needs to be mutable — i.e. expose members that mutate it or have a modifiable dict. The same thing as above happens when you rebind p — it doesn't touch points at all, it simply modifies the meaning of local p name.
If you want to simulate C++-like references, you need to encapsulate the object into a mutable container, e.g. a list.
reflect([points], 'X')
# inside reflect:
p[0] = ...
But you shouldn't, at least in this case — you should just return the new object instead.
points = reflect(points, 'X')
# inside reflect, instead of p = ...
return map(func, p)
Well, now that I think about it, you can also do
p[:] = map(func, p)
But again, returning new object is usually better.
The data model of Python is based on a trilogy:
identifier - reference - object
.
The identifier is a string written in the code.
The reference is a variable stricto sensu, that is to say "a chunk of
memory whose content can change". The value of a reference is the adress of the object.
The object has an
implementation based on the structures of the langage C that is the
foundations of Python.
Other words are also used to designate the 'identifier' :
1) name
2) variable ; because this word is used by metonymy in mathematics to designate the symbols that represent the real mathematical variables, and the variables in a computer have conceptually the same functioning as the mathematical variables (their values can change).
This use in Python is a very bad habit in my opinion : it creates ambiguities and confusion with what is called 'variable' in computer science: "chunk of memory whose content can change".
The best is to use the word : identifier
.
An identifier and an object are binded in a certain namespace. Namespaces are displayed under the form of Python's dictionaries, but they ARE NOT dictionaries.
The binding of the identifier and the object is indirect, via the reference.
The binding of the identifier and the reference is direct, and realized in the SYMBOL TABLE (or symbol-table).
In computer science, a symbol table is a data structure used by a
language translator such as a compiler or interpreter, where each
identifier in a program's source code is associated with information
relating to its declaration or appearance in the source, such as its
type, scope level and sometimes its location.
http://en.wikipedia.org/wiki/Symbol_table
They say: identifiers. Precisely.
I rarely see allusions to symbol table, though it's the crucial thing that sheds light on the functioning of the Python's data model IMO.
In my opinion, the word
binding
doesn't designate a precise and unique mechanism but globally the set of all the mechanisms concerning the trilogy identifier - reference - object
.
I dont' pretend that I perfectly understood all the matters concerning the data and execution models of Python, and that the above considerations are the more exact and precisely expressed that can be done.
However, they allow me to have an operational understanding of what happens during executions of code.
I would be very happy to be corrected on some points if I am wrong (for exemple, I am very satisfied to have learnt from Michael Foord that the nature of namespaces is not being dictionary, which is only the way they are represented)
.
That said, I don't know what is called value and reference when the subject of passing something as argument in Python is discussed, and I have the impression that a lot of the persons that have expressed on the subject in numerous esoteric discussions don't know more than me.
I think that there is no best and lucid opinion on the subject that this one of Alex Martelli:
"Trying to reuse terminology that is more generally applied to
languages where "variables are boxes" to a language where "variables
are post-it tags" is, IMHO, more likely to confuse than to help."
Alex Martelli
http://bytes.com/topic/python/answers/37219-value-reference
I want to bump this. The answers don't really answer the question - they disregard the fact that the OP was able to achieve the desired result by iterating. The question comes down to the behavior of map. Here is a more direct example:
f=(lambda pair: pair[0].append(pair[1]))
a = ([],1)
f(a)
print(a) #prints ([1],1)
a=([],1)
map(f,[a])
print(a) #prints ([0],1)
So map isn't mutating objects in the way the OP is expecting. I have the same confusion.
Can anyone comment on exactly what is going on here? I think that'd be a good answer to the OP's question.
Note that we have different behavior if we assign the output of map as follows (as per Cat Plus Plus' answer)
f=(lambda pair: pair[0].append(pair[1]))
a = ([],1)
x = [a]
x[:] = map(f,x)
print(x) #prints None
print(a) # prints [1]
Please note that in the first example, we simply called f(a), not a=f(a). Why do we need assignment when using map and not when working outside of map?
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.
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
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. :)
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.
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.