Long and short dashes

14 Apr, 2020

Jan vK writes:

here's an example from your book that gives an error:
count_down_by_twos = list(range(40, 10, −2))
SyntaxError: invalid character in identifier
please inform me how to solve this problem

It looks like you might have copied-and-pasted the code? Perhaps from the digital version of the book? It looks like the -2 in your example is actually a hyphen (i.e. a long dash −) instead of a minus (i.e. a short dash -). So if I try the version of the code you sent, I get the same error:

>>> count_down_by_twos = list(range(40, 10, 2))
  File "<stdin>", line 1
    count_down_by_twos = list(range(40, 10, 2))
SyntaxError: invalid character in identifier

However, if I try with the correct character, there's no error:

>>> count_down_by_twos = list(range(40, 10, -2))

The next question you might ask is what does "invalid character in identifier" actually mean? An identifier is the name of something (the name of a keyword, a variable, a function or a class, and so on) -- valid identifiers are a sequence of letters (characters), digits and underscores. In effect you're getting that error message because python doesn't recognise "−2" (a long dash followed by 2) as any recognisable keyword, or variable, or anything resembling a valid identifier.

Hope that helps.

ICANN corruption

18 Jan, 2020

Belatedly... this article about ICANN & dot-com price increases (which does look rather like ICANN corruption, in my opinion), annoyed me more than I can properly express, despite the fact that initially (first few years) the 7% increase will still be less than I'm paying for dot-nz domain names. This is pretty obvious profiteering by Verisign, and I worry that this will trickle out from the US to domain names for other countries, inevitably turning the ownership of a domain name from something that's a petty cash expense into a real, and significant, cost. It's particularly concerning if you take into consideration similar news in the dot-org space.

It irritated enough that I started looking at alternatives. However, none of the blockchain domain name options look like a particular economic, straightforward, sure-fire win (paticularly not for a non-technical audience) - even OpenNIC, which is the closest tech to the incumbent, would require jumping through additional hoops because I don't believe my current hosting provider supports DNS alternatives - on their forums, I can't find any mention of OpenNIC apart from a note on a "Rejected Feature Proposals" forum, back in 2007, about it being a stale project.

Maybe the global Internet community will eventually route its way around the "damage" by selecting a generally acceptable alternative. Or a privacy-focused browser maker like Mozilla will come up with (and promote) a viable domain name system that the other browser makers will have to implement or be left behind.

In the meantime, perhaps I'll look at redirecting my primary domain elsewhere and route around the problem myself, before the name comes up for renewal in a few years time...

Problems with restarting the game

22 Sep, 2019

Serhii writes (excerpted from two emails):

I teach programming lessons for the pupils. We try to do restart button for the "Bounce" as it follows:
...there is a problem in "command=restart".

If I run your code I get the following error:

Traceback (most recent call last):
  File "game1.py", line 105, in <module>
  File "game1.py", line 86, in add_restart
    self.restart_button = Button(tk, text="Click to Restart Game", command=restart, bg="green")
NameError: name 'restart' is not defined

Looking at your code...

    def add_restart(self):
        self.restart_button = Button(tk, text="Click to Restart Game", command=restart, bg="green")

...the problem is you don't actually have a function called restart anywhere - which explains the error message "name 'restart' is not defined". The easiest way to fix this, is to define that function inside your Game class, in which case the above function should actually be:

    def add_restart(self):
        self.restart_button = Button(tk, text="Click to Restart Game", command=self.restart, bg="green")
(note the addition of self there)

The restart function itself should remove the restart button from the screen, move the paddle and ball back to the starting position, and reset the score. I suggest you create a simple function first, just to prove the button works:

    def restart(self):
        print("Restart the game!")

If you see "Restart the game!" printed when clicking the button, you know you're good to start adding the code to do the actual restart (you might also find this post useful: journal/2018/03/04/restarting-the-bounce-game-revisited). The other thing you might want to think about changing, is to only add the restart button if the game is over (so that's a small change to the while loop at the bottom).

Hope that helps.

IDLE3 on Ubuntu

07 Sep, 2019

Chris K writes (excerpted):

I'm teaching myself and home educating my three young daughters at the same time. Just a little bit every day (excepting Sunday which is entirely reserved for pancakes and not inter-computery-things) . Thank you for providing the opportunity for me to introduce the subject of computer programming in a fun way.
Anyhow, we hit a snag early on that does not seem to get a mention on the publishers site or your blog. When instructed to search for IDLE on the Ubuntu software centre nothing of relevance was listed. I had one of the girls do it and she was very disappointed...
...I've done some homework and followed vitux.com/how-to-install-idle-python-ide-on-your-ubuntu instructions on installing idle3, which worked. So we are all set to go today.

I don't know how useful this feedback is to you but this is an opportunity to express my appreciation for all your hard work and skill, so I'm taking it.

The next re-print of Python for Kids will include updated instructions for installing IDLE3 on Ubuntu - which obviously doesn't help anyone reading the current print of the book. My steps are pretty similar to the link you've referenced:

snippet from the book

Thanks for the email - it's a good prompting to put something on my site, which others in the same position might come across.

Not the normal knock-off

02 Aug, 2019

Usually on Amazon, we see Python for Kids knock-offs which are an exact copy of the book, with cruddy printing and/or binding. No Starch have a clever binding which allows books to open flat, without falling apart after reading a couple of chapters (clever enough that a few people thought it was actually a failure in the glue) - so these were pretty obviously cheap copies, even without the often misprinted and missing pages.

However, an eagle-eyed reader recently notified No Starch of a new type of knock-off -- where they have slightly rewritten the text (I assume just enough to fool a copyright-checking algorithm), and included content from (I think) other sources, to make it even less likely that any automation would flag the book.

For example, here's an excerpt from Python for Kids...

pfk excerpt 1

And here's the dodgy knock off...

knockoff excerpt 1

Erm... what the heck is a "Trump String"?

Here's another one from PfK:

pfk excerpt 2

And here's the knock off again...

knockoff excerpt 2

Yeah... way to rewrite it to be more boooooooring, Book Pirates!

The code examples are pretty much exactly the same in the knock-off (at least the examples I checked) - if badly formatted (including misprinted wingdings characters and other artifacts).

So, the first part of the book is basically a slightly (and extremely poorly) rewritten knock-off of mine. The second part of the book has things like bubble sort, insertion sort and...

knockoff pagerank

...because every self-respecting kid needs to how to write a sorting algorithm (by just looking at the code) and how to use numpy and pandas for page-rank???

And from there, on to games like Hangman, but written with Python2 and incorrectly formatted as well...

knockoff hangman

A garbage knock-off, and 29 five-star reviews in a couple of weeks, no less (I assume paid for). Interestingly, I clicked through a few of the other reviews by those same reviewers and found more poorly written texts. It's an Amazonian (sic) nest of crappy Python books!

Tick tick tick. I wonder how long it'll take Amazon to catch on...

String formatting

23 Jul, 2019

Lou O writes:

Hi Jason, Read a few good reviews of your book on Amazon.
One of the reviews pointed out "The explanation of String formatting needs to be updated. We don't do embedded values using %s anymore. I recommend skipping the chapters on Turtle Graphics and tkinter. The introductory chapter on classes and objects is not bad, but the topic is beyond what most kids will need, and they should really focus on imperative / procedural programming first using just lists and dictionaries as their basic data structures."
And I was wondering if those points had been taken into account and updated since then.

In terms of string formatting, the reviewer is correct but, on the other hand, % formatting hasn't actually been deprecated yet. From the official Python 3 documentation:

The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals, the str.format() interface, or template strings may help avoid these errors. Each of these alternatives provides their own trade-offs and benefits of simplicity, flexibility, and/or extensibility.

I have thought about updating the section on formatting though, just because using str.format is the more accepted/modern method -- but this will probably have to wait for a second edition, or perhaps the next major reprint.

In terms of classes and objects, I don't agree at all. When originally writing the book, I thought rather hard about whether it was worth going into the complexity of that topic and, in the end, came to the conclusion that there is too much in Python which is object-oriented, and would be more confusing to explain without at least covering the basics (IMHO).

And finally, in regard to the comment about skipping the chapters on turtle and tkinter... sure, if they want a dry book on programming fundamentals, with nothing fun for a kid to experiment with -- one that they will then put down 10 minutes after opening and never return to -- by all means, skip those chapters.

For everyone else: will your child use the turtle and/or tkinter modules in the future? Probably not. But are they a useful tool to learn how to use those fundamental programming concepts (without needing to install any complicated third party libraries)? Personally, I believe so.

Bouncing in Polski

08 May, 2019

Marzena writes:

I'm writing to you because neither me nor my 11-year old daughter with whom we're learning Python can figure out where the problem is. We get the following error:
 ================ RESTART: C:/Users/Enarpol/Desktop/brajan.py ================
 Traceback (most recent call last):
   File "C:/Users/Enarpol/Desktop/brajan.py", line 5, in <module>
     class Piłka:
   File "C:/Users/Enarpol/Desktop/brajan.py", line 20, in Piłka
     if pozycja[1] <= 0:
 NameError: name 'pozycja' is not defined
And the code is exactly like in the book (some words are in Polish, but I assume it's not a problem for you to trace the error despite of it)

Your problem problem is caused by indentation and the idea of "scope" - I guess you're using the Polish language version of the book, so I'm not sure of the correct page number, but in the English language version of the book the section on Variables and Scope (page 84) would be useful to re-read.

In short, here is the incorrect bit of your code:

    def rysuj(self):
        self.płótno.move(self.id, self.x, self.y)
        pozycja = self.płótno.coords(self.id)
    if pozycja[1] <= 0:
        self.y = 3

If I re-indent this with visible spaces, to show how it should look, hopefully you can see what you need to fix in the rest of your code:

    def rysuj(self):
        self.płótno.move(self.id, self.x, self.y)
        pozycja = self.płótno.coords(self.id)
    ␣␣␣␣if pozycja[1] <= 0:
    ␣␣␣␣    self.y = 3

Why does this make a difference? Because in the case of rysuj above, the variable pozycja is only visible within the function - or to be exact, within the block of code that makes up the function. And how do we create a block of code? Basically through indentation. Your if statement was at the same indentation level as def rysuj(self), so it wasn't part of the function and that's why you're getting the error name 'pozycja' is not defined.

Hope that helps.

Traditional Chinese

16 Mar, 2019

你 好!

traditional chinese translation cover

Available from Wu Nan Books.

Stickman Moves

31 Jan, 2019

Alex Z writes (edited):

I'm playing with the last chapter game, cf. the stickmangame7.py file. I'm a little confused about how the key presses are expected to cause the character [to] move.

If the player hits the right or left key, the character starts moving but keeps moving and doesn't stop unless [colliding] against a wall or a platform. Even jumping on a platform doesn't cause the stick figure to stop. This makes hard to climb to the top parts of the area. Is it the expected behavior of the game ?

I am not the first to experience this problem, see the folowing message on stackoverflow: How to move character only when key is pressed down Python

Yes, that is the expected behaviour of the game. The point is to make it more difficult to reach the top platform, so once the character starts running he doesn't stop, unless he collides with something. However, if you do want to change it so that the character only moves when the key is held down, there's a few fairly minor modifications you can make (which are discussed in that stack overflow article):

i. Add a new function to stop the character moving, by changing the value of the x variable to 0. So in the StickFigureSprite sprite class we add this new function:

class StickFigureSprite(Sprite):
    def stop_moving(self, evt):
        self.x = 0
ii. Now we need a way to call the new function. So we add two new key bindings to the __init__ function of the same class, and set the starting value of the x variable to 0 (it's currently set to -2 so that the stick figure starts running as soon as the game starts):
class StickFigureSprite(Sprite):
    def __init__(self, game):
        self.x = 0
        game.canvas.bind_all('<KeyRelease-Left>', self.stop_moving)
        game.canvas.bind_all('<KeyRelease-Right>', self.stop_moving)

So with these changes the game starts and the stick man won't move. When you hold the left or right key down, the x variable is set to -2 or 2 respectively which starts "him" moving. When you release the key, it sets the value back to 0, which stops him moving again.

You might notice one slight problem when running the game after this change - the stickman starts quickly and then slows down. This is caused by the keyboard repeating which then impacts the performance of the mainloop function in the Game class. This is an unfortunate side effect of the way the code for the game is written (the stickman code is not the standard way to handle animation with tkinter -- however it was written this way to hopefully make animation concepts, in programming, slightly easier for a child to understand).

PFK and iPython

27 Jan, 2019

Jobakhan writes (edited):

Using this code
import sys
def moon_weight():
    print('Please enter your current Earth weight')
    weight = float(sys.stdin.readline())
    print('Please enter the amount your weight might increase each year')
    increase = float(sys.stdin.readline())
    print('Please enter the number of years')
    years = int(sys.stdin.readline())
    years = years + 1
    for year in range(1, years):
        weight = weight + increase
        moon_weight = weight * 0.165
        print('Year %s is %s' % (years , moon_weight))

then it gives me this:
error in ipython
Why is this happening?

It looks like you're using iPython/Jupyter Notebook to run the examples in the book. Python for Kids wasn't specifically written to work with iPython (nor was the code actually tested with that app), which is why you're having issues. Having said that, I think many of the examples will work, but some, like the code you sent, will need modification.

In the case of the moon_weight function, it's using sys.stdin.readline() (that's the sys module, "standard input" object, readline function) to read input from the user running the program. There are a couple of ways to read command line input in Python - and in the case of Jupyter it looks like stdin doesn't behave the same way as the Python shell. I found the following in the Jupyter client documentation:

This pattern of requesting user input is quite different from how stdin works at a lower level. The Jupyter protocol does not support everything code running in a terminal can do with stdin...

So what's actually happening when you run that code? The function sys.stdin.readline() returns straight away with an empty string (''), and Python then throws an error when trying to convert that empty string to a floating point number. If you replace sys.stdin.readline() in the above code with input(), the program should then work in jupyter:

weight = float(input())
increase = float(input())
years = int(input())