Re: [Tutor] Issue with Python

2014-11-13 Thread Steven D'Aprano
On Fri, Nov 14, 2014 at 10:07:08AM +1100, Daniel Williams wrote:
> Hi, I'm dw0391
> I have an issue with a class task that my teacher can't seem to fix.

Oooh, that's sad that your teacher can't fix this.

Jumping ahead to the relevant part of the code, I can see two errors 
with the same line of code:

print ("You need to pay:"% rate, "each month")

The first error is that the variable `rate` is not defined when the 
print line is run. You will get an error like:

Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'rate' is not defined

The print line should be indented, so that it is part of the main 
function. Putting some comments in, you have:

# define a function called "main"
def main():
# everything indented is part of main()
loantotal = float(input("Total of Loan: $"))
timetotal = float(input("Time Provided (in months):"))
rate = payrate(loantotal, timetotal)

# The next line is NOT indented, so it is not part of the function.
# Instead, it tries to run immediately, but fails because there is
# no variable called `rate` defined yet.
print ("You need to pay:"% rate, "each month")



To fix that bug, simply indent the print(...) line so that it is level 
with the rest of the main() function:

def main():
# everything indented is part of main()
loantotal = float(input("Total of Loan: $"))
timetotal = float(input("Time Provided (in months):"))
rate = payrate(loantotal, timetotal)
print ("You need to pay:"% rate, "each month")


That will then reveal the second error:

TypeError: not all arguments converted during string formatting

which seems a bit cryptic, but all it means is that when Python tries to 
"glue" the pieces of the strings together, it doesn't know where to 
stick them together. The problem is that you try using the string 
formatting operator % like this:

"You need to pay:" % rate


which causes Python to glue the string value of `rate` into the string 
"You need to pay:", but it needs a formatting code %s to know where to 
put the pieces together. You can fix this problem in two ways:

print("You need to pay: %s" % rate, "each month")

or avoid string formatting altogether:

print("You need to pay:", rate, "each month")


The difference between a comma and a percent sign isn't much, but they 
do completely different things and work in very different ways.


-- 
Steven
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] Issue with Python

2014-11-13 Thread Alan Gauld

On 13/11/14 23:07, Daniel Williams wrote:

Hi, I'm dw0391
I have an issue with a class task that my teacher can't seem to fix.


Please explain what the issue is, don;t make us guess.
And don't expect us to run code that you acknowledge
is buggy!


We were asked to write the code for a 'no interest loan repayment
calculator'. I tried, but it did not work.


As above. 'did not work' is way too vague. What happens? Does the 
program give an error? If so include the error mesage. Diod it

crash your computer and format your hard drive? If so tell us
(thats why I won't run untrusted buggy code!) Or did it seem
to run and just gove a result you did not expect? Tell us.

In this case its fairly easy to spot the problem in the code but
in future make it easy for us to help you and you will get more
help.


Attached is the relevant file, 'Prog 6'


Please include the code in the mail message rather than
as an attachment. It makes replying much easier!


> def payrate(loantotal, timetotal):
> rate = loantotal / timetotal
> return rate
>
> def main():
> loantotal = float(input("Total of Loan: $"))
> timetotal = float(input("Time Provided (in months):"))
>
> rate = payrate(loantotal, timetotal)

rate is a local variable inside the main() function, it is
not visible outside.

> print ("You need to pay:"% rate, "each month")

So your print statement needs to be inside the main function
to be able to print rate.


--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.flickr.com/photos/alangauldphotos

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


[Tutor] Issue with Python

2014-11-13 Thread Daniel Williams
Hi, I'm dw0391
I have an issue with a class task that my teacher can't seem to fix.
We were asked to write the code for a 'no interest loan repayment
calculator'. I tried, but it did not work.
Attached is the relevant file, 'Prog 6'
Could you please tell me what I am doing wrong?
Thanks.
# No Interest Loan Repayment Calculator
# How to work out how much a person will need to pay per month:

print (

"""

Loan Repayment Calculator

This programs calculates how much you need
to pay each month to pay off a loan,
because maths hurts your head:

"""

)

def payrate(loantotal, timetotal):
rate = loantotal / timetotal
return rate

def main():
loantotal = float(input("Total of Loan: $"))
timetotal = float(input("Time Provided (in months):"))

rate = payrate(loantotal, timetotal) 

print ("You need to pay:"% rate, "each month")

main()

input("\n\nPlease Press the Enter key to Exit.")
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] “has a value of True” versus “evaluates true”

2014-11-13 Thread Steven D'Aprano
On Thu, Nov 13, 2014 at 12:16:19PM -0800, Danny Yoo wrote:
> > Unlike some languages, which choose confusing and arbitrary sets of
> > values that count as truthy or falsey, Python encourages a simple
> > distinction, something versus nothing. Values which represent some kind
> > of "nothing" are falsey:
> 
> 
> Hi Steven,
> 
> Sure.  I'm not arguing that Python's choices of what's truthy or
> falsey isn't consistent or hard to remember.  What I'm saying is that
> I personally have to deal with multiple languages at once these days.
> (JavaScript is the big one.)  I find one of the points where things
> seem needlessly divergent is truthiness.  Given that, I personally
> subset the language so that I don't have to spend brainpower on this
> issue.

*shrug*

Any time you use multiple languages, you have to deal with their 
differences in syntax and semantics. That's the cost of using multiple 
languages. I'm sympathetic to that fact, but I'm not sympathetic to the 
suggestion that we should avoid a sensible and useful language feature 
because *other* languages do things differently.

Deliberately forgoing the use of a language feature because you have 
trouble with it is understandable. You know your own strengths and 
weaknesses, and if that helps you avoid bugs that's a good thing. But 
the cost is that you may be writing unidiomatic code, or code that works 
but can be improved.

If I couldn't remember the difference between [1, 2] and (1, 2), and 
consequently wrote list([1, 2]) "just to be sure", we'd all agree that 
it was poor coding style even if we sympathised with my confusion over 
lists and tuple.


> As a confession: I have run and introduced bugs where the root cause
> was one language's truthiness table diverging from another.  Given
> that I recognise that I make this particular class of mistake a bit
> more frequently than I'd like, I try to account for this personal
> weakness and engineer for it.  I am not clever.

I think you're a lot cleverer than you give yourself credit for, and 
your coping strategy is a matter for you and anyone reviewing your code. 
Criticising people's code can often be mistaken for criticising the 
person themselves, but it shouldn't be seen that way. 


> >> To quote: "Let your statement be: 'Yes, yes', or "no, no': anything beyond
> >> these is of evil."
> >
> > "Have you stopped abusing small children yet?"
> 
> :(
> 
> I don't understand what your response here means.

"Yes" means that the answerer previously abused children; "No" means 
that haven't stopped. If the answerer never abused children at all, the 
question is a trap and neither Yes nor No is an accurate, truthful 
answer. Matthew's instruction that an accurate answer that explains the 
facts correctly ("I have never abused children at all") is to be 
considered "evil" is itself wicked, or at least terribly naive.


> My intent in
> quoting Matthew 5:37 was purely as a joke, precisely because the quote
> is so ridiculously out of context and definitely not intended to apply
> to the boolean context of programming languages.  My apologies if you
> didn't like that.

Your quote was intended to be humourous, as was my reply. I certainly 
wasn't implying that you have abused children.


-- 
Steve
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] don't understand iteration

2014-11-13 Thread Steven D'Aprano
On Mon, Nov 10, 2014 at 03:08:23PM -0800, Clayton Kirkwood wrote:

> Also of confusion, the library reference says:
> 
> Match objects always have a boolean value of True. Since match() and
> search() return None when there is no match, you can test whether there was
> a match with a simple if statement:
> 
> match = re.search(pattern, string)
> if match:
> process(match)
> 
> blah = re.search(
>r'<\w\w>(\w{3})\.\s+(\d{2}),\s+(\d{2}).+([AP]M)\s+(E[SD]T)', line)
> >>> blah
> <_sre.SRE_Match object; span=(45, 73), match='Nov. 09, 07:15:46 PM EST'>
> >>> if blah: print(blah)
> <_sre.SRE_Match object; span=(45, 73), match='Nov. 09, 07:15:46 PM EST'>
> >>> if blah == True: print(blah)>
> No print out
> 
> To me, this doesn't *appear* to be quite true.

I think you are misreading a plain English expression, namely to "have 
a boolean value", as "is a boolean value". If I said:

The binary string '0b1001' has a numeric value of 9.

I don't think anyone would interpret that sentence as meaning that 
Python treats the string equal to the int:

'0b1001' == 9  # returns False

but rather that converting the string to an int returns 9:

int('0b1001', 0) == 9  # returns True


Somebody unfamiliar with Python might (wrongly) believe that Python 
requires an explicit bool() conversion, just as Python requires an 
explicit int() conversion, but to avoid that misapprehension, the docs 
show an example of the correct idiomatic code to use. You tried it 
yourself and saw that it works:

if blah: print(blah)

prints blah, exactly as the docs suggest. As you can see from the 
printed string value of blah, it is a Match object, and it behaves like 
True in conditionals (if-statement).

On the other hand, this piece of code does something completely 
different:

s = "<_sre.SRE_Match object; span=(45, 73), match='Nov. 09, 07:15:46 PM 
EST'>"
if blah == s: print(blah)

First it checks whether blah equals the given string, then it tests the 
condition. Not surprisingly, that doesn't print anything. Match objects 
are not strings, and although they do have a printable string 
representation, they are not equal to that representation.

Nor are they equal to True:

if blah == True: print(blah)  # also fails to print anything

The comparison "blah == True" returns False, as it should, and the if 
clause does not run.

Match objects might not be equal to True, however they are true, in 
the same way that my car is not equal to red, but it is red. (You'll 
have to take my word for it that I actually do own a red car.)

[...]
> I would expect len(sizeof, whatever)(blah) to return the number of (in this
> case) matches, so 5. Doing a search suggests what is important: the number
> of matches. Why else would you do a search, normally.

The number of groups in a match is comparatively unimportant. The 
*content* of the matched groups is important. Consider this regular 
expression:

regex = r'(\w*?)\s*=\s*\$(\d*)'

That has two groups. It *always* has two groups, regardless of what it 
matches:

py> re.match(regex, "profit = $10").groups()
('profit', '10')
py> re.match(regex, "loss = $3456").groups()
('loss', '3456')

I can imagine writing code that looks like:

key, amount = mo.groups()
if key == 'profit':
handle_profit(amount)
elif key == 'loss':
handle_loss(amount)
else:
raise ValueError('unexpected key "%s"' % key)


but I wouldn't expect to write code like this:

t = mo.groups()
if len(t) == 2:
handle_two_groups(t)
else:
raise ValueError('and a miracle occurs')


It truly would be a miracle, or perhaps a bug is more accurate, if the 
regular expression r'(\w*?)\s*=\s*\$(\d*)' ever returned a match object 
with less than, or more than, two groups. That would be like:

mylist = [1, 2]
if len(mylist) != 2:
raise ValueError


The only time you don't know how many groups are in a Match object is if 
the regular expression itself was generated programmatically, and that's 
very unusual.


> That could then be used in the range()
> It would be nice to have the number of arguments.
> I would expect len(blah.group()) to be 5, because that is the relevant
> number of elements returned from group. And that is the basic thing that
> group is about; the groups, what they are and how many there are. I
> certainly wouldn't want len(group) to return the number of characters, in
> this case, 28 (which it does:>{{{
> 
> 
> >>> blah.group()
> 'Nov. 09, 07:15:46 PM EST'

MatchObject.group() with no arguments is like a default argument of 0, 
which returns the entire matched string. For many purposes, that is all 
you need, you may not care about the individual groups in the regex.


> >>> len(blah.group())
> 28

What would you expect 

len('Nov. 09, 07:15:46 PM EST')

to return? There are 28 characters, so returning anything other than 28 
would be a terrible bug. There is no way that len() can tell the 
difference between any of these:


Re: [Tutor] “has a value of True” versus “evaluates true”

2014-11-13 Thread Marc Tompkins
On Thu, Nov 13, 2014 at 12:40 PM, Ben Finney 
wrote:

> Danny Yoo  writes:
>
> > >> To quote: "Let your statement be: 'Yes, yes', or "no, no': anything
> > >> beyond these is of evil."
> > >
> > > "Have you stopped abusing small children yet?"
> >
> > :(
> >
> > I don't understand what your response here means.
>
> He's pointing out that many questions that ask a yes-or-no question
> can't actually be truthfully answered “yes, yes”, nor “no, no”.
>
> It seems you demonstrated his point quite well :-)
>

The classic version, of course, is "Have you stopped beating your wife?
"
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] “has a value of True” versus “evaluates true”

2014-11-13 Thread Ben Finney
Danny Yoo  writes:

> >> To quote: "Let your statement be: 'Yes, yes', or "no, no': anything
> >> beyond these is of evil."
> >
> > "Have you stopped abusing small children yet?"
>
> :(
>
> I don't understand what your response here means.

He's pointing out that many questions that ask a yes-or-no question
can't actually be truthfully answered “yes, yes”, nor “no, no”.

It seems you demonstrated his point quite well :-)

-- 
 \   “Some people have a problem, and they think “I know, I'll use |
  `\ Perl!”. Now they have some number of problems but they're not |
_o__) sure whether it's a string or an integer.” —Benno Rice, 2011 |
Ben Finney

___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] “has a value of True” versus “evaluates true”

2014-11-13 Thread Danny Yoo
> Unlike some languages, which choose confusing and arbitrary sets of
> values that count as truthy or falsey, Python encourages a simple
> distinction, something versus nothing. Values which represent some kind
> of "nothing" are falsey:


Hi Steven,

Sure.  I'm not arguing that Python's choices of what's truthy or
falsey isn't consistent or hard to remember.  What I'm saying is that
I personally have to deal with multiple languages at once these days.
(JavaScript is the big one.)  I find one of the points where things
seem needlessly divergent is truthiness.  Given that, I personally
subset the language so that I don't have to spend brainpower on this
issue.

As a confession: I have run and introduced bugs where the root cause
was one language's truthiness table diverging from another.  Given
that I recognise that I make this particular class of mistake a bit
more frequently than I'd like, I try to account for this personal
weakness and engineer for it.  I am not clever.


>> To quote: "Let your statement be: 'Yes, yes', or "no, no': anything beyond
>> these is of evil."
>
> "Have you stopped abusing small children yet?"

:(

I don't understand what your response here means.  My intent in
quoting Matthew 5:37 was purely as a joke, precisely because the quote
is so ridiculously out of context and definitely not intended to apply
to the boolean context of programming languages.  My apologies if you
didn't like that.
___
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor


Re: [Tutor] don't understand iteration

2014-11-13 Thread Steven D'Aprano
Coming in late to the conversation:


On Sun, Nov 09, 2014 at 04:34:29PM -0800, Clayton Kirkwood wrote:
> I have the following code:
> 
> import urllib.request,re,string
> months = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May.', 'Jun.', 'Jul.', 'Aug.',
> 'Sep.', 'Oct.', 'Nov.', 'Dec.']
> from urllib.request import urlopen
> for line in urlopen('http://tycho.usno.navy.mil/cgi-bin/timer.pl'):
> line = line.decode('utf-8')  # Decoding the binary data to text.
> if 'EST' in line or 'EDT' in line:  # look for Eastern Time
>  blah = re.search(
>  r'<\w\w>(\w{3}\.)\s+(\d{2}),\s+(\d{2}).+([AP]M)\s+(E[SD]T)', line)
>  (month, day, time, ap, offset) = blah.group(1,2,3,4,5)
>  print(blah,'\n',ap,month, offset,day, time )


In programming, just like real life, it usually helps to try to isolate 
the fault to the smallest possible component. When confused by some 
programming feature, eliminate everything you can and focus only on that 
feature.

In this case, all that business about downloading a Perl script from the 
web, decoding it, iterating over it line by line, is completely 
irrelevent. You can recognise this by simplifying the code until either 
the problem goes away or you have isolated where the problem is.

In this case, I would simplify the regular expression to something much 
simpler, and apply it to a single known string:

text = '1234 5678xxx'
regex = r'(\d*) (\d*)'  # Match 
mo = re.search(regex, text)  # "mo" = Match Object
a, b = mo.group(1, 2)
print(a, b)


Now we can focus on the part that is confusing you, namely the need to 
manually write out the group numbers. In this case, writing 1,2 is no 
big deal, but what if you had twenty groups?

a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t = mo.group(1, 2, 3, 4, 5, ...

When you find yourself doing something ridiculously error-prone like 
that, chances are there is a better way. And in this case, we have this:

a, b = mo.groups()

mo.groups() returns a tuple of all the groups. You can treat it like any 
other tuple:

mo.groups() + (1, 2, 3)
=> returns a new tuple with five items ('1234', '5678', 1, 2, 3)

mo.groups() gives you *all* the groups. What if you only wanted some of 
them? Well, it's a tuple, so once you have it, you can slice it the same 
as any other tuple:

mo.groups()[1:]  # skip the zeroth item, keep all the rest
mo.groups()[:-1]  # skip the last item, keep all the rest
mo.groups()[3::2]  # every second item starting from the third


Let's go back to the mo.group(1, 2) again, and suppose that there are 
more than two groups. Let's pretend that there are 5 groups. How can you 
do it using range?

mo.group(*range(1, 5+1))

In this case, the asterisk * behaves like a unary operator. Binary 
operators take two arguments:

10-6

Unary operators take one:

-6

The single * isn't a "real" operator, because it is only legal inside a 
function call. But it takes a single operand, which must be some sort of 
iterable, like range, lists, tuples, strings, even dicts.

With a small helper function, we can experiment with this:

def test(a, b, *args):
print("First argument:", a)
print("Second argument:", b)
print("All the rest:", args)

And in use:

py> test(*[1, 2, 3, 4])
First argument: 1
Second argument: 2
All the rest: (3, 4)


What works with our test() function will work with mo.group() as well, 
and what works with a hard-coded list will work with range:

py> test(*range(1, 10))
First argument: 1
Second argument: 2
All the rest: (3, 4, 5, 6, 7, 8, 9)


There is no need to turn the range() object into a list first.

Iterator unpacking does require an iterable object. You can't iterate 
over integers:

py> for x in 10:
... pass
...
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'int' object is not iterable

nor can you unpack them:

py> test(*10)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: test() argument after * must be a sequence, not int

Note that the error message is a bit too conservative, in fact 
any iterable is allowed as well as sequences.


> This works fine, but in the (month... line, I have blah.group(1,2,3,4,5),
> but this is problematic for me. I shouldn't have to use that 1,2,3,4,5
> sequence. I tried to use many alternatives using:  range(5) which doesn't
> work, list(range(5)) which actually lists the numbers in a list, and several
> others. As I read it, the search puts out a tuple. I was hoping to just
> assign the re.search to month, day, time, ap, offset directly. Why wouldn't
> that work? Why won't a range(5) work? I couldn't find a way to get the len
> of blah.

The length of a Match Object is meaningful. What do you mean by the 
length of it?

- the total number of groups in the regular expression?

- the number of groups which actually matched something?

- the total number of characters matched?

- something else?

The idea of the Match Object itself having a length is problematic. 


-- 
Steven
__