map/filter/reduce/lambda opinions and background unscientificmini-survey

D

Duncan Booth

Steven said:
Put it this way: whenever I see a two-line def as above, I can't help
feeling that it is a waste of a def. ("Somebody went to all the trouble
to define a function for *that*?") Yet I would never think the same about
a lambda -- lambdas just feel like they should be light-weight.

Obviously we think differently there. I don't see why lambdas are any
different than single expression functions. I certainly don't think of them
as lighter weight; they take just as long to call; and they involve just as
much stack setup/tear down. On the other hand I don't consider functions as
heavyweight, I'm happy to define short helper functions anywhere I think it
makes the code more expressive.
Am I just weird?

No, just different[*]. There's nothing wrong with different.

[*] conclusion based entirely on your postings here. I have no evidence
beyond that.
 
R

Ron Adam

Reinhold said:
Ron Adam wrote:




Whoops! a (which is None) is equal to the empty tuple (which is not None)?

It's not an empty tuple, it's an empty parenthesis. Using tuples it
would be.

(a,) == (,)

which would be the same as:

(,) == (,)
I can't really see any coherent concept here.

Reinhold

It would work out that.

if: == if:

Does that help?

Cheers,
Ron
 
B

Bengt Richter

Steven said:
Put it this way: whenever I see a two-line def as above, I can't help
feeling that it is a waste of a def. ("Somebody went to all the trouble
to define a function for *that*?") Yet I would never think the same about
a lambda -- lambdas just feel like they should be light-weight.

Obviously we think differently there. I don't see why lambdas are any
different than single expression functions. I certainly don't think of them
as lighter weight; they take just as long to call; and they involve just as
much stack setup/tear down. On the other hand I don't consider functions as
heavyweight, I'm happy to define short helper functions anywhere I think it
makes the code more expressive.
Am I just weird?

No, just different[*]. There's nothing wrong with different.

[*] conclusion based entirely on your postings here. I have no evidence
beyond that.
I think def is a form of assignment, with the target binding name
specified inside the expression syntax instead of to the left of an '=' as usual. I.e.,

def f(args): suite

is like

f = def(args): suite

except that I can't use an arbitrary left-hand side in the assignment, such as

MyClass.method = def(self, args): suite
or
somedict['foo'] = def(self, args): suite

Personally, I think def(args): suite ought to be allowed as an expression that you could
put in parentheses like any other expression if you need/want to write it with multiple lines.
Obviously this could both replace and expand the functionality of lambda ;-)

Regards,
Bengt Richter
 
T

Tom Anderson

That's the sort of thing i like to see!
Ok, lets see... I found a few problems with the testing (and corrected
it) so the scores have changed. My sort in place routines were cheating
because the lists weren't reset between runs so it had the advantage
after the first time though of sorting already sorted list.

Aaagh, of course!
And Tom's recursive no copy had a bug which kept a reference to one of
it's arguments so it's output was doubling the list.

Oops. I really should have written tests as well as benchmarks.
And the hasattr function was slowing everyone down, so I inlined it for
everyone who used it. Using a 1000 item list and starting with a flat
list and increasing the depth (unflatten) to shallow, medium, and deep.
(but not so deep to cause recursion errors.)

I wrote a new and improved test harness myself, but left my laptop at
work, and haven't been in today due to the bombs :(. I ran tests out to
100 000 elements, and your implementation was still faster, although not
by a lot - but then i hadn't found the bugs you had, so it's all moot.
And the winners are...

Stians flatten generator is nearly tied with Tom's recursive zerocopy.
My nonrecursive inplace is faster for very shallow lists, but Tom's
quickly over takes it. I was able to improve my nonrecursive copy
flatten a bit, but not enough to matter.

I also came up with an improvement to my version that should cut down on
recursive calls - a sort of recursion unrolling. I doubt it wouldd make
much difference, though.
So Tom's recursive zerocopy is the overall winner with Stian's flatten
generator close behind and just barely winning out in the very deep
catagory.
\o/

But they're all respectable times so everyone wins. ;-)

Everyone shall have medals!

tom
 
R

Ron Adam

Use a descriptive name like this?

def get_the_cube_of_x_and_then_subtract_five_multiplied_by_x(x):
x**3 - 5*x

I think I like the lambda version here. ;-)

It would probably have a name which refers to the context in which it's
used, but sometimes the math expression it self is also the most readable.


Put it this way: whenever I see a two-line def as above, I can't help
feeling that it is a waste of a def. ("Somebody went to all the trouble
to define a function for *that*?") Yet I would never think the same about
a lambda -- lambdas just feel like they should be light-weight.

In the case of an interface module you might have a lot of two like
def's that simply change the name and argument format so several modules
can use it and have a standard consistent or simplified interface.

The lambda may be perfectly fine for that. But why not use def?

func_x = lambda x: (someother_func_x(x,'value'))

def func_x(x): return someother_func_x(x,'value')

There's both nearly identical, but the def is understandable to
beginners and advanced python programs.


Cheers,
Ron

Am I just weird?

Aren't we all? ;-)
 
G

Guest

Tom Anderson said:
That's the sort of thing i like to see!

That can be a neat method. It's a pretty verbose way to do flatten(),
but it's a good example:

def flatten(l):
for e in l:
if isinstance(e, list):
for f in flatten(e):
yield f
else:
yield e

for x in flatten([0, [1, 2, [3, 4], 5], 6, 7]):
whatever()
 
T

Tom Anderson

Which would be called object.del() I presume.

That would be fine.
And that opens a big can of worms.

Suppose we have a list L = [4, 3, 2, 1, 0], what should L.del(1) do?

Delete the item at index 1, just as del L[1] would.
It looks like it should result in L becoming [4, 3, 2, 0].

Why? I guess if you hadn't read the documentation and were just calling
methods at random, you might, but i can't think of any other case.
An easy mistake to make, if you forget that the argument is (presumably)
an index.

Not the kind of thing people tend to forget! There is the problem that
people might get del and remove mixed up; i didn't actually realise there
was a remove method on lists until i looked just now.
You could make it clear by insisting on L.del[1] but that requires a big
change in Python's syntax.

It would, so i wouldn't dream of insisting on it.
What should L.del() do, with no arguments? Raise an error?

Er, exactly the same as calling any other method with a wrong-length
argument list - a TypeError, i should think.
Now, you have something like this:

class thing:
pass
obj = thing()
obj.alpha = [4, 3, 2, 1, 0]
obj.beta = 5

Python's object model suggests that obj.alpha.del() should call alpha's
del method, in the same way that obj.alpha.append() would call alpha's
append method.

Exactly so.
So how do you delete obj.alpha? obj.del("alpha") might work. But what if
obj itself is a mapping, with a key "alpha" as well as an attribute
alpha. Which one should obj.del("alpha") delete?

I don't think objects should have a del method by default. I'd suggest a
delattr or removeattr builtin, working along the lines of getattr and
setattr, for this task.
Now, if you said that L.del() should raise an exception earlier, what
about obj.beta.del()?

Unless int has a del method, that would be an exception - an
AttributeError, to be precise.
Presumably every object automatically has a del method,

I'd say no, but for the sake of argument, let's say it does.
so you don't have to program a del method yourself. obj.del is a method
object. So it has a del method. (Yes, sometimes you want to delete
methods. Functions are first class objects in Python.) Which has a del
method. Which has a del method.
Right.

What should obj.del.del.del.del.del.del.del.del.del() do?

Raise a TypeError, since you haven't passed any parameters.

As for obj.del.del.del.del.del.del.del.del.del("del"), that's an
interesting one - it comes down to the question of whether those del
methods are the same object or not. I'd say not: for two objects a and b
of the same class, a.foo and b.foo are considered different objects in
python, and that applies here.
Most other languages don't have namespaces that can get polluted, or
on-the-fly creation of variables. Most other languages don't consider
variables to be simply attributes of a module object.

How much is that really used? And how many of those cases wouldn't be
covered by a delattr builtin?
And most other languages don't allow you to run interactive sessions
where it is easy to mistakenly make variables you don't want.

py> x = 1
py> u = x+2 # oops, typo, meant y not u
py> del u # prevent confusion in the programmer's mind

It is also useful sometimes to delete a module object from the top level
namespace before re-importing it, rather than merely reloading it. That
requires being able to delete a variable.

That's a very strong use case. However, it would be straightforward to
make variable deletion an interpreter thing rather than a language thing.
In summary: del being a keyword works. del() being an object method is
unclear, confusing and complicated.

Only if you give it the bizarre semantics you use above!

I think having del as a keyword is actually unhelpful, since it's
overloaded to do two quite different things - remove items from lists and
dicts, and expunge attributes from namespaces. Far better to do let lists
and dicts expose methods to let themselves be manipulated, and to play
with attributes through a uniform troika of {get, set, del}attr builtins.

tom
 
D

Dieter Maurer

Daniel Dittmar said:
globals ().__delitem__ (varname)

except that the method would probably be called delete.

You now have a uniform way to remove an object from a namespace.

Why would you want to give each namespace its own method to
remove objects from it?

Can you imagine how much code you would break (would your proposal
to remove "del" got accepted)?


Dieter
 
?

=?ISO-8859-1?Q?Daniel_Sch=FCle?=

Am I just weird?

I feel the same way about where to use lambda's and where *not*
I come from C and C++ background and defining a function at the top
level (no nested functions) would always require good reasons

function name has to be remembered, to put it in other words it has to
be added in a mental list of available function
and writing a 2 liner function would only cause call overhead
(if not inlined) this may be the reason why "def" feels to me to have
more weight as "lambda"
usually you define lambda and forget it, no wasted time to find proper
name, which may also pollute the namespace
I find it more clear solution, it's concise
 
T

Terry Reedy

The difference in readability between

func = lambda x: x**3 - 5*x

def func(x):
return x**3 - 5*x

def func(x): return x**3 - 5*x

is obviously a matter of personal vision.

The fuctional difference (and, I believe, the only difference) is that the
def form attaches the specific name 'func' to the function object as its
func_name attribute while the lambda form attaches the generic 'name'
'<lambda>'. This makes tracebacks, for instance, less informative.

The reason some think the name=lambda form an abuse is that it is a second
way to do almost the same thing (with the functional difference being a
negative) while ignoring the intended purpose of lambda's presence in the
language. (But I will not argue this either way.)

Terry J. Reedy
 
T

Terry Reedy

Tom said:
def flatten(ll):
return reduce(lambda a, l: a.extend(l), ll, [])

How would one do that as a list comp, by the way? I'm really not very
good
with them yet.

Not really a list-comprehension based solution, but I think what you want
is
ll=[[1,2],[3,4,5],[6]]
sum(ll,[])
[1, 2, 3, 4, 5, 6]

Unless sum knows to typecheck input items and special-case lists and use
list.extend rather than list+list, this turns an O(n) operation into an
O(n**2) operation. Sum is meant for numbers.

Terry J. Reedy
 
T

Terry Hancock

Terry said:
And a syntax just occured to me -- what about this:
[<expression> for <argument list>]

If you haven't already, see:
http://wiki.python.org/moin/AlternateLambdaSyntax
for other similar proposals.

Yeah, it's basically "Robert Brewer: for (no-parens) syntax
[3]" isn't it (except that his eliminates the [], which is
probably saner).

But hey, it's surely a good thing (in the sense of being
more obvious) that it occured to me independently,
right? ;-)
 
T

Terry Reedy

Objections for the "else if" might be that it sounds like you can
replace "else if" with "else x=94" if you want. Thumbs up for "else if"
because it explains what it is much better than "elif". "elseif" ?

Today, on pydev list, in thread Chaining try statements: eltry?,
Guido said 'and in fact I sometimes think it was
a mistake to introduce elif just to save typing "else if".'

So maybe this will be reconsidered for 3.0

Terry J. Reedy
 
T

Terry Hancock

I'm going to resist the temptation to argue that list comps are themselves
unpythonic :).

Ah, but GvR likes them, so that pretty much makes them "pythonic"
by definition, doesn't it? ;-)
Hang on, where's the punctuation in either of those? They *are* done with
keywords!

Yeah, that's true, and it could be true here too, if you throw away the
[]. I see that's one of the proposed alternatives.
 
M

Marc 'BlackJack' Rintsch

It's not an empty tuple, it's an empty parenthesis. Using tuples it
would be.

But empty parenthesis are parsed as empty tuple::

In [8]: type( () )
Out[8]: <type 'tuple'>

Ciao,
Marc 'BlackJack' Rintsch
 
P

Peter Hansen

Duncan said:
I generally find that unit tests force me to structure the code in a
cleaner manner, e.g. to not use globals as much, but if you do need to
delete a global you do it in exactly the same way as you delete anything:
use the "del" statement:

Umm: huh? Tom suggested getting rid of del, I suggested it was required
for some cases in doing testing in the fact of globals (for cases where
they are the simplest approach), then you suggest that the correct
approach is to use "del".

Well, I agree (including your point on use of globals, which I attempted
to anticipate with my comment starting "sometimes"), but I don't really
see the point of your posting, Duncan... it appears redundant on all
counts.

-Peter
 
C

Christopher Subich

Terry said:
With list comprehensions and generators becoming so integral, I'm
not sure about "unpythonic". And a syntax just occured to me --
what about this:

[y*x for x,y]

?

(that is:

[<expression> for <argument list>]

It's just like the beginning of a list comprehension or generator, but
without the iterator. That implies that one must be given, and
the result is therefore a callable object.

As others have mentioned, this looks too much like a list comprehension
to be elegant, which also rules out () and {}... but I really do like
the infix syntax.

Perhaps using angle-brackets would be useful? These have no
grouping-meaning in Python that I'm aware of. Example,

<y*x for x,y>

I'd also prefer using 'with' rather than 'for' as the keyword -- 'with'
doesn't suggest iteration. I also suggest parenthization of the
argument list, since that makes a zero-argument lambda not look weird.

To replicate the examples from
http://wiki.python.org/moin/AlternateLambdaSyntax

1 lambda a, b, c:f(a) + o(b) - o(c)
<f(a) + o(b) - o(c) with (a, b, c)>
2 lambda x: x * x
<x * x with (x)>
3 lambda : x
<x with ()>
4 lambda *a, **k: x.bar(*a, **k)
<x.bar(*a, **k) with (*a, **k)>
5 ((lambda x=x, a=a, k=k: x(*a, **k)) for x, a, k in funcs_and_args_list)
(<x(*a,**k) with (x=x, a=a, k=k)> for x, a, k in funcs_and_args_list)
 
E

Erik Max Francis

Ron said:
It's not an empty tuple, it's an empty parenthesis. Using tuples it
would be.

(a,) == (,)

which would be the same as:

(,) == (,)
File "<stdin>", line 1
(,)
^
SyntaxError: invalid syntax

You've wandered way off into the woods now.
 
R

Ron Adam

Christopher said:
As others have mentioned, this looks too much like a list comprehension
to be elegant, which also rules out () and {}... but I really do like
the infix syntax.

Why would it rule out ()?

You need to put a lambda express in ()'s anyways if you want to use it
right away.

print (lambda x,y:x+y)(1,2)

If you don't use the ()'s it reads the y(1,2) as part of the lambda
expression, might as well require the ()'s to start with rather than
leave it open for a possible error.


You could even say () is to function as [] is to list.

a function : name(args) -> returns a value

a list : name[index] -> returns a value



My choice:

name = (let x,y return x+y) # easy for beginners to understand
value = name(a,b)

value = (let x,y return x+y)(a,b)



I think the association of (lambda) to [list_comp] is a nice
distinction. Maybe a {dictionary_comp} would make it a complete set. ;-)

Cheers,
Ron
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top