Stephan
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
Thanks for the link. I don't find the given reason really compelling,
though. (but that's mine, not your problem)
>
> Daniel
Stephan
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" <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
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
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
>
>
> > 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
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
> 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.
> 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.
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