Why return None?

M

Martin DeMello

It seems to be a fairly common pattern for an object-modifying method to
return None - however, this is often quite inconvenient.

For instance

def f(lst1, lst2):
g((lst1 + lst2).reverse()) # doesn't work!

you need to say

def f(lst1, lst2):
a = lst1 + lst2
a.reverse()
g(a)

this is actually getting in my way a lot when scripting Blender - for
instance, I can't say move(Vector([a,b,c]).normalize()), I have to do
a = Vector([a,b,c])
a.normalize()
move(a)

but it seems to be recommended practice rather than a fault in the
Blender API, since the standard list does it. Is there any drawback to
returning self rather than None?

martin

c
 
A

Anthony Baxter

It seems to be a fairly common pattern for an object-modifying method to
return None - however, this is often quite inconvenient.

list.reverse() modifies the list in place. The python idiom is that
these don't return a reference to the modified list. Although note the
new list.sorted() method in 2.4...

Anthony
 
M

Max M

Martin said:
It seems to be a fairly common pattern for an object-modifying method to
return None - however, this is often quite inconvenient.


With newstyle classe you can subclass the list class and make some
functions return self.

class List(list):

def sort(self):
list.sort(self)
return self


l = List([1,2,4,2,5,2,6])
print l.sort()

Well ok. Sort is a bad example, as you could just use sorted() insted.


regards Max M
 
M

Martin DeMello

Anthony Baxter said:
list.reverse() modifies the list in place. The python idiom is that
these don't return a reference to the modified list. Although note the

Yes, but why? I mean, is there either an advantage to returning None or
some inherent danger in returning self?

martin
 
P

Peter Otten

Martin said:
Yes, but why? I mean, is there either an advantage to returning None or
some inherent danger in returning self?

martin

I think Guido would rather have newbies stumbling over
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: zip argument #2 must support iteration

than
.... def reverse(self):
.... list.reverse(self)
.... return self
....[('c', 'c'), ('b', 'b'), ('a', 'a')]

The latter is more likely to remain undetected until some damage is done.
Still, I would prefer it.

Peter
 
B

Benjamin Niemann

Martin said:
Yes, but why? I mean, is there either an advantage to returning None or
some inherent danger in returning self?
The danger is that you might forget about the side effect.

l = [1, 2, 4, 3]
sorted = l.sort()
.... # do something with sorted list
print l # I want to unsorted list here, but oops...

In place object modification is often faster, but you loose the original
data - this should be explicitly visible in the code
 
M

Max M

Martin said:
Yes, but why? I mean, is there either an advantage to returning None or
some inherent danger in returning self?

It is a design philosophy. Explicit is better than implicit.

what would you expect a_list.sort() to return?

If it returns a list, you would expect it to be a sorted copy of the
list. Not the list itself.

But for performance reasons the list is sorted in place.

So if you modify the list in place, why should sort() then return the list?

That the sort() method returns a None is actually a pedagocical tool to
tell the programmer that the list is modified in place.

If it had returned the list the programmer would later be surprised to
find that the list had been modified. It would seem like hard to find
nasty side effect. The way it is now is very explicit and easy to find out.


regards Max M
 
D

Dan Sommers

Yes, but why? I mean, is there either an advantage to returning None or
some inherent danger in returning self?

If list.sort returned the sorted list, how many lists would there be
after this code executed?

original_list = a_function_that_returns_a_list( )
sorted_list = original_list.sort( )

HTH,
Dan
 
M

Michael Hudson

Anthony Baxter said:
list.reverse() modifies the list in place. The python idiom is that
these don't return a reference to the modified list. Although note the
new list.sorted() method in 2.4...

It's a builtin now (as is reversed).

Cheers,
mwh
 
M

Martin DeMello

Dan Sommers said:
If list.sort returned the sorted list, how many lists would there be
after this code executed?

original_list = a_function_that_returns_a_list( )
sorted_list = original_list.sort( )

One - sorted_list would be a reference to the (now sorted) original
list. The newbie problem could be solved by having <verb> and <verbed>
versions of each destructive method - i.e. list.sort() to sort the list
in-place and return it, and list.sorted() to return a new, sorted list,
and experienced users could chain methods and all that other good stuff.

martin
 
R

Roy Smith

Martin DeMello said:
One - sorted_list would be a reference to the (now sorted) original
list. The newbie problem could be solved by having <verb> and <verbed>
versions of each destructive method - i.e. list.sort() to sort the list
in-place and return it, and list.sorted() to return a new, sorted list,
and experienced users could chain methods and all that other good stuff.

martin

The problem with sort() vs. sorted() is that it's obscure. I'm not a
big fan of quoting gospel, but it seems to me that it violates the
gospel of "explicit is better than implicit".

If I took 10 programmers who were generally familiar with typical OO
syntax, but not specifically with Python and asked them what list.sort()
and list.sorted() did, I imagine all of them would say something like,
"Well, they both obviously sort a list, and clearly the existence of two
different methods means they do it differently somehow, but I can't for
the life of me guess what the difference is".

Explicit would be something like having two methods named in ways that
people could figure out without having to RTFM:

list.sort () ==> returns a sorted copy of the list
list.sort_in_place () ==> sorts in place, returns None

or if you prefer to do it the other way:

list.sort () ==> sorts in place, returns None
list.sort_copy () ==> returns a sorted copy of the list

But, personally, I find that all kind of silly; IMHO, list.sort ()
should have just returned self to begin with and then we wouldn't be
having this discussion.

heretic-ly yours.
 
D

Dave Opstad

Max M said:
It is a design philosophy. Explicit is better than implicit.

what would you expect a_list.sort() to return?

If it returns a list, you would expect it to be a sorted copy of the
list. Not the list itself.

But for performance reasons the list is sorted in place.

So if you modify the list in place, why should sort() then return the list?

That the sort() method returns a None is actually a pedagocical tool to
tell the programmer that the list is modified in place.

If it had returned the list the programmer would later be surprised to
find that the list had been modified. It would seem like hard to find
nasty side effect. The way it is now is very explicit and easy to find out.

But isn't the fact that the list is modified in place incidental to the
fact of sorting? One is an implementation detail, and the other is the
semantic meaning you're trying to express.

In my opinion, it would make more sense to have:

[1, 3, 4, 2].sort() return [1, 2, 3, 4]
(1, 3, 4, 2).sort() return (1, 2, 3, 4)
'1342'.sort() return '1234'

and so on. As it is, we have sort working on lists but not on immutable
sequences, which is inconvenient at times.

Just my tuppence.
Dave
 
M

Mel Wilson

But isn't the fact that the list is modified in place incidental to the
fact of sorting? One is an implementation detail, and the other is the
semantic meaning you're trying to express.

It's not incidental to the fact that the original list is
"destroyed" by the sorting operation.

Regards. Mel.
 
D

Dan Bishop

Dave Opstad said:
[discussion of why list.sort returns None instead of a list]

But isn't the fact that the list is modified in place incidental to the
fact of sorting? One is an implementation detail, and the other is the
semantic meaning you're trying to express.

The fact that the list is sorted in-place *is* semantically
meaningful. It's often important to know whether the original list is
sorted or not.
 
M

Martin DeMello

Dave Opstad said:
But isn't the fact that the list is modified in place incidental to the
fact of sorting? One is an implementation detail, and the other is the
semantic meaning you're trying to express.

Nope, especially since objects are passed by reference.
In my opinion, it would make more sense to have:

[1, 3, 4, 2].sort() return [1, 2, 3, 4]
(1, 3, 4, 2).sort() return (1, 2, 3, 4)
'1342'.sort() return '1234'

and so on. As it is, we have sort working on lists but not on immutable
sequences, which is inconvenient at times.

This again would be a difference between sort() and sorted().

My main point is that returning None is pretty useless - it 'wastes' the
return value, and doesn't allow method chaining. Returning self allows
you to either use or discard the return value depending on whether
you're interested in it, whereas using None doesn't give you any choice.

martin
 
E

Erik Max Francis

Martin said:
Yes, but why? I mean, is there either an advantage to returning None
or
some inherent danger in returning self?

The "inherent danger" is that the user might think that it returns a new
object rather than mutating the original. Returning a new object vs.
mutating the argument and returning None is merely a convention, but
it's one used consistently in the Python standard library.
 

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

No members online now.

Forum statistics

Threads
474,431
Messages
2,571,677
Members
48,796
Latest member
Greg L.

Latest Threads

Top