list…pushed, or something

I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have variations of list().append(obj) and set().add(obj) that return, obj. I've never come up with perfect method names; sometimes I go with past-tense versions, "added" and "appended" (or "pushed"). I often use these methods in places where performance is a factor, such as using a set() to filter repeats from an input sequence.
I was thinking it may be worth considering adding it to core, or perhaps creating collections types with these features, so they are high performance and standardized. They can be particularly useful in generator recipes, etc.
Shane Green www.umbrellacode.com 408-692-4666 | shane@umbrellacode.com

On 27Mar2013 19:29, Shane Green shane@umbrellacode.com wrote: | I'm not sure if there's anything inherently wrong with this idea, and I am well aware | how incredibly easy it is to implement as an extension of the built-ins, but, I find it | very useful to have variations of list().append(obj) and set().add(obj) that return, | obj. I've never come up with perfect method names; sometimes I go with past-tense | versions, "added" and "appended" (or "pushed"). I often use these methods in places | where performance is a factor, such as using a set() to filter repeats from an input | sequence.
I don't suppose you could post example code?
I suspect the thinking goes that functions/methods that do something shouldn't just be "identity" functions where f(x) == x. And if they are defined to be identity functions you may as well return None, since the calling code isn't learning anything new.
At any rate, that would be part of my thinking, and for me it is enough to not favour the proposal without a better demo of where it is a win, or of how horribly I have misconstrued your suggestion.
Cheers,

On 3/27/2013 10:29 PM, Shane Green wrote:
I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have /variations/ of list().append(obj) and set().add(obj) that return, obj.
There are other people who agree with you, but it is Guido's design decision from the beginning of Python that mutation methods do not return the object mutated.

Also, we shouldn't introduce multiple ways of spelling the same operation if we can help it.
On Wednesday, March 27, 2013, Terry Reedy wrote:
On 3/27/2013 10:29 PM, Shane Green wrote:
I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have /variations/ of list().append(obj) and set().add(obj) that return, obj.
There are other people who agree with you, but it is Guido's design decision from the beginning of Python that mutation methods do not return the object mutated.
-- Terry Jan Reedy
______________________________**_________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/**mailman/listinfo/python-ideashttp://mail.python.org/mailman/listinfo/python-ideas

Actually this was the value doing the mutating, i.e, the <thing> i pushed or added, would be returned as operation's output. I see what you're saying, and I wasn't thinking of chaining so much as being able to include the operation in a expression and, in particular, a generator scenario.
Ordered sequence of unique values from sequence with possible repeats [seen.added(value) for value in sequence if value not in seen] *
* again, please forgive the method names, I was hoping to crowd source something better ;-)
Replaces workarounds like: [seen.setdefault(value, value) for value in sequence if value not in seen] or seen = dict(sequence); [unique.pop(value) for value in sequence if value in unique]
Like I said, it's incredibly simple to just extend set and list and add these in user space.
Shane Green www.umbrellacode.com 408-692-4666 | shane@umbrellacode.com
On Mar 27, 2013, at 9:49 PM, Terry Reedy tjreedy@udel.edu wrote:
On 3/27/2013 10:29 PM, Shane Green wrote:
I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have /variations/ of list().append(obj) and set().add(obj) that return, obj.
There are other people who agree with you, but it is Guido's design decision from the beginning of Python that mutation methods do not return the object mutated.
-- Terry Jan Reedy
Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

On Wed, Mar 27, 2013 at 10:11 PM, Shane Green shane@umbrellacode.comwrote:
[seen.added(value) for value in sequence if value not in seen] *
Here's an easy way to do it:
seen = set() seq = [3,2,1,2,3,4,5,4] [seen.add(v) or v for v in seq if v not in seen]
[3, 2, 1, 4, 5]
seen
{1, 2, 3, 4, 5}
--- Bruce Latest blog post: Alice's Puzzle Page http://www.vroospeak.com Learn how hackers think: http://j.mp/gruyere-security

That's clever: even works for zero because it's returned by or as second false. Cool. So I suppose I have to come up with more examples now ;-)
Actually, on that point, I actually think the seen.added(value) (with a better name) is quite a bit cleaner than the one using "or". Clever as it is, I think someone learning the language would flinch when they saw that… :-)
Shane Green www.umbrellacode.com 408-692-4666 | shane@umbrellacode.com
On Mar 27, 2013, at 10:28 PM, Bruce Leban bruce@leapyear.org wrote:
On Wed, Mar 27, 2013 at 10:11 PM, Shane Green shane@umbrellacode.com wrote: [seen.added(value) for value in sequence if value not in seen] *
Here's an easy way to do it:
seen = set() seq = [3,2,1,2,3,4,5,4] [seen.add(v) or v for v in seq if v not in seen]
[3, 2, 1, 4, 5]
seen
{1, 2, 3, 4, 5}
--- Bruce Latest blog post: Alice's Puzzle Page http://www.vroospeak.com Learn how hackers think: http://j.mp/gruyere-security

On Wed, Mar 27, 2013 at 10:48 PM, Shane Green shane@umbrellacode.comwrote:
That's clever: even works for zero because it's returned by or as second false. Cool. So I suppose I have to come up with more examples now ;-)
Actually, on that point, I actually think the seen.added(value) (with a better name) is quite a bit cleaner than the one using "or". Clever as it is, I think someone learning the language would flinch when they saw that… :-)
Yes, I suppose it's a bit obscure but you're only going to use it in a case where you value brevity over clarity, right?
The C comma operator which always returns the second value, not depending that the first value is false. You can write this in Python as:
(foo, bar)[1]
or is there some cleaner way to write that?
#define comma and False or :-)
--- Bruce Latest blog post: Alice's Puzzle Page http://www.vroospeak.com Learn how hackers think: http://j.mp/gruyere-security

Always makes me think of my favorite C snippet:
if (attack = true) { launch_nukes(); }
Well, I have to admit that I'm drawing a blank on all the great use cases I had in mind, and it didn't seem to light any particular fires with the list, so I'd say this isn't a compelling enough story to take anymore time with at the moment. If a good–and not obscure–example comes to mind, I'll send it out, otherwise, thanks for the feedback!
Shane Green www.umbrellacode.com 408-692-4666 | shane@umbrellacode.com
On Mar 27, 2013, at 10:58 PM, Bruce Leban bruce@leapyear.org wrote:
On Wed, Mar 27, 2013 at 10:48 PM, Shane Green shane@umbrellacode.com wrote: That's clever: even works for zero because it's returned by or as second false. Cool. So I suppose I have to come up with more examples now ;-)
Actually, on that point, I actually think the seen.added(value) (with a better name) is quite a bit cleaner than the one using "or". Clever as it is, I think someone learning the language would flinch when they saw that… :-)
Yes, I suppose it's a bit obscure but you're only going to use it in a case where you value brevity over clarity, right?
The C comma operator which always returns the second value, not depending that the first value is false. You can write this in Python as:
(foo, bar)[1]
or is there some cleaner way to write that?
#define comma and False or :-)
--- Bruce Latest blog post: Alice's Puzzle Page http://www.vroospeak.com Learn how hackers think: http://j.mp/gruyere-security

On 28/03/2013 05:28, Bruce Leban wrote:
On Wed, Mar 27, 2013 at 10:11 PM, Shane Green <shane@umbrellacode.com mailto:shane@umbrellacode.com> wrote:
[seen.added(value) for value in sequence if value not in seen] *
Here's an easy way to do it:
seen = set() seq = [3,2,1,2,3,4,5,4] [seen.add(v) or v for v in seq if v not in seen]
[3, 2, 1, 4, 5]
seen
{1, 2, 3, 4, 5}
I think I would prefer a "unique" function that yields unique items:
def unique(items): seen = set()
for item in items: if item not in seen: seen.add(item) yield item
seq = [3,2,1,2,3,4,5,4] list(unique(seq))
[3, 2, 1, 4, 5]

Yes, that would be a nice function. The unique items from a ordered sequence was just supposed to be an example of something you could do with the concept I was going for: push/add methods that returned the value pushed/added, so a 'list.pushed(value)' could replace value inline. Of course it's turning out the unique items may best example which, in and of itselft, isn't very compelling argument for my original idea because there are better ways to solve this problem, as you've pointed out.
On Mar 28, 2013, at 8:15 AM, MRAB python@mrabarnett.plus.com wrote:
On 28/03/2013 05:28, Bruce Leban wrote:
On Wed, Mar 27, 2013 at 10:11 PM, Shane Green <shane@umbrellacode.com mailto:shane@umbrellacode.com> wrote:
[seen.added(value) for value in sequence if value not in seen] *
Here's an easy way to do it:
seen = set() seq = [3,2,1,2,3,4,5,4] [seen.add(v) or v for v in seq if v not in seen]
[3, 2, 1, 4, 5]
seen
{1, 2, 3, 4, 5}
I think I would prefer a "unique" function that yields unique items:
def unique(items): seen = set()
for item in items: if item not in seen: seen.add(item) yield item
seq = [3,2,1,2,3,4,5,4] list(unique(seq))
[3, 2, 1, 4, 5]
Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

And I was suggesting something along these lines would be generally useful (if not pointlessly easy):
class Set(set): __slots__ = () def added(self, value): super(Set, self).add(value) return value
def unique(items): seen = Set() return (seen.added(item) for item in items if item not in seen)
On Mar 28, 2013, at 8:15 AM, MRAB python@mrabarnett.plus.com wrote:
On 28/03/2013 05:28, Bruce Leban wrote:
On Wed, Mar 27, 2013 at 10:11 PM, Shane Green <shane@umbrellacode.com mailto:shane@umbrellacode.com> wrote:
[seen.added(value) for value in sequence if value not in seen] *
Here's an easy way to do it:
seen = set() seq = [3,2,1,2,3,4,5,4] [seen.add(v) or v for v in seq if v not in seen]
[3, 2, 1, 4, 5]
seen
{1, 2, 3, 4, 5}
I think I would prefer a "unique" function that yields unique items:
def unique(items): seen = set()
for item in items: if item not in seen: seen.add(item) yield item
seq = [3,2,1,2,3,4,5,4] list(unique(seq))
[3, 2, 1, 4, 5]
Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas

On 27 March 2013 23:29, Shane Green shane@umbrellacode.com wrote:
I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have variations of list().append(obj) and set().add(obj) that return, obj. I've never come up with perfect method names; sometimes I go with past-tense versions, "added" and "appended" (or "pushed"). I often use these methods in places where performance is a factor, such as using a set() to filter repeats from an input sequence.
I was thinking it may be worth considering adding it to core, or perhaps creating collections types with these features, so they are high performance and standardized. They can be particularly useful in generator recipes, etc.
I understand that the equivalent methods in other languages - sometimes frameworks int hose languages - allow one to chain method calls on the parent object, so they can happly write things along:
list().append(1).append(0).sort().append(2)
Python style, as well placed on the thread is that methods that perform changes to the underlying object return None, thus not allowing such constructs to mutable objects - even though one can happily do something like:
image_name = url.split("/")[-1].split(".")[0]
You can easily have the former behavior if you wrap your object in a construct that, whenever a called method would return "None", returns the original object itself. That could be placed in a utility module - and probably there is even some "MyHacks" package on pypi with functionality like that.
If a naive implementation fits your needs, this one would work:
class Chain: def __init__(self, obj, root=None): self.__obj = obj def __getattr__(self, attr): val = getattr(self.__obj, attr) if callable(val): self.__callable = val return self return val def __call__(self, *args, **kw): val = self.__callable(*args, **kw) if val is None: return self return val
---------------
a = [] Chain(a).append(5).append(6).append(-1).sort().append(3)
<__main__.Chain object at 0x12b6f50>
a
[-1, 5, 6, 3]
I'd be -0 for something like that on the stlib, though - but if it was there, I'd look around "functools" (but it is obviously more like an "objecttool")
js -><-

On 3/29/2013 7:40 AM, Joao S. O. Bueno wrote:
I understand that the equivalent methods in other languages - sometimes frameworks int hose languages - allow one to chain method calls on the parent object, so they can happly write things along:
list().append(1).append(0).sort().append(2)
Python style, as well placed on the thread is that methods that perform changes to the underlying object return None,
Unless the method returns something else other than the underlying mutable. Examples are list.pop, set.pop, dict.pop, and dict.popitem, which return the item (or pair) removed.

Yes, these are good points. To be clear about one thing, though, what I was suggesting behaves very differently than the mechanism most other languages may use to enable chaining. Chaining is usually based around the operations on X, returning a reference to X after completion (of course this only makes sense for mutator methods, otherwise nothing would been done or gotten).
Given my recommendation: X.pushed(Y) -> X IFF (and-only-if) Y is X.
However, X.pushed(Y) -> Y is always true.
The idea was not to be able chain invocations, but to be able to use theses operations inline in places the plain value, Y, would have appeared. It's such a trivial matter it's almost moot point anyhow...
The only time "list.puhed(x)" wou On Mar 29, 2013, at 4:40 AM, Joao S. O. Bueno jsbueno@python.org.br wrote:
On 27 March 2013 23:29, Shane Green shane@umbrellacode.com wrote:
I'm not sure if there's anything inherently wrong with this idea, and I am well aware how incredibly easy it is to implement as an extension of the built-ins, but, I find it very useful to have variations of list().append(obj) and set().add(obj) that return, obj. I've never come up with perfect method names; sometimes I go with past-tense versions, "added" and "appended" (or "pushed"). I often use these methods in places where performance is a factor, such as using a set() to filter repeats from an input sequence.
I was thinking it may be worth considering adding it to core, or perhaps creating collections types with these features, so they are high performance and standardized. They can be particularly useful in generator recipes, etc.
I understand that the equivalent methods in other languages - sometimes frameworks int hose languages - allow one to chain method calls on the parent object, so they can happly write things along:
list().append(1).append(0).sort().append(2)
Python style, as well placed on the thread is that methods that perform changes to the underlying object return None, thus not allowing such constructs to mutable objects - even though one can happily do something like:
image_name = url.split("/")[-1].split(".")[0]
You can easily have the former behavior if you wrap your object in a construct that, whenever a called method would return "None", returns the original object itself. That could be placed in a utility module - and probably there is even some "MyHacks" package on pypi with functionality like that.
If a naive implementation fits your needs, this one would work:
class Chain: def __init__(self, obj, root=None): self.__obj = obj def __getattr__(self, attr): val = getattr(self.__obj, attr) if callable(val): self.__callable = val return self return val def __call__(self, *args, **kw): val = self.__callable(*args, **kw) if val is None: return self return val
a = [] Chain(a).append(5).append(6).append(-1).sort().append(3)
<__main__.Chain object at 0x12b6f50>
a
[-1, 5, 6, 3]
I'd be -0 for something like that on the stlib, though - but if it was there, I'd look around "functools" (but it is obviously more like an "objecttool")
js -><-

Shane Green writes:
Given my recommendation: X.pushed(Y) -> X IFF (and-only-if) Y is X.
However, X.pushed(Y) -> Y is always true.
FWIW, Steve McConnell (Code Complete) recommends that
X.pushed(foo())
quite often should be written as
foo_value = foo() # but with a descriptive name! X.pushed(foo_value) # sic, probably pushed -> push?
even if "foo_value" is only used once. It's not like you save anything but one line by putting foo() inside the parentheses, it's just a name binding that is resolved by the compiler anyway. My understanding of Guido's decisions is that he agrees with McConnell on this point.
participants (8)
-
Bruce Leban
-
Cameron Simpson
-
Guido van Rossum
-
Joao S. O. Bueno
-
MRAB
-
Shane Green
-
Stephen J. Turnbull
-
Terry Reedy