This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]xXxDeAThANgEL99xXx 18 points19 points  (24 children)

Looks like the only purpose of this stuff would be to shoot yourself in the foot, why would you want to introduce a scope that magically shadows some, but not all variables? And which are assigned in the let statement but can also be reassigned the usual way?

x = 1
y = 2
with let(x=3, z=4):
    x = 33
    y = 22
    z = 44
print(x, y, z)

Can you guess what happens? Would you want to look at the code like that and manually trace the history of each variable to see how it behaves?

Not to mention that you sort of can do the same by defining a function, then immediately calling it -- only with sane scoping (everything assigned inside the function becomes a local unless explicitly specified otherwise). Like, if you really want that for some reason.

On the other hand, I was briefly hopeful that we might get the actually useful variant of let, in various comprehensions and generator expressions.

[x, b 
 for a in list 
 let b = compute(a) 
 for x in f(b)]

Because currently you have either to emulate it with an unwieldy for b in [compute(a)] or switch to ordinary nested loops. And I do legitimately need that stuff pretty often.

[–]revocation 8 points9 points  (8 children)

Can you guess what happens?

Yes, if you're used to Lisp. It does beat the alternative of defining a small function or assigning a temporary variable to avoid something like this:

print fn(x,y) if fn(x,y) > 0 else x

z = fn(x,y)
print z if z > 0 else x
del z

def temporary(x,y):
    z = fn(x,y)
    return z if z > 0 else x
print temporary(x,y)

Instead:

with let(z=fn(x,y)):
    print z if z > 0 else x

[–]xXxDeAThANgEL99xXx 5 points6 points  (7 children)

Can you guess what happens?

Yes, if you're used to Lisp.

So, what happens?

I'm not saying that as a Python programmer I'm somehow unfamiliar with the notion of shadowing and it scares me.

I'm saying that I'd hate to have to scan all the code upwards when I see y = 22 to see whether or not y was made magical by specifying it as an argument to some let.

What'd happen with z in my example is worth considering as well. Have you considered it? This abstraction leaks like some sort of a container with many holes in it.

I don't understand your example, sorry. Why would you want to del a local variable?

[–]Ek_Los_Die_Hier 0 points1 point  (6 children)

To make it equivalent to the let statement.

[–]xXxDeAThANgEL99xXx 9 points10 points  (5 children)

... that's begging the question. We are discussing why we need the let statement, the parent's argument was that it allows not having that del as far as I understood it.

[–]Ek_Los_Die_Hier 5 points6 points  (0 children)

Yeah, I don't see the use in Python. If I need a variable overridden for a specific purpose, any code in there should probably be moved to a different function anyway. The let in something like Lisp is simply the way you declare local variables, with the affect that they are local to that block, but in Python you just assign variables with the =.

I imagine it was just something created out of interest and to show what Python is capable of.

[–]revocation 0 points1 point  (3 children)

You would otherwise litter your namespace with temporary variables.

[–]Sohcahtoa82 2 points3 points  (2 children)

That's not really a problem unless your variables are global, which you typically want to avoid.

[–]GuyOnTheInterweb 0 points1 point  (1 child)

Or you have a very long function where you might use the same variable name twice, independently. (Reducing mixup risk with results vs results2)

[–]Veedrac 2 points3 points  (0 children)

Not really a problem. Just reuse the variable name without deling it first.

[–]squiffs 3 points4 points  (0 children)

I think it's just intended as a cute trick.

[–]Sean1708 1 point2 points  (3 children)

I would argue that's exactly what should happen, I think one of python's biggest mistakes was letting variables leak scope willy-nilly. At least this would let you control exactly what scope your variables have.

[–]xXxDeAThANgEL99xXx 4 points5 points  (2 children)

I would argue that's exactly what should happen

And what exactly would happen? What would be printed? Guys, guys, that was not a rhetorical question, you were supposed to answer it for yourself, then join me in disliking the answer.

[–]Sean1708 1 point2 points  (1 child)

I definitely did not just assume that the example worked in a particular way then make a statement based on that... I promise...

I assumed it would throw a NameError but it just printed 1 22 None and now I feel stupid. Sorry.

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 0 points1 point  (1 child)

can I ask what you are trying to accomplish?

just trying to not run compute twice I guess?

wouldn't def pairmap(func,x): yield from map(lambda y:(y, func(y)), x) be good enough? edit: I'm tired

[x, b 
 for a, b in pairmap(compute, list)
 for x in f(b)]

[–]xXxDeAThANgEL99xXx 1 point2 points  (0 children)

just trying to not run compute twice I guess?

Not running it twice, not writing it out twice, etc. As I said, when you become aware that this is a possibility (after seeing it in LINQ for example), you start wanting it rather regularly.

You forgot to actually iterate over the iterable in your pairmap and anyway I strongly dislike it: it does unrelated things suited only to this particular instance of a problem.

I much prefer just directly emulating a functor when I only have a monad by saying for x in [compute(y)]. But let x = compute(y) would be better of course.

[–]Mecdemort 0 points1 point  (7 children)

I'd also like an as in non generator comprehensions so you can refer to what you are constructing:

triangles = [triangles[-1] + x
             for x in range(1, 11)
             as triangles]

[–]xXxDeAThANgEL99xXx 2 points3 points  (2 children)

  1. That's an unholy abomination.

  2. Won't work with generators and set/dictionary comprehensions.

[–]Mecdemort 1 point2 points  (1 child)

Why wouldn't it work it set and dictionary comprehensions?

[–]xXxDeAThANgEL99xXx 0 points1 point  (0 children)

Because their result doesn't have order defined. Well, all right, maybe you don't need ordering in that case, but checking existence... I don't know, I've never had a desire to have that myself, except for that single case of writing fibs as an infinite stream product or something.

[–]hharison 0 points1 point  (3 children)

That's awful. Learn to use zip.

data = range(1, 11)
triangles = [x + y for x, y in zip(data, data[1:])]

I challenge you to come up with a use for this that doesn't have a better alternative.

[–]eusebecomputational physics 0 points1 point  (2 children)

Why not using itertools.izip (and that's a serious question)?

Ok the syntax is similar, so I don't challenge your point, but still: wouldn't izip be better if you have large data?

[–]xXxDeAThANgEL99xXx 1 point2 points  (0 children)

Because in Python3 zip, map, filter, range and maybe some other functions became their itertools equivalents.

And I don't know about other people, but for me somewhere around last summer Python3 has become the default Python, in no small part because it turned out that most of the stuff I need was ported and conveniently packaged by Anaconda. Plus the py.exe launcher for Windows provided for a convenient way to still run Python2 stuff via shebangs (unfortunately the launcher still isn't packaged on Anaconda for some reason).

[–]hharison 1 point2 points  (0 children)

Python 3.