return statement in functions

H

hokiegal99

I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off. Could someone enlighten me on why or
why not to use a return statement when defining functions? Below is
the bit of code I was told should return something:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
print "Number of Files Examined: ", file_count
print "Number of Folders Examined: ", dir_count
print "Total Number of FS Objects:", file_count + dir_count
 
P

Paul Rubin

I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off. Could someone enlighten me on why or
why not to use a return statement when defining functions? Below is
the bit of code I was told should return something:

It will return None.
 
F

Francis Avila

hokiegal99 wrote in message
I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off.

Functions return something in Python, by definition. If you leave the
return statement off, Python inserts an implicit 'return None' to the end of
the text of your function.
Could someone enlighten me on why or
why not to use a return statement when defining functions?

This is a philosophic question. The final end of a function is to take
input and return output which is somehow based upon that input. The
functional *construct* can be abused every which way (often validly) to
violate any part of that statement: it can take arguments which don't
matter; it can return things which are unrelated to the arguments; it can
have all sorts of side effects that the caller isn't interested in, etc.
Below is
the bit of code I was told should return something:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
print "Number of Files Examined: ", file_count
print "Number of Folders Examined: ", dir_count
print "Total Number of FS Objects:", file_count + dir_count

The code is in poor style simply because it does multiple things at once in
a way that is not terribly modular: namely, it does file/dir counts, and it
prints results.

Better would be this:

def fs_object_count(path):
"""Return (numdirs, numfiles) in path and its subdirectories."""
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
return dir_count, file_count

dn, fn = fs_object_count('mypath')
print "Number of Files:", fn
print "Number of Directories:", dn
print "Total:", fn+dn

You can have more complex/prettier output logic if you want, but the point
is to make your functions as general as possible, while doing only one
thing. (And that's not a contradiction!)
 
B

BW Glitch

hokiegal99 said:
I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off. Could someone enlighten me on why or
why not to use a return statement when defining functions? Below is
the bit of code I was told should return something:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
print "Number of Files Examined: ", file_count
print "Number of Folders Examined: ", dir_count
print "Total Number of FS Objects:", file_count + dir_count

All Python functions return None by default.

--
Glitch

-----BEGIN TF FAN CODE BLOCK-----
G+++ G1 G2+ BW++++ MW++ BM+ Rid+ Arm-- FR+ FW-
#3 D+ ADA N++ W OQP MUSH- BC- CN++ OM P75
-----END TF FAN CODE BLOCK-----

"I can suffer your treachery, Lieutenant ... but NOT your
incompetence! ... Treachery requires NO mistakes."
-- Megatron to Tarantulas, "Master Blaster"
 
P

Peter Otten

hokiegal99 said:
I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off. Could someone enlighten me on why or
why not to use a return statement when defining functions? Below is
the bit of code I was told should return something:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
print "Number of Files Examined: ", file_count
print "Number of Folders Examined: ", dir_count
print "Total Number of FS Objects:", file_count + dir_count

I think I was the guy who told that. I did not mean that you should always
add an explicit return statement, you just posted a slightly different
function

[hokiegal99 in earlier post]
def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
for fname in files:
file_count += len(fname)
for dname in dirs:
dir_count += len(dname)
fs_object_count()


which attempted to calculate file and dir count but did neither return these
values nor print them. Thus the file_count and dir_count were lost and the
function useless.

As to the advantage of returning the result instead of printing it, Francis
Avila has already covered that.

Peter
 
H

hokieghal99

Francis said:
hokiegal99 wrote in message



Functions return something in Python, by definition. If you leave the
return statement off, Python inserts an implicit 'return None' to the end of
the text of your function.




This is a philosophic question. The final end of a function is to take
input and return output which is somehow based upon that input. The
functional *construct* can be abused every which way (often validly) to
violate any part of that statement: it can take arguments which don't
matter; it can return things which are unrelated to the arguments; it can
have all sorts of side effects that the caller isn't interested in, etc.




The code is in poor style simply because it does multiple things at once in
a way that is not terribly modular: namely, it does file/dir counts, and it
prints results.

Better would be this:

def fs_object_count(path):
"""Return (numdirs, numfiles) in path and its subdirectories."""
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
return dir_count, file_count

dn, fn = fs_object_count('mypath')
print "Number of Files:", fn
print "Number of Directories:", dn
print "Total:", fn+dn

You can have more complex/prettier output logic if you want, but the point
is to make your functions as general as possible, while doing only one
thing. (And that's not a contradiction!)

This was very helpful to me. Thank you for taking the time to explain it!!!
 
H

hokieghal99

Peter said:
hokiegal99 wrote:

I was told earlier (w/o explanation) that functions should return
something. I was under the impression that it was OK with Python to
leave the return statement off. Could someone enlighten me on why or
why not to use a return statement when defining functions? Below is
the bit of code I was told should return something:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
print "Number of Files Examined: ", file_count
print "Number of Folders Examined: ", dir_count
print "Total Number of FS Objects:", file_count + dir_count


I think I was the guy who told that. I did not mean that you should always
add an explicit return statement, you just posted a slightly different
function

[hokiegal99 in earlier post]

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
for fname in files:
file_count += len(fname)
for dname in dirs:
dir_count += len(dname)
fs_object_count()



which attempted to calculate file and dir count but did neither return these
values nor print them. Thus the file_count and dir_count were lost and the
function useless.

Yes, I see this now. It makes a lot of sense. In an earlier reply from
Francis, it was made clear to me that functions work best when they do
one thing well and when it's evident what they are attempting to do. By
taking this approach to defining them, functions are very modular and
easier to comprehend, no? So, they can then return their value to
another function that maybe was written to print the information nicely
or whatever. Is that a good way of thinking about functions?
 
H

hokieghal99

OK, I took the good advice I was given here and made my functions very
specific. I made them all return something sane... something one would
expect them to return. I even wrote a function to handle the report
writing itself, but when it gets the output from the other functions to
write the report file, it only writes this:

<function fs_object_count at 0x405d84c4>
<function clean_dir_names at 0x405d8534>
<function clean_file_names at 0x405d856c>
<function clean_dir_spaces at 0x405d85a4>
<function clean_file_spaces at 0x405d85dc>
<function doc_extension at 0x405d8614>
<function xls_extension at 0x405d864c>
<function pdf_extension at 0x405d8684>
<function ppt_extension at 0x405d86bc>
<function wpd_extension at 0x405d86f4>
<function jpg_extension at 0x405d872c>

I would like for it to write out what the functions returned. Obviously,
I am missing something very fundamental here. Could someone hit me in
the head with a hammer and help me understand this?

Here is an example of what I'm trying to do. Please be gentle with me.
From experience, I know that intelligence and modesty do not mix, but
make an exception for me, just this once...

The ugly report function:

def report(a,b,c,d,e,f,g,h,i,j,k):
outputFile = open('report.txt', 'w')
print >> outputFile, a
print >> outputFile, b
print >> outputFile, c
print >> outputFile, d
print >> outputFile, e
print >> outputFile, f
print >> outputFile, g
print >> outputFile, h
print >> outputFile, i
print >> outputFile, j
print >> outputFile, k
outputFile.close()

This function is an exception to my new "ALWAYS RETURN SOMETHING SANE"
motto as I don't care what this returns.

Below is the first function where the output is taken as 'a' by the
report function:

def fs_object_count(path):
file_count = 0
dir_count = 0
for root, dirs, files in os.walk(path):
file_count += len(files)
dir_count += len(dirs)
return dir_count, file_count

Here's one other example:

def doc_extension(path):
for root, dirs, files in os.walk(path, topdown=False):
for fname in files:
doc_new = fname + '.doc'
ms_id = string.find(file(os.path.join(root,fname),
'rb').read(), 'Microsoft')
doc_id = string.find(file(os.path.join(root,fname),
'rb').read(), 'Word.Document.')
ext = os.path.splitext(fname)
if not ext[1] and doc_id >=1 and ms_id >=1:
newpath = os.path.join(root,doc_new)
oldpath = os.path.join(root,fname)
os.renames(oldpath,newpath)
return oldpath, newpath

Here's how I call the report function:

report(fs_object_count,clean_dir_names,etc., etc.)

Thanks!!!
 
I

Irmen de Jong

hokieghal99 said:
Here's how I call the report function:

report(fs_object_count,clean_dir_names,etc., etc.)

You're forgetting to actually *call* the other functions too,
(you just pass the functions themselves, as you see in your output,
Python prints the function objects that you pass to the report
function).
So try this:

report(fs_object_count(),clean_dir_names(),...)

these you forgot: ^^ ^^


--Irmen
 
J

Jp Calderone

OK, I took the good advice I was given here and made my functions very
specific. I made them all return something sane... something one would
expect them to return. I even wrote a function to handle the report
writing itself, but when it gets the output from the other functions to
write the report file, it only writes this:

<function fs_object_count at 0x405d84c4>
<function clean_dir_names at 0x405d8534>
<function clean_file_names at 0x405d856c>
<function clean_dir_spaces at 0x405d85a4>
<function clean_file_spaces at 0x405d85dc>
<function doc_extension at 0x405d8614>
<function xls_extension at 0x405d864c>
<function pdf_extension at 0x405d8684>
<function ppt_extension at 0x405d86bc>
<function wpd_extension at 0x405d86f4>
<function jpg_extension at 0x405d872c>

I would like for it to write out what the functions returned. Obviously,
I am missing something very fundamental here. Could someone hit me in
the head with a hammer and help me understand this?

Here is an example of what I'm trying to do. Please be gentle with me.
From experience, I know that intelligence and modesty do not mix, but
make an exception for me, just this once...

The ugly report function:

def report(a,b,c,d,e,f,g,h,i,j,k):
outputFile = open('report.txt', 'w')
print >> outputFile, a
print >> outputFile, b
print >> outputFile, c
print >> outputFile, d
print >> outputFile, e
print >> outputFile, f
print >> outputFile, g
print >> outputFile, h
print >> outputFile, i
print >> outputFile, j
print >> outputFile, k
outputFile.close()


Ugly *indeed*

def report(*work):
outputFile = open('report.txt', 'w')
for f in work:
outputFile.write(str(work()) + '\n')
outputFile.close()

Notice how it does "work()", not "work". This is the root of the trouble
in your solution - you aren't calling the functions.

Jp

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/6GcUedcO2BJA+4YRAiTOAJ9zhR0yLBo0K/Ghu0rmgoKAWQchwgCgn3Ze
rQSgdVoPhaH0HOqreTklUtA=
=8DPq
-----END PGP SIGNATURE-----
 
J

Jp Calderone

def report(*work):
outputFile = open('report.txt', 'w')
for f in work:
outputFile.write(str(work()) + '\n')
outputFile.close()

Of course, I meant to be calling "f()" in the loop.

Jp

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/6GfxedcO2BJA+4YRAiA2AKCIA7Dq+cvmuc2AsFib7r/TruKPnACgqeuh
oho4Iz/MCey1kBydqqTgYKs=
=51Sz
-----END PGP SIGNATURE-----
 
H

hokieghal99

Irmen said:
You're forgetting to actually *call* the other functions too,
(you just pass the functions themselves, as you see in your output,
Python prints the function objects that you pass to the report
function).
So try this:

report(fs_object_count(),clean_dir_names(),...)

these you forgot: ^^ ^^


--Irmen

That did it. Many thanks to you!!!
 
H

hokieghal99

Irmen said:
You're forgetting to actually *call* the other functions too,
(you just pass the functions themselves, as you see in your output,
Python prints the function objects that you pass to the report
function).
So try this:

report(fs_object_count(),clean_dir_names(),...)

these you forgot: ^^ ^^


--Irmen

That works, but it only returns the first item in the list. The
functions are returning a list of paths or of files/dirs. For example,
the function 'clean_dir_names' should return a complete list of
directory names that it has removed bad characters from.

def clean_dir_names(path):
for root, dirs, files in os.walk(path, topdown=False):
for dname in dirs:
new_dname = re.sub(bad,'-' ,dname)
if new_dname != dname:
newpath = os.path.join(root, new_dname)
oldpath = os.path.join(root, dname)
os.renames(oldpath, newpath)
return new_dname

How do I make the report function see all of the contents of the list
and not just the first item of the list?
 
J

Jp Calderone

[snip]

That works, but it only returns the first item in the list. The
functions are returning a list of paths or of files/dirs. For example,
the function 'clean_dir_names' should return a complete list of
directory names that it has removed bad characters from.

def clean_dir_names(path):
for root, dirs, files in os.walk(path, topdown=False):
for dname in dirs:
new_dname = re.sub(bad,'-' ,dname)
if new_dname != dname:
newpath = os.path.join(root, new_dname)
oldpath = os.path.join(root, dname)
os.renames(oldpath, newpath)
return new_dname

How do I make the report function see all of the contents of the list
and not just the first item of the list?

No. "return new_dname" terminates execution of the function. If you want
many results, you need to collect them in a list and return the list, or use
a generator.

Jp


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/6IVuedcO2BJA+4YRAljEAJ97scFRe1svI7Ik8kYW9IQngzY7vACdE8n1
jqAJMDQC8aErVlnaTuBsOKo=
=Sjpb
-----END PGP SIGNATURE-----
 
H

hokieghal99

Jp said:
[snip]

That works, but it only returns the first item in the list. The
functions are returning a list of paths or of files/dirs. For example,
the function 'clean_dir_names' should return a complete list of
directory names that it has removed bad characters from.

def clean_dir_names(path):
for root, dirs, files in os.walk(path, topdown=False):
for dname in dirs:
new_dname = re.sub(bad,'-' ,dname)
if new_dname != dname:
newpath = os.path.join(root, new_dname)
oldpath = os.path.join(root, dname)
os.renames(oldpath, newpath)
return new_dname

How do I make the report function see all of the contents of the list
and not just the first item of the list?


No. "return new_dname" terminates execution of the function. If you want
many results, you need to collect them in a list and return the list, or use
a generator.

Jp

That's odd, it seems silly to terminate something that should be
recursive (os.walk) before it's finished being recursive. What's the
logic behind that idea?
 
J

Jp Calderone

Jp Calderone wrote:
[snip]
No. "return new_dname" terminates execution of the function. If you
want many results, you need to collect them in a list and return the
list, or use a generator.

Jp

That's odd, it seems silly to terminate something that should be
recursive (os.walk) before it's finished being recursive. What's the
logic behind that idea?

That is what "return" does. Always. :) With no regard for context,
recursive or not (the implementation of os.walk itself may be recursive, or
it may not be, but I don't think it has any bearing on what "return" should
do, since the return isn't even inside the definition of os.walk!)

If this is surprising, you should probably take some time to review the
fundamentals of the language. I would recommend either the official
tutorial or the book "How to Think Like a Computer Scientist":

http://www.python.org/doc/tut/

http://www.ibiblio.org/obp/thinkCSpy/

Jp

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.3 (GNU/Linux)

iD8DBQE/6JxzedcO2BJA+4YRAskEAKCaGGTEpz8Cjg4NRO7lTIt0tMuJbgCguTGc
U5Mbe2B0IDNyS9doHObVhUs=
=ky67
-----END PGP SIGNATURE-----
 
C

Carl Banks

Francis said:
hokiegal99 wrote in message


Functions return something in Python, by definition. If you leave the
return statement off, Python inserts an implicit 'return None' to the end of
the text of your function.


It seems that Python inserts the implicit "return None" even if you do
return a value.


Python 2.2.3c1 (#12, May 27 2003, 21:32:04)
[GCC 2.95.4 20011002 (Debian prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information..... return 1
.... 0 SET_LINENO 1

3 SET_LINENO 2
6 LOAD_CONST 1 (1)
9 RETURN_VALUE
10 LOAD_CONST 0 (None)
13 RETURN_VALUE

today's-trivia-tidbit-brought-to-you-by-truly yr's
 
S

Skip Montanaro

Carl> It seems that Python inserts the implicit "return None" even if
Carl> you do return a value.

Yes, that's true. A fairly straightforward basic block analysis can
eliminate the trailing LOAD_CONST None/RETURN_VALUE pair, but all you'd be
doing is reducing the size of the bytecode, not eliminating instructions
which would otherwise be executed. Dead code elimination in other parts of
the bytecode is more interesting because it might lead an optimizer to
consider other possible optimizations.

Skip
 

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

Forum statistics

Threads
474,173
Messages
2,570,937
Members
47,481
Latest member
ElviraDoug

Latest Threads

Top