First script, please comment and advise

P

Pedro Graca

I'm sure this isn't very pythonic; comments and advice appreciated


def curious(text):
""" Return the words in input text scrambled except for the first and last letter. """
new_text = ""
word = ""
for ch in text:
if ch in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
word = word + ch
else:
new_text = new_text + scramble(word)
word = ""
new_text = new_text + ch
return new_text

def scramble(word):
""" scramble word """
from random import randint

if len(word) < 4:
return word
new_word = word[0]

### transform "curious" into ['u', 'r', 'i', 'o', 'u']
letters = []
for ch in word:
letters.append(ch)
del letters[0:1]
del letters[-1]

### why doesn't range(len(letters) - 1, 0, -1) work?
for i in range(len(letters) - 1, -1, -1):
j = randint(0, i)
new_word = new_word + letters[j]
del letters[j]
return new_word + word[-1]

print curious(curious.__doc__)
 
J

Just

Pedro Graca said:
I'm sure this isn't very pythonic; comments and advice appreciated


def curious(text):
""" Return the words in input text scrambled except for the first and
last letter. """
new_text = ""
word = ""
for ch in text:
if ch in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
word = word + ch
else:
new_text = new_text + scramble(word)
word = ""
new_text = new_text + ch
return new_text

def scramble(word):
""" scramble word """
from random import randint

if len(word) < 4:
return word
new_word = word[0]

### transform "curious" into ['u', 'r', 'i', 'o', 'u']
letters = []
for ch in word:
letters.append(ch)
del letters[0:1]
del letters[-1]

### why doesn't range(len(letters) - 1, 0, -1) work?
for i in range(len(letters) - 1, -1, -1):
j = randint(0, i)
new_word = new_word + letters[j]
del letters[j]
return new_word + word[-1]

print curious(curious.__doc__)

def curious(text):
""" Return the words in input text scrambled except for the
first and last letter. """
new_text = ""
word = ""
for ch in text:
if ch.isalpha():
word += ch
else:
new_text += scramble(word)
word = ""
new_text += ch
new_text += scramble(word)
return new_text

def scramble(word):
""" scramble word """
from random import shuffle
if len(word) < 4:
return word
letters = list(word[1:-1])
shuffle(letters)
return word[0] + "".join(letters) + word[-1]

Just
 
B

bearophileHUGS

My version is similar to Just one:

from random import shuffle

def scramble_text(text):
"""Return the words in input text string scrambled
except for the first and last letter."""
def scramble_word(word):
if len(word) < 4: return word
core = list(word[1:-1])
shuffle(core)
return word[0] + "".join(core) + word[-1]

return " ".join(map(scramble_word, text.split()))

print scramble_text(scramble_text.__doc__)
 
J

Just

My version is similar to Just one:

from random import shuffle

def scramble_text(text):
"""Return the words in input text string scrambled
except for the first and last letter."""
def scramble_word(word):
if len(word) < 4: return word
core = list(word[1:-1])
shuffle(core)
return word[0] + "".join(core) + word[-1]

return " ".join(map(scramble_word, text.split()))

print scramble_text(scramble_text.__doc__)

Your text split behaves completely different from the original; I think
it was intentional that any puntuation wasn't affected.

Btw. I find the use of a nested function here completely bogus: you
don't need the surrounding scope.

Just
 
B

bearophileHUGS

Just:
Your text split behaves completely different from the original; I think
it was intentional that any puntuation wasn't affected.

You are right, my versions is simpler but different and worse. Maybe it
can be fixed a little...

Btw. I find the use of a nested function here completely bogus: you
don't need the surrounding scope.

I don't agree, nested functions are useful to better structure your
program: to really nest things that are logically nested, to have just
one thing when you have only one thing to do, and they help you to
avoid polluting the namespace (those were the main purposes of nested
functions in Pascal-like languages).

Bye,
bearophile
 
J

Just

Just:


I don't agree, nested functions are useful to better structure your
program: to really nest things that are logically nested, to have just
one thing when you have only one thing to do, and they help you to
avoid polluting the namespace (those were the main purposes of nested
functions in Pascal-like languages).

Luckily this is Python, not Pascal :)

1. The fact that one function calls the other doesn't mean they're
"logically nested".

2. The helper function is useful on its own, so it is a waste to hide it
inside another.

3. The nested function makes the enclosing function harder to read.

4. I don't buy the namespace pollution argument, we have modules after
all.

5. Overhead: every time a new function object is created, for no good
reason. (But you save a global name lookup, yet that doesn't appear to
weigh up against the function def. [*])

To me, nested functions (in Python) are *only* helpful when using
closures. If you're not using the enclosing scope, you're just
obfuscating things.

Just

[*]
% python2.4 -m timeit -s "def helper(): pass" "def nonnesting():" \
" helper()" "nonnesting()"
1000000 loops, best of 3: 1.92 usec per loop
% python2.4 -m timeit "def nesting():" " def helper(): pass" \
" helper()" "nesting()"
100000 loops, best of 3: 2.04 usec per loop
 
P

Pedro Graca

Just said:
[snip: very un-pythonic code]

def curious(text):
""" Return the words in input text scrambled except for the
first and last letter. """
new_text = ""
word = ""
for ch in text:
if ch.isalpha():

Ah! shorter and clearer than my attempt.
word += ch
else:
new_text += scramble(word)
word = ""
new_text += ch
new_text += scramble(word)

Oops. I failed to test bad input :)
Thanks for the correction.
return new_text

def scramble(word):
""" scramble word """
from random import shuffle
if len(word) < 4:
return word
letters = list(word[1:-1])

list(string) transforms a string in a list ...
shuffle(letters)
return word[0] + "".join(letters) + word[-1]

.... and delimiter.join(list) transforms a list in a string.

I guess I shouldn't have payed attention to the people who said "Don't
bother with the manual, just try it."

I've just downloaded "Dive Into Python" and will print and read it.


Thank you for your corrections and hints for my code.
 
P

Pedro Graca

Just:
[previous post, hand inserted]
def scramble_text(text):
def scramble_word(word):

Btw. I find the use of a nested function here completely bogus: you
don't need the surrounding scope.

I don't agree, nested functions are useful to better structure your
program: to really nest things that are logically nested, to have just
one thing when you have only one thing to do, and they help you to
avoid polluting the namespace (those were the main purposes of nested
functions in Pascal-like languages).

I have never used nested functions before, but I like the idea.
For the code posted earlier I stated the reason I don't like it in
another post to this thread.
 
P

Pedro Graca

My version is similar to Just one:

from random import shuffle

def scramble_text(text):
"""Return the words in input text string scrambled
except for the first and last letter."""
def scramble_word(word):

Nice. You can have functions inside functions.
However I think scramble_word() deserves being a callable function by
itself.

Can a "sub-function" be called directly from outside the defining
function?
if len(word) < 4: return word
core = list(word[1:-1])
shuffle(core)
return word[0] + "".join(core) + word[-1]

return " ".join(map(scramble_word, text.split()))

print scramble_text(scramble_text.__doc__)

Thank you for your version of the scrambler.
 
B

bearophileHUGS

This is different too, but maybe a little better than my last version:

from random import shuffle
from itertools import groupby

def scramble_text(text):
"""Return the words in input text string scrambled,
except for the first and last letter."""
def scramble(word):
if len(word) < 4: return "".join(word)
core = word[1:-1]
shuffle(core)
return "".join([word[0]] + core + [word[-1]])

return "".join(scramble(list(g)) for _,g in groupby(text,
str.isalpha))

print scramble_text(scramble_text.__doc__)

Just, we can discuss a lot, but it's probably of little use, because
different programmers have different style of programming. If you take
someone that is expert in Lisp or Java (and with some Python
experienxe) you can probably find other possibile solutions of this
same little problem. Sometimes you can ask them to write more pythonic
programs, but you cannot ask them to program exactly as you do.

I can answer some of your points, but it's just a relaxed discussion...
Luckily this is Python, not Pascal :)

I agree that Python isn't a Pascal-like language, they are usually
compiled, so nested functions have less overhead, and there are other
differences. But Object Pascal has some good parts too, some things are
better than Python ones. Like the sintax of bitwise operators and
%-like formatting, some parts of the the I/O, etc. Many languages are
designed by intelligent people. Lisp, Mathematica, Perl, Ruby, Delphi,
Haskell, D, Smalltalk, OCaml, ML aren't Python, so you can't copy them,
but sometimes they can teach you how to better program in Python.

1. The fact that one function calls the other doesn't mean they're "logically nested".<

In this situation they were logically nested, because the scramble_word
is a function needed by another one only, in Java the scramble_word
becomes a (private) method of a text scrambling class; this means it is
nested and hided inside a class. Pascal languages were pre-OOP and they
used nested function for similar purposes, that it to hide and
structure functions inside other ones, that in OO languages like Java,
ObjectPascal, Python, etc. you usually do with classes.

2. The helper function is useful on its own, so it is a waste to hide it inside another.

Good luck at finding other uses for that function :)

3. The nested function makes the enclosing function harder to read.

I can read it fine, but I can understand that you are probably less
used to nested functions, so what's true for me isn't true for you.

4. I don't buy the namespace pollution argument, we have modules after all.

I presume Guido agrees that function nesting is another way to avoid
namespace pollution :) There are many ways to do the same thing, to
avoid pollution in one module.

5. Overhead: every time a new function object is created, for no good reason. (But you save a global name lookup, yet that doesn't appear to weigh up against the function def. [*])<

This function isn't going to be used much. If later I find I have to
optimize it, then I don't use a nested function at all, and I just use
a single function that does everything. But until such optimization
needs, I choose the most logical structuration of the program, that
allows me better programming.

To me, nested functions (in Python) are *only* helpful when using closures. If you're not using the enclosing scope, you're just obfuscating things.<

Note that until recent time, Python nested scopes were managed quite
differently (in a bad way), and yet, they regarded the function nesting
useful enough to be kept anyway.
I don't agree with you, but every programmer is diffent, because their
story is different. If you have learnt to program with Java/C then you
probably don't like nested functions. If you started with a Pascal-like
language then probably you like that nesting more. If you start with a
Scheme/Lisp background you progrably write Python programs like the
good Norvig:
http://aima.cs.berkeley.edu/python/utils.py
And so on...

Maybe other people here can say what they think about this topic, it's
just a relaxed discussion.

Bye,
bear hugs,
bearophile
 
B

bearophileHUGS

nested and hided inside a class.

Hidden, sorry :)

Can a "sub-function" be called directly from outside the defining function?

I don't think you can access a nested function in a clean&nice way (and
you can nest classes too, etc). With a little of Python magic maybe
there is a way to do it... But for such purposes probably a module (or
maybe a class with a static method) is better.

Bye,
bearophile
 
S

Scott David Daniels

Hidden, sorry :)
No, and each call to scramble_text defines a new function "scramble".
Further, there is no way to unit test "scramble".

--Scott David Daniels
(e-mail address removed)
 
B

bruno at modulix

Pedro said:
Nice. You can have functions inside functions.
However I think scramble_word() deserves being a callable function by
itself.

Can a "sub-function" be called directly from outside the defining
function?

"directly", no, but since Python's functions are objects, you can return
a function from another function. The nice thing is that the inner
function will carry it's environnement along (it's called a "closure").


def make_adder(add_val=1):
def adder(num):
return num + add_val
return adder

add1 = make_adder()
add1(42)

add42 = make_adder(42)
add42(3)
 
J

Just

In this situation they were logically nested, because the scramble_word
is a function needed by another one only, in Java the scramble_word
becomes a (private) method of a text scrambling class; this means it is
nested and hided inside a class. Pascal languages were pre-OOP and they
used nested function for similar purposes, that it to hide and
structure functions inside other ones, that in OO languages like Java,
ObjectPascal, Python, etc. you usually do with classes.

In Python one usually doesn't try to hide things.

[ ... ]
I presume Guido agrees that function nesting is another way to avoid
namespace pollution :)

I dare you to find out. I'm fairly sure of the answer.
There are many ways to do the same thing, to
avoid pollution in one module.

Nesting functions is not one of them. Factoring a bit of functionality
into a helper function, giving that function a name and sticking it into
a module can _hardly_ be called "pollution". That's what modules are
*for*.

[ ... ]
Note that until recent time, Python nested scopes were managed quite
differently (in a bad way),
Yup.

and yet, they regarded the function nesting
useful enough to be kept anyway.

Nested functions are supported because you _need_ them if you want to
work with closures. Not because it's a good way to structure code in the
*absence* of closures.
I don't agree with you, but every programmer is diffent, because their
story is different. If you have learnt to program with Java/C then you
probably don't like nested functions. If you started with a Pascal-like
language then probably you like that nesting more. If you start with a
Scheme/Lisp background you progrably write Python programs like the
good Norvig:
http://aima.cs.berkeley.edu/python/utils.py

That's pretty good code. Well documented, clearly written, uses some
functional idioms in a sensible way. And no, it doesn't use gratuitous
nested functions.
Maybe other people here can say what they think about this topic, it's
just a relaxed discussion.

Sure, it's just that I feel strongly that your advice is not a good one
(*especially* for beginners).

So I repeat: using nested functions are NOT a good way to factor your
code. (I don't need to add "in Python" there, do I?)

Just
 

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
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top