Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

basic language question

14 views
Skip to first unread message

Stephan Diehl

unread,
Sep 4, 2003, 12:24:58 PM9/4/03
to
Once in a while, I get bitten by the fact, that mutating list methods such
as 'append' or 'extend' return None instead of the (mutated) list itself.
Is there a compelling reason for that? I googled around, but didn't find
anything about this.
Thanks

Stephan

Daniel Dittmar

unread,
Sep 4, 2003, 12:33:01 PM9/4/03
to

The reasons described for sort ()
<http://www.python.org/doc/faq/general.html#why-doesn-t-list-sort-return-the
-sorted-list> also apply to append and extend.

Daniel

Stephan Diehl

unread,
Sep 4, 2003, 12:53:08 PM9/4/03
to
Daniel Dittmar wrote:

Thanks for the link. I don't find the given reason really compelling,
though. (but that's mine, not your problem)

>
> Daniel

Stephan

Terry Reedy

unread,
Sep 4, 2003, 12:55:27 PM9/4/03
to

"Stephan Diehl" <stepha...@gmx.net> wrote in message
news:bj7ots$48j$06$1...@news.t-online.com...

Some people, including GvR, think in-place mutators should not return
anything to distinguish them from methods which return a new object
and leave original untouched. One argument, I presume based on some
experience, is that if both types of method look the same, people will
forget difference, leading to obscure bugs. Others, confident in
their ability to remember and not make mistakes, want mutated object
returned so they chain methods togethers. While Python is generally a
consenting-adults language, this is one place where Guido opted for
the supposedly 'safer' choice.

'Returners' could wrap no-return mutators with functions or
derived-class methods that do return the object, but I have never seen
anyone post a complete module that does so for, say, all list
mutators.

Terry J. Reedy


Stephan Diehl

unread,
Sep 4, 2003, 1:31:47 PM9/4/03
to
Terry Reedy wrote:

>
> "Stephan Diehl" <stepha...@gmx.net> wrote in message
> news:bj7ots$48j$06$1...@news.t-online.com...
>> Once in a while, I get bitten by the fact, that mutating list
> methods such
>> as 'append' or 'extend' return None instead of the (mutated) list
> itself.

[...]

>
> 'Returners' could wrap no-return mutators with functions or
> derived-class methods that do return the object, but I have never seen
> anyone post a complete module that does so for, say, all list
> mutators.

That's actually a nice idea. I might just do that.
>
> Terry J. Reedy

Stephan

Michael Peuser

unread,
Sep 4, 2003, 2:58:04 PM9/4/03
to

"Stephan Diehl" <stepha...@gmx.net> schrieb im Newsbeitrag
news:bj7ots$48j$06$1...@news.t-online.com...

There is a very old religious law saying: Thou shalt not cause side effects
by a function.
There is some wisdom in it but it is easily forgotten with languages which
do not differ between functions and routines.

Kindly
Michael P


John Roth

unread,
Sep 4, 2003, 3:32:47 PM9/4/03
to

"Michael Peuser" <mpe...@web.de> wrote in message
news:bj820r$8up$05$1...@news.t-online.com...

Like most "religious laws," it's a rule that has a multitude of exceptions.
You can't do I/O without having side effects, something that the designers
of functional languages have learned, sometimes the hard way.

As far as I'm concerned, this is one of those things that Ruby did
right. By convention, methods that modify the object as a side effect
have names that end with an "!" (exclamation point.) Ruby usually supplies
two methods in such cases, one that modifies the object, and one that
creates a copy and then modifies the copy. Both methods return the
modified or new object.

It obeys the Principle of Least Surprise because if you forget the
"!", you get the slow version that does a copy and leaves the original
intact.

Nice as the solution is, it's impossible to import it into Python
cleanly without introducing catastrophic backward incomaptibilities.

John Roth
>
> Kindly
> Michael P
>
>


Michael Peuser

unread,
Sep 4, 2003, 4:27:36 PM9/4/03
to

"John Roth" <newsg...@jhrothjr.com>
> "Michael Peuser" <mpe...@web.de>

> > There is a very old religious law saying: Thou shalt not cause side
>> effects by a function.
> > There is some wisdom in it but it is easily forgotten with languages
which
> > do not differ between functions and routines.
>
> Like most "religious laws," it's a rule that has a multitude of
exceptions.
> You can't do I/O without having side effects, something that the designers
> of functional languages have learned, sometimes the hard way.

Stream I/O can never be done right in functional languages. The only
solution is to use something like memory mapped files (which I do more and
more ...)

But look at this mess:
twolines=f.readline()+f.readline()
There are solutions in some languages with _routines_ and
_call-by-reference_ but it it is generally very clumpy.

On the other there is a growing popularity of generators and iterators which
by concept are *heretic*

Kindly
Michael P

Stephan Diehl

unread,
Sep 5, 2003, 4:40:48 AM9/5/03
to
Stephan Diehl wrote:

o.k., the following short code would give you a list class, that returns
'self' when invoking any of the mutating methods.
The solution involves a metaclass and I wouldn't consider this code more as
an example than an industrial strength solution (for example, at the
moment, you couldn't overload any of these methods)
------------------------------------------------------------------
def wrapedmeth(classname,meth):
def _meth(self,*argl,**argd):
getattr(super(globals()[classname],self),meth)(*argl,**argd)
return self

return _meth

class ReturnMeta(type):
def __new__(cls,classname,bases,classdict):
wrap = classdict.get('return_self_super_methods')
if wrap is not None:
for method in wrap:
classdict[method] = wrapedmeth(classname,meth)
return super(ReturnMeta,cls).__new__(cls,classname,bases,classdict)

class mylist(list):
__metaclass__ = ReturnMeta
return_self_super_methods = ['append',
'extend',
'insert',
'remove',
'reverse',
'sort']


if __name__ == '__main__':
print 'l = [1,2]'
print 'mylist: print l.append(3)'
l = mylist([1,2])
print l.append(3)
print 'list: print l.append(3)'
l = [1,2]
print l.append(3)
------------------------------------------------------------------------------

have fun

Stephan

Jacek Generowicz

unread,
Sep 5, 2003, 8:31:27 AM9/5/03
to
Stephan Diehl <stepha...@gmx.net> writes:

> Stephan Diehl wrote:
>
> > Terry Reedy wrote:
> >
> >> 'Returners' could wrap no-return mutators with functions or
> >> derived-class methods that do return the object, but I have never seen
> >> anyone post a complete module that does so for, say, all list
> >> mutators.
> >
> > That's actually a nice idea. I might just do that.
>
> o.k., the following short code would give you a list class, that returns
> 'self' when invoking any of the mutating methods.

OK, here's a variaton on the theme, just wraps the object in-place in
a way which makes it return self. This way you can get the desired
effect on any object you get, without pre-meditation, so you can still
fit the call chain on one line (wasn't that the point?)

> The solution involves a metaclass

Mine avoids them altogether.

> and I wouldn't consider this code more as an example than an
> industrial strength solution

Ditto.


class returner:

def __init__(self, object):
self.object = object

def __getattr__(self, name):
def proxy(*args, **kwds):
getattr(self.object, name)(*args, **kwds)
return self.object
return proxy

lst = [1,2,3]
print lst.append(4) # Here you get None
print returner(lst).append(5) # Here you get the modified list.

Jacek Generowicz

unread,
Sep 5, 2003, 8:39:38 AM9/5/03
to
Jacek Generowicz <jacek.ge...@cern.ch> writes:

> OK, here's a variaton on the theme, just wraps the object in-place in
> a way which makes it return self.

I meant "makes any calls to methods return the original object in its
new state."

> This way you can get the desired effect on any object you get,
> without pre-meditation, so you can still fit the call chain on one
> line (wasn't that the point?)

Of course, the chaining should work in Stephan's example too.

Alex Martelli

unread,
Sep 5, 2003, 9:23:52 AM9/5/03
to
Terry Reedy wrote:
...

> 'Returners' could wrap no-return mutators with functions or
> derived-class methods that do return the object, but I have never seen
> anyone post a complete module that does so for, say, all list
> mutators.

What about...:

def wrapReturning(func):
def returningWrapper(*args, **kwds):
func(*args, **kwds)
return args[0]
return returningWrapper


class metaReturner(type):
''' simplified metaReturner: deal with single inheritance only '''

def __new__(mcl, className, classBases, classDict):

# get the "real" base class, then wrap its mutators
for base in classBases:
if not isinstance(base, metaReturner):
for mutator in classDict['__mutators__']:
classDict[mutator] = wrapReturning(getattr(base,
mutator))
break

# delegate the rest to built-in 'type'
return type.__new__(mcl, className, classBases, classDict)

class Returner: __metaclass__ = metaReturner


# example usage

class returnerlist(Returner, list):
__mutators__ = 'sort reverse append extend insert'.split()

print returnerlist('hello').extend('ciao').sort().reverse()


Alex

0 new messages