Easy function, please help.

N

Nanderson

def num_digits(n):
count = 0
while n:
count = count + 1
n = n / 10
return count

This is a function that basically says how many digits are in a
number. For example,4

This function counts the number of decimal digits in a positive
integer expressed in decimal format. I get this function ALMOST
completely. The only thing I don't understand is why it eventually
exits the loop, and goes off to the second branch. "while n" is
confusing me. What I am thinking is that if someone puts "while n" the
loop would be infinite. I get what is happening in the function, and I
understand why this would work, but for some reason it's confusing me
as to how it is exiting the loop after a certain number of times. Help
is appreciated, thanks.
 
M

Michael Hrivnak

Your function only works if n is an integer. Example:
325

When doing integer division, python will throw away the remainder and
return an int. Using your example of n==44, 44/10 == 4 and 4/10 == 0

Before each iteration of the while loop, the given expression (in this
case just n) is evaluated as a boolean. Your function would act the
same if it looked like this:

def num_digits(n):
count = 0
while bool(n):
count = count + 1
n = n / 10
return count

0 of course evaluates to False as a boolean, which is why the while loop stops.

Just for kicks, this function would work about as well:

def num_digits(n):
return len(str(n))

And if either of these were a real function you planned to use, you'd
probably want to either cast n as an int ( int(n) ) or at least check
its type:

if not isinstance(n, int):
raise TypeError("WTF you didn't pass me an int")

Michael
 
N

Nanderson

Your function only works if n is an integer.  Example:


325

When doing integer division, python will throw away the remainder and
return an int.  Using your example of n==44, 44/10 == 4 and 4/10 == 0

Before each iteration of the while loop, the given expression (in this
case just n) is evaluated as a boolean.  Your function would act the
same if it looked like this:

def num_digits(n):
  count = 0
  while bool(n):
      count = count + 1
      n = n / 10
  return count

0 of course evaluates to False as a boolean, which is why the while loop stops.

Just for kicks, this function would work about as well:

def num_digits(n):
   return len(str(n))

And if either of these were a real function you planned to use, you'd
probably want to either cast n as an int ( int(n) ) or at least check
its type:

if not isinstance(n, int):
   raise TypeError("WTF you didn't pass me an int")

Michael

Ah, thank you very much! I forgot completely about how python does
division, do this confused me quite a bit. Thanks for the help, and
the only reason I wrote the function this way is because I'm learning
to program, and I'm try to do the exercises of the tutorial I'm
reading. It really is for learning purposes. Thanks again!
 
P

Paul Rudin

loop would be infinite. I get what is happening in the function, and I
understand why this would work, but for some reason it's confusing me
as to how it is exiting the loop after a certain number of times. Help
is appreciated, thanks.

It works because 0 tests false and because integer division yields
integers... eventually you'll get something like 1/10 giving 0.

It's not necessarily a good thing to rely on. For example if you try it
after "from __future__ import division" - or in python 3 - you'll get a
float as the result of the division and it won't test False.
 
R

RJB

It works because 0 tests false and because integer division yields
integers... eventually you'll get something like 1/10 giving 0.

It's not necessarily a good thing to rely on. For example if you try it
after "from __future__ import division" - or in python 3 - you'll get a
float as the result of the division and it won't test False.

What operator should I use if I want integer division?
Ada and Pascal used "div" if I recall rightly.
 
T

Terry Reedy

What operator should I use if I want integer division?

//

The line should be 'n = n//10' or even 'n //= 10'.
You might have answered your own question be putting
print(n, count) at the top of the loop body.
Print is an excellent tool.
 
R

rantingrick

It works because 0 tests false and because integer division yields
integers... eventually you'll get something like 1/10 giving 0.

It works because of a design flaw in the language! Specifically i am
referring to treating the integer 0 as False and any other integers as
True. There is also the empty vs non empty containers that work this
way besides numeric types.

----------------------------------
Python 3.1.1 Interactive Session
----------------------------------
bool(0) False
bool(1) True
bool(-1) True
bool(-1.0) True
bool(1.0) True
bool(0.0) False
bool([]) False
bool([1])
True

Sure it *seems* like a "bright" idea at first however later you find
out this sort of laziness just induces more subtle bugs in code.
Python3000 fixed a lot of warts in Python2.x HOWEVER, python3000 did
not go near far enough! We need more radical changes (and when i say
radical i mean removing poorly thought out design patterns like this
"boolean emulation"!) to the language before python will be a truly
21st century language.

We should have never written code like this...

------------
Example 1:
------------
# lst = []

if lst:
blah
if not lst:
blah

Example 1 is not explicit enough. Too much guessing is required by the
reader!

-----------
Example 2
-----------
if lst.lengh > 0:
blah
if not lst.empty():
blah
if not empty(lst):
blah

Example 2 is just explicit enough so that the reader knows *exactly*
what is happening! We don't want to be redundantly explicit, however
we sure as *hell* don't want to be implicit! Hmm, believe i read that
somewhere once... import this!

The point is NEVER use an integer in place of a boolean! And i am NOT
blaming the OP here, no! I am blaming who ever came up with this
idiotic "emulation of booleans" crap! And don't tell me... "Well C did
it first we were just following by example"... because you just make
yourself out to be even a bigger idiot!

Here is another example of bad habits passed down from "on high".

def is_foo():
if something:
return 1
return 0

Why the HELL are you retuning integers in an obviously boolean
function? Are you trying to win Pearl hacker of the year award? Please
folks, always remember... NEVER emulate booleans, always use True/
False for boolean behaviors.

def is_foo():
if something:
return True
return False

*school-bell*
 
M

MRAB

You've gotten several good explanations, mainly saying that 0 -> False
and not 0 -> True, which is why the while loop exits. You've also
gotten advice about how to make your method more robust (i.e. force
integer division).

However, as surprising as this may be I'm actually with RR on this one
(for a little) -- for code readability's sake, you should make your
conditional more readable (i.e. don't depend on the fact that the
iterations will take your test value down to 0 which conveniently in
this case evaluates to False). This could encourage you in later cases
to think that if this result eventually converged to a different number,
say the multiplicative identity instead, that the same approach will
work (when instead it'll dump you into an infinite loop).

You've also gotten the suggestion of typecasting to a string and then
looking at the number of characters in the string. This works fine for
integers and positive numbers, but not so well for negatives and floats,
since both the decimal and negative sign will be counted. You could
typecast to a string then strip out '-' and '.' and then count the
characters. i.e.

def num_digits(n):
return len(str(n).replace('-','').replace('.',''))
Or:

def num_digits(n):
return len(str(abs(n)).replace('.',''))
Or typecast to an int if you want to neglect decimals before converting
to a string, etc.
[snip]
Python doesn't have typecasting. :)
 
R

Rikishi42

Your function only works if n is an integer. Example:

325

When doing integer division, python will throw away the remainder and
return an int. Using your example of n==44, 44/10 == 4 and 4/10 == 0

Before each iteration of the while loop, the given expression (in this
case just n) is evaluated as a boolean. Your function would act the
same if it looked like this:

def num_digits(n):
count = 0
while bool(n):
count = count + 1
n = n / 10
return count

0 of course evaluates to False as a boolean, which is why the while loop stops.

Just for kicks, this function would work about as well:

def num_digits(n):
return len(str(n))


What about this one:

import math
def num_digits(n):
return int(math.ceil(math.log(n,10)))
 
R

Rikishi42

It works because of a design flaw in the language! Specifically i am
referring to treating the integer 0 as False and any other integers as
True. There is also the empty vs non empty containers that work this
way besides numeric types.

I would have defined the flaw to be use of '/' for the integer division.


Using 0 as false and any other value as true is hardly unique to python. Lots
of languages have been doing this long before Python even existed.
 
R

rantingrick

[...]
Using 0 as false and any other value as true is hardly unique to python. Lots
of languages have been doing this long before Python even existed.

Well, the only way to reply is to paraphrase an anecdotes my mother
would tell me often as a young lad...

Mother: "Just because other language developers choose to jump off the
cliffs of implicit-ey should we jump also?"

Son: i guess not

Mother: Now brush your teeth and go to bed.
 
S

Steven D'Aprano

I would have defined the flaw to be use of '/' for the integer division.

Well, it was a long time ago, when it seemed like a good idea.

Now, Python has // for integer division.
 
L

Littlefield, Tyler

Uh oh, I think we found RR's evil twin: another python to the modern day
visionary.
>Example 1 is not explicit enough. Too much guessing is required by the
>reader!
if list is empty, bla. if not, bla. it's not all that hard, and there's
no guessing that needs to take place, honest.
 
A

alex23

rantingrick said:
Well, the only way to reply is to paraphrase an anecdotes my mother
would tell me often as a young lad...

Mother: "Just because other language developers choose to jump off the
cliffs of implicit-ey should we jump also?"

You think of yourself as a _language developer_? Oh that's classic.
 
E

Ethan Furman

Jason said:
However, as surprising as this may be I'm actually with RR on this one
(for a little) -- for code readability's sake, you should make your
conditional more readable (i.e. don't depend on the fact that the
iterations will take your test value down to 0 which conveniently in
this case evaluates to False).

while n: is plenty readable. n is either something or nothing, and
something evaluates to True, nothing to False.
This could encourage you in later cases
to think that if this result eventually converged to a different number,
say the multiplicative identity instead, that the same approach will
work [...]

See above comment -- something or nothing, not mathematical identities.
def num_digits(n):
return len(str(n).replace('-','').replace('.',''))

Or typecast to an int if you want to neglect decimals before converting
to a string, etc.

Or use recursion!

... if n == 0:
... return 0
... else:
... return num_digits(n//10) + 1
...
0

0 is still one digit. ;)

~Ethan~
 
D

drygal

Your function only works if n is an integer.  Example:


325

When doing integer division, python will throw away the remainder and
return an int.  Using your example of n==44, 44/10 == 4 and 4/10 == 0

Before each iteration of the while loop, the given expression (in this
case just n) is evaluated as a boolean.  Your function would act the
same if it looked like this:

def num_digits(n):
  count = 0
  while bool(n):
      count = count + 1
      n = n / 10
  return count

0 of course evaluates to False as a boolean, which is why the while loop stops.

Just for kicks, this function would work about as well:

def num_digits(n):
   return len(str(n))

And if either of these were a real function you planned to use, you'd
probably want to either cast n as an int ( int(n) ) or at least check
its type:

if not isinstance(n, int):
   raise TypeError("WTF you didn't pass me an int")

Michael
I guess it needs:

def num_digits(n):
return len(str(n)) -1

or
6

Regards,
Damian.
 
T

Terry Reedy

On 2/9/2011 6:00 PM, Rikishi42 wrote:
numeric types.
I would have defined the flaw to be use of '/' for the integer division.

Guido agreed, and hence changed it (after much contentious discussion!).
 
B

Benjamin Kaplan

You've gotten several good explanations, mainly saying that 0 -> False
and not 0 -> True, which is why the while loop exits.  You've also
gotten advice about how to make your method more robust (i.e. force
integer division).

However, as surprising as this may be I'm actually with RR on this one
(for a little) -- for code readability's sake, you should make your
conditional more readable (i.e. don't depend on the fact that the
iterations will take your test value down to 0 which conveniently in
this case evaluates to False).  This could encourage you in later cases
to think that if this result eventually converged to a different number,
say the multiplicative identity instead, that the same approach will
work (when instead it'll dump you into an infinite loop).

You've also gotten the suggestion of typecasting to a string and then
looking at the number of characters in the string.  This works fine for
integers and positive numbers, but not so well for negatives and floats,
since both the decimal and negative sign will be counted.  You could
typecast to a string then strip out '-' and '.' and then count the
characters.  i.e.

def num_digits(n):
   return len(str(n).replace('-','').replace('.',''))
Or:

def num_digits(n):
   return len(str(abs(n)).replace('.',''))
Or typecast to an int if you want to neglect decimals before converting
to a string, etc.
[snip]
Python doesn't have typecasting. :)

Because these basic types are not mutable?  <excuse> Most of my work has to
be in Fortran, so I'm a relative newcomer to Python.  When I don't need
Fortran-y performance it's much nicer (obviously to anyone that's used them
both)!  Still don't know much deeper than Python's cosmetic surface at this
point. </excuse>

Not exactly. It's because everything in Python is an object. What
you're doing isn't type casting. It's just calling an object
constructor- no different than any other class in the language.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top