Re: itemgetter with default arguments

2018-05-08 Thread Antoon Pardon
On 07-05-18 17:45, Peter Otten wrote:
> Antoon Pardon wrote:
>
>> On 05-05-18 09:33, Peter Otten wrote:
>>> I think you have established that there is no straight-forward way to
>>> write this as a lambda. But is adding a default to itemgetter the right
>>> conclusion?
>>>
>>> If there were an exception-catching decorator you could write
>>>
>>> f = catch(IndexError, "spam")(itemgetter(2))
>> I think your catch function would be a usefull addition, but I don't see
>> it solving this problem once we use itemgetter te get multiple entries.
> Good catch()
>
> ;)
>
> The obvious way, expressing the n-tuple case in terms of the solution for 
> scalars
>
 f = lambda items: tuple(catch(IndexError, "spam")(itemgetter(i))(items) 
> for i in (2, 1, 5))
>>> f("abc")
> ('c', 'b', 'spam')
>
> is a bit too complex to inline -- and also inefficient. You'd be tempted to 
> factor out the repetetive parts
>
 f = lambda items, gets=[catch(IndexError, "spam")(itemgetter(i)) for i 
> in (2, 1, 5)]: tuple(get(items) for get in gets)
 f("abc")
> ('c', 'b', 'spam')
>
> and thus make it even less readable.
>
> That said -- grepping my code I'm a bit surprised to find only
>
> 17 itemgetter(0)
>  9 itemgetter(1)
>  1 itemgetter(1, 0)
>  1 itemgetter(*indices)
>
> Checking /usr/lib/python3/dist-packages it looks like the situation is 
> similar. I conclude that the most useful addition to the operator module 
> would be
>
> first = itemgetter(0)
> second = itemgetter(1)

The problem with looking for such uses, is that I am working on a lot of
legacy code written in 2.2. It needed few adaptions to still work in 2.7
but I didn't adapt the code to make use of all the new features. But looking
at sort statements, it seems I have (the equivallent of) the following

itemgetter(2)
itemgetter(3)
itemgetter(4)
itemgetter(slice(0,-1))

-- 
Antoon Pardon.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-07 Thread Peter Otten
Antoon Pardon wrote:

> On 05-05-18 09:33, Peter Otten wrote:
>> I think you have established that there is no straight-forward way to
>> write this as a lambda. But is adding a default to itemgetter the right
>> conclusion?
>>
>> If there were an exception-catching decorator you could write
>>
>> f = catch(IndexError, "spam")(itemgetter(2))
> 
> I think your catch function would be a usefull addition, but I don't see
> it solving this problem once we use itemgetter te get multiple entries.

Good catch()

;)

The obvious way, expressing the n-tuple case in terms of the solution for 
scalars

>>> f = lambda items: tuple(catch(IndexError, "spam")(itemgetter(i))(items) 
for i in (2, 1, 5))
>>> >>> f("abc")
('c', 'b', 'spam')

is a bit too complex to inline -- and also inefficient. You'd be tempted to 
factor out the repetetive parts

>>> f = lambda items, gets=[catch(IndexError, "spam")(itemgetter(i)) for i 
in (2, 1, 5)]: tuple(get(items) for get in gets)
>>> f("abc")
('c', 'b', 'spam')

and thus make it even less readable.

That said -- grepping my code I'm a bit surprised to find only

17 itemgetter(0)
 9 itemgetter(1)
 1 itemgetter(1, 0)
 1 itemgetter(*indices)

Checking /usr/lib/python3/dist-packages it looks like the situation is 
similar. I conclude that the most useful addition to the operator module 
would be

first = itemgetter(0)
second = itemgetter(1)


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-07 Thread Antoon Pardon
On 05-05-18 09:33, Peter Otten wrote:
> I think you have established that there is no straight-forward way to write 
> this as a lambda. But is adding a default to itemgetter the right 
> conclusion?
>
> If there were an exception-catching decorator you could write
>
> f = catch(IndexError, "spam")(itemgetter(2))

I think your catch function would be a usefull addition, but I don't see
it solving this problem once we use itemgetter te get multiple entries.

-- 
Antoon.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Ian Kelly
On Fri, May 4, 2018 at 5:34 PM, Thomas Jollans  wrote:
> On 04/05/18 22:38, Ian Kelly wrote:
>> The real thing is written in C.
>>
>
> Is it though?
>
> https://github.com/python/cpython/blob/a1fc949b5ab8911a803eee691e6eea55cec43eeb/Lib/operator.py#L265

It is. First, notice the docstring of that module says, "This is the
pure Python implementation of the module." Second, notice that near
the bottom of the file is this code:

try:
from _operator import *
except ImportError:
pass
else:
from _operator import __doc__

The implementation actually used by CPython is here:

https://github.com/python/cpython/blob/a1fc949b5ab8911a803eee691e6eea55cec43eeb/Modules/_operator.c#L402

The pure Python version exists to be shared by other Python
implementations that don't support C extensions.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Serhiy Storchaka

05.05.18 12:59, Steven D'Aprano пише:

On Sat, 05 May 2018 10:31:17 +0300, Serhiy Storchaka wrote:

Consider a concrete example. You need to sort a list of 2- or 3- element
tuples by the first and third items (third items are strings if
presented). itemgetter(0, 2) doesn't work because some tuples has only 2
items. But you can use the following lambdas:


 lambda t: t[0:3:2]

should also work.


Right, this works in this special case (for tuples and 2-items key). You 
can even utilize itemgetter()!


 itemgetter(slice(0, 3, 2))


  lambda t: (t[0], t[2] if len(t) > 2 else '')
  lambda t: (t[0], t[2]) if len(t) > 2 else (t[0],)
  lambda t: (t[0], (t + ('',))[2])
  lambda t: t[:1] + t[2:]


So which of these is the "One Obvious Way"?


Depending on the details of the problem and the background of the 
author, the first or the second options can be the most obvious way. Or 
any other.



The second and the forth options support also the case when there is no
natural minimal value for third items (e.g. for negative integers) or if
you want to order 2-tuples before 3-tuples with empty third item and the
same first item. This isn't possible with itemgetter with default
arguments.


Nobody suggests that itemgetter is a magic wand that ought to solve every
imaginable problem. There will always be sufficiently complex examples
where you have to write your own key function.


Right. And it looks to me, that the case in which you want to use 
itemgetter with default arguments is the case where you have to write 
your own key function.


--
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Steven D'Aprano
On Sat, 05 May 2018 10:31:17 +0300, Serhiy Storchaka wrote:

> Consider a concrete example. You need to sort a list of 2- or 3- element
> tuples by the first and third items (third items are strings if
> presented). itemgetter(0, 2) doesn't work because some tuples has only 2
> items. But you can use the following lambdas:

lambda t: t[0:3:2]

should also work.


>  lambda t: (t[0], t[2] if len(t) > 2 else '')
>  lambda t: (t[0], t[2]) if len(t) > 2 else (t[0],)
>  lambda t: (t[0], (t + ('',))[2])
>  lambda t: t[:1] + t[2:]

So which of these is the "One Obvious Way"?


> The second and the forth options support also the case when there is no
> natural minimal value for third items (e.g. for negative integers) or if
> you want to order 2-tuples before 3-tuples with empty third item and the
> same first item. This isn't possible with itemgetter with default
> arguments.

Nobody suggests that itemgetter is a magic wand that ought to solve every 
imaginable problem. There will always be sufficiently complex examples 
where you have to write your own key function.

-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Steven D'Aprano
On Sat, 05 May 2018 09:33:37 +0200, Peter Otten wrote:

> I think you have established that there is no straight-forward way to
> write this as a lambda.

What I *mostly* established was that I was having a "cannot brain, I have 
the dumb" day, because the solution with ternary if was obvious in 
hindsight:

lambda x: x[3] if len(x) > 4 else "MISSING"


nevertheless, I still think that making this a feature of itemgetter and 
attrgetter is worthwhile.


> But is adding a default to itemgetter the right conclusion?

I think so, but others (including Raymond Hettinger and Serhiy Storchaka) 
don't. Even when they're clearly and obviously wrong, as in this case 
*wink*, I don't think we should just dismiss their objection without 
careful thought.



-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Peter Otten
Steven D'Aprano wrote:

> A re-occurring feature request is to add a default to itemgetter and
> attrgetter. For example, we might say:
> 
> from operator import itemgetter
> f = itemgetter(1, 6, default="spam")  # proposed feature
> f("Hello World!")  # returns ('e', 'W')
> f("Hello") # returns ('e', 'spam')
> 
> 
> Two senior developers have rejected this feature, saying that we should
> just use a lambda instead.
> 
> I might be slow today, but I cannot see how to write a clear, obvious,
> efficient lambda that provides functionality equivalent to itemgetter
> with a default value.
> 
> Putting aside the case where itemgetter takes multiple indexes, how about
> the single index case? How could we get that functionality using a lambda
> which is simple and obvious enough to use on the fly as needed, and
> reasonably efficient?
> 
> Here are the specifications:
> 
> * you must use lambda, not def;
> 
> * the lambda must take a single function, the sequence you want to
>   extract an item from;
> 
> * you can hard-code the index in the body of the lambda;
> 
> * you can hard-code the default value in the body of the lambda;
> 
> * if sequence[index] exists, return that value;
> 
> * otherwise return the default value;
> 
> * it should support both positive and negative indices.
> 
> Example: given an index of 2 and a default of "spam":
> 
> (lambda seq: ... )("abcd") returns "c"
> 
> (lambda seq: ... )("") returns "spam"
> 
> 
> I might be missing something horribly obvious, but I can't see how to do
> this using a lambda. I tried using slicing:
> 
> seq[index:index+1]
> 
> which will return either an empty slice or a one-item slice, but that
> didn't help me. I feel I'm missing something either obvious, or something
> impossible, and I don't know which.
> 
> (This isn't a code-golf problem. I care more about being able to do it at
> all, than about doing it in the minimum number of characters.)

I think you have established that there is no straight-forward way to write 
this as a lambda. But is adding a default to itemgetter the right 
conclusion?

If there were an exception-catching decorator you could write

f = catch(IndexError, "spam")(itemgetter(2))


>>> from operator import itemgetter
>>> def catch(exc, default):
... def deco(f):
... def catcher(*args, **kw):
... try: return f(*args, **kw)
... except exc: return default
... return catcher
... return deco
... 
>>> f = catch((IndexError, KeyError), "spam")(itemgetter(1))
>>> f("abc")
'b'
>>> f("")
'spam'
>>> f(dict(a=1))
'spam'
>>> f({1: "ham"})
'ham'

This may be useful for other applications:

>>> g = catch(ZeroDivisionError, "#error")(lambda x: 1/x)
>>> g(2)
0.5
>>> g(0)
'#error'


-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-05 Thread Serhiy Storchaka

04.05.18 20:04, Steven D'Aprano пише:

On Fri, 04 May 2018 09:17:14 -0600, Ian Kelly wrote:

On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
 wrote:

Here are the specifications:

* you must use lambda, not def;


Why? This seems like an arbitrary constraint.


You'll have to ask the two core devs. In my post, in the part you
deleted, I wrote:

 Two senior developers have rejected this feature, saying
 that we should just use a lambda instead.


My guess is that they were thinking that there's no need to complicate
itemgetter for this use-case when it is just as easy to write up a quick
lambda to do the job.


I prefer using local functions instead of lambdas, but in many concrete 
cases it is possible to use a lambda.


Consider a concrete example. You need to sort a list of 2- or 3- element 
tuples by the first and third items (third items are strings if 
presented). itemgetter(0, 2) doesn't work because some tuples has only 2 
items. But you can use the following lambdas:


lambda t: (t[0], t[2] if len(t) > 2 else '')

lambda t: (t[0], t[2]) if len(t) > 2 else (t[0],)

lambda t: (t[0], (t + ('',))[2])

lambda t: t[:1] + t[2:]

The second and the forth options support also the case when there is no 
natural minimal value for third items (e.g. for negative integers) or if 
you want to order 2-tuples before 3-tuples with empty third item and the 
same first item. This isn't possible with itemgetter with default arguments.


If 2-tuples are pretty rare, it may be more efficient to use the 
following function:


def sortkey(t):
try:
return t[0], t[2]
except IndexError:
return t[0],

--
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Steven D'Aprano
On Fri, 04 May 2018 14:38:54 -0600, Ian Kelly wrote:

> On Fri, May 4, 2018 at 11:04 AM, Steven D'Aprano
>  wrote:
[...]

>> My guess is that they were thinking that there's no need to complicate
>> itemgetter for this use-case when it is just as easy to write up a
>> quick lambda to do the job.
> 
> I saw that. I just don't understand why the solution should require a
> lambda just because that was the word used by a couple of core devs.

If it were up to me, the solution wouldn't require anything beyond 
itemgetter *wink*

In practice, we don't often use itemgetter like this:

f = itemgetter(2)
result = f(alist)

since we could just say result = alist[2] instead. I think the main use-
case for itemgetter is as a sort key:

alist.sort(key=itemgetter(2))

or perhaps even map (although this will be obsoleted somewhat by list 
comprehensions):

results = map(itemgetter(2), alist)

The point being, in these cases, it is inconvenient to have to define a 
function ahead of time. Hence, a lambda would be the right solution:

alist.sort(key=lambda item: ...)

rather than having to define the key function ahead of time.


-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Steven D'Aprano
On Fri, 04 May 2018 15:27:02 +0200, Thomas Jollans wrote:

 spamgetter = (lambda seq, i=2, fallback="spam":
> ...  seq[i] if abs(i) < len(seq) or i == -len(seq) 
> ...  else fallback)
 spamgetter("abcd", i=-4)
> 'a'
 spamgetter("abcd")
> 'c'
 spamgetter("")
> 'spam'


Doh! Obvious in hindsight. Thanks.

And on my computer, it's only 125% slower than itemgetter.


> Making this work for sequences AND dict-like objects is more
> difficult...

Indeed. At least with dicts you can call .get(). I don't think it is 
necessary to have the one lambda handle both sequence and dict cases. 
Surely when calling it you know whether or not you are looking at a 
sequence or a mapping.



-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Steven D'Aprano
On Fri, 04 May 2018 15:27:16 +0200, Antoon Pardon wrote:

>> I might be slow today, but I cannot see how to write a clear, obvious,
>> efficient lambda that provides functionality equivalent to itemgetter
>> with a default value.
[...]

> This seems to work:
> 
> f = (lambda seq: (list(seq) + 3 * ["spam"])[2])

Yep, I'm definitely slow today. Thanks for that.


Of course, it's also pretty slow, nearly as slow as me :-) On my 
computer, it was about 2000% slower than itemgetter using a moderate 
sized list (100 items).




-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Thomas Jollans
On 04/05/18 22:38, Ian Kelly wrote:
> On Fri, May 4, 2018 at 11:04 AM, Steven D'Aprano
>  wrote:
>> On Fri, 04 May 2018 09:17:14 -0600, Ian Kelly wrote:
>>
>>> On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
>>>  wrote:
 Here are the specifications:

 * you must use lambda, not def;
>>>
>>> Why? This seems like an arbitrary constraint.
>>
>> You'll have to ask the two core devs. In my post, in the part you
>> deleted, I wrote:
>>
>> Two senior developers have rejected this feature, saying
>> that we should just use a lambda instead.
>>
>>
>> My guess is that they were thinking that there's no need to complicate
>> itemgetter for this use-case when it is just as easy to write up a quick
>> lambda to do the job.
> 
> I saw that. I just don't understand why the solution should require a
> lambda just because that was the word used by a couple of core devs.
> 
>> For what it's worth, your itemgetter2() is about 1600% slower on my
>> computer than the real thing. I know I didn't specify speed as a
>> requirement, but I mention it because on the bug tracker, the importance
>> of keeping itemgetter fast is stressed.
> 
> The real thing is written in C.
> 

Is it though?

https://github.com/python/cpython/blob/a1fc949b5ab8911a803eee691e6eea55cec43eeb/Lib/operator.py#L265
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Ian Kelly
On Fri, May 4, 2018 at 11:04 AM, Steven D'Aprano
 wrote:
> On Fri, 04 May 2018 09:17:14 -0600, Ian Kelly wrote:
>
>> On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
>>  wrote:
>>> Here are the specifications:
>>>
>>> * you must use lambda, not def;
>>
>> Why? This seems like an arbitrary constraint.
>
> You'll have to ask the two core devs. In my post, in the part you
> deleted, I wrote:
>
> Two senior developers have rejected this feature, saying
> that we should just use a lambda instead.
>
>
> My guess is that they were thinking that there's no need to complicate
> itemgetter for this use-case when it is just as easy to write up a quick
> lambda to do the job.

I saw that. I just don't understand why the solution should require a
lambda just because that was the word used by a couple of core devs.

> For what it's worth, your itemgetter2() is about 1600% slower on my
> computer than the real thing. I know I didn't specify speed as a
> requirement, but I mention it because on the bug tracker, the importance
> of keeping itemgetter fast is stressed.

The real thing is written in C.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Steven D'Aprano
On Fri, 04 May 2018 09:17:14 -0600, Ian Kelly wrote:

> On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
>  wrote:
>> Here are the specifications:
>>
>> * you must use lambda, not def;
> 
> Why? This seems like an arbitrary constraint.

You'll have to ask the two core devs. In my post, in the part you 
deleted, I wrote:

Two senior developers have rejected this feature, saying 
that we should just use a lambda instead.


My guess is that they were thinking that there's no need to complicate 
itemgetter for this use-case when it is just as easy to write up a quick 
lambda to do the job.

For what it's worth, your itemgetter2() is about 1600% slower on my 
computer than the real thing. I know I didn't specify speed as a 
requirement, but I mention it because on the bug tracker, the importance 
of keeping itemgetter fast is stressed.




-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Chris Angelico
On Sat, May 5, 2018 at 1:17 AM, Ian Kelly  wrote:
> On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
>  wrote:
>> Here are the specifications:
>>
>> * you must use lambda, not def;
>
> Why? This seems like an arbitrary constraint.
>
> def itemgetter2(*items, default):
> return lambda seq: tuple(get_default(seq, item, default) for item in 
> items)
>
> def get_default(seq, item, default):
> try:
> return seq[item]
> except (IndexError, KeyError):
> return default
>
> py> f = itemgetter2(1, 6, default="spam")
> py> f("Hello World!")
> ('e', 'W')
> py> f("Hello!")
> ('e', 'spam')

PEP 463 wants to say hello.

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Ian Kelly
On Fri, May 4, 2018 at 7:01 AM, Steven D'Aprano
 wrote:
> Here are the specifications:
>
> * you must use lambda, not def;

Why? This seems like an arbitrary constraint.

> * the lambda must take a single function, the sequence you want to
>   extract an item from;
>
> * you can hard-code the index in the body of the lambda;
>
> * you can hard-code the default value in the body of the lambda;
>
> * if sequence[index] exists, return that value;
>
> * otherwise return the default value;
>
> * it should support both positive and negative indices.
>
> Example: given an index of 2 and a default of "spam":
>
> (lambda seq: ... )("abcd") returns "c"
>
> (lambda seq: ... )("") returns "spam"

def itemgetter2(*items, default):
return lambda seq: tuple(get_default(seq, item, default) for item in items)

def get_default(seq, item, default):
try:
return seq[item]
except (IndexError, KeyError):
return default

py> f = itemgetter2(1, 6, default="spam")
py> f("Hello World!")
('e', 'W')
py> f("Hello!")
('e', 'spam')
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Antoon Pardon
On 04-05-18 15:01, Steven D'Aprano wrote:
> A re-occurring feature request is to add a default to itemgetter and 
> attrgetter. For example, we might say:
>
> from operator import itemgetter
> f = itemgetter(1, 6, default="spam")  # proposed feature
> f("Hello World!")  # returns ('e', 'W')
> f("Hello") # returns ('e', 'spam')
>
>
> Two senior developers have rejected this feature, saying that we should 
> just use a lambda instead.
>
> I might be slow today, but I cannot see how to write a clear, obvious, 
> efficient lambda that provides functionality equivalent to itemgetter 
> with a default value.
>
> Putting aside the case where itemgetter takes multiple indexes, how about 
> the single index case? How could we get that functionality using a lambda 
> which is simple and obvious enough to use on the fly as needed, and 
> reasonably efficient?
>
> Here are the specifications:
>
> * you must use lambda, not def;
>
> * the lambda must take a single function, the sequence you want to
>   extract an item from;
>
> * you can hard-code the index in the body of the lambda;
>
> * you can hard-code the default value in the body of the lambda;
>
> * if sequence[index] exists, return that value;
>
> * otherwise return the default value;
>
> * it should support both positive and negative indices.
>
> Example: given an index of 2 and a default of "spam":
>
> (lambda seq: ... )("abcd") returns "c"
>
> (lambda seq: ... )("") returns "spam"
>
>
> I might be missing something horribly obvious, but I can't see how to do 
> this using a lambda. I tried using slicing:
>
> seq[index:index+1]
>
> which will return either an empty slice or a one-item slice, but that 
> didn't help me. I feel I'm missing something either obvious, or something 
> impossible, and I don't know which.
>
> (This isn't a code-golf problem. I care more about being able to do it at 
> all, than about doing it in the minimum number of characters.)

This seems to work:

f = (lambda seq: (list(seq) + 3 * ["spam"])[2])

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: itemgetter with default arguments

2018-05-04 Thread Thomas Jollans
On 2018-05-04 15:01, Steven D'Aprano wrote:
> A re-occurring feature request is to add a default to itemgetter and 
> attrgetter. For example, we might say:
> 
> from operator import itemgetter
> f = itemgetter(1, 6, default="spam")  # proposed feature
> f("Hello World!")  # returns ('e', 'W')
> f("Hello") # returns ('e', 'spam')
> 
> 
> Two senior developers have rejected this feature, saying that we should 
> just use a lambda instead.
> 
> I might be slow today, but I cannot see how to write a clear, obvious, 
> efficient lambda that provides functionality equivalent to itemgetter 
> with a default value.
> 
> Putting aside the case where itemgetter takes multiple indexes, how about 
> the single index case? How could we get that functionality using a lambda 
> which is simple and obvious enough to use on the fly as needed, and 
> reasonably efficient?
> 
> Here are the specifications:
> 
> * you must use lambda, not def;
> 
> * the lambda must take a single function, the sequence you want to
>   extract an item from;
> 
> * you can hard-code the index in the body of the lambda;
> 
> * you can hard-code the default value in the body of the lambda;
> 
> * if sequence[index] exists, return that value;
> 
> * otherwise return the default value;
> 
> * it should support both positive and negative indices.
> 
> Example: given an index of 2 and a default of "spam":
> 
> (lambda seq: ... )("abcd") returns "c"
> 
> (lambda seq: ... )("") returns "spam"

Like this?

>>> spamgetter = (lambda seq, i=2, fallback="spam":
...  seq[i] if abs(i) < len(seq) or i == -len(seq)
...  else fallback)
>>> spamgetter("abcd", i=-4)
'a'
>>> spamgetter("abcd")
'c'
>>> spamgetter("")
'spam'
>>>


Making this work for sequences AND dict-like objects is more difficult...


> 
> 
> I might be missing something horribly obvious, but I can't see how to do 
> this using a lambda. I tried using slicing:
> 
> seq[index:index+1]
> 
> which will return either an empty slice or a one-item slice, but that 
> didn't help me. I feel I'm missing something either obvious, or something 
> impossible, and I don't know which.
> 
> (This isn't a code-golf problem. I care more about being able to do it at 
> all, than about doing it in the minimum number of characters.)
> 
> 
> 


-- 
https://mail.python.org/mailman/listinfo/python-list


itemgetter with default arguments

2018-05-04 Thread Steven D'Aprano
A re-occurring feature request is to add a default to itemgetter and 
attrgetter. For example, we might say:

from operator import itemgetter
f = itemgetter(1, 6, default="spam")  # proposed feature
f("Hello World!")  # returns ('e', 'W')
f("Hello") # returns ('e', 'spam')


Two senior developers have rejected this feature, saying that we should 
just use a lambda instead.

I might be slow today, but I cannot see how to write a clear, obvious, 
efficient lambda that provides functionality equivalent to itemgetter 
with a default value.

Putting aside the case where itemgetter takes multiple indexes, how about 
the single index case? How could we get that functionality using a lambda 
which is simple and obvious enough to use on the fly as needed, and 
reasonably efficient?

Here are the specifications:

* you must use lambda, not def;

* the lambda must take a single function, the sequence you want to
  extract an item from;

* you can hard-code the index in the body of the lambda;

* you can hard-code the default value in the body of the lambda;

* if sequence[index] exists, return that value;

* otherwise return the default value;

* it should support both positive and negative indices.

Example: given an index of 2 and a default of "spam":

(lambda seq: ... )("abcd") returns "c"

(lambda seq: ... )("") returns "spam"


I might be missing something horribly obvious, but I can't see how to do 
this using a lambda. I tried using slicing:

seq[index:index+1]

which will return either an empty slice or a one-item slice, but that 
didn't help me. I feel I'm missing something either obvious, or something 
impossible, and I don't know which.

(This isn't a code-golf problem. I care more about being able to do it at 
all, than about doing it in the minimum number of characters.)



-- 
Steve

-- 
https://mail.python.org/mailman/listinfo/python-list