EX40 - Code in the book didnt work

Im new to python. I learned java first, awhile back. But, Ive been learning linux lately and got into python. I couldnt make the code in the book to work. I had to change it after a few trial runs on powershell. Im pasting the before and after code (that I got to work) and the errors from PS. I should also mention Shaw is using python 3.6 and Im using 3.8. I didnt think there were many differences between the two but Im constantly finding more and more. What I want to know is why, after the constructor method, init(self,lyrics) creates the object do I have to pass lyrics to my method, AGAIN?. Also, is it the case where in every situation of instantiation the constructor takes no argument? The before and after code with PS output:

class Song(object):

    def __init__(self, lyrics):
        self.lyrics = lyrics

    def sing_me_a_song(self):
        for line in self.lyrics:
            print(line)

 happy_bday = Song(["Happy birthday to you",
 "I don't want to get sued",
 "So I'll stop right there"])
 bulls_on_parade = Song(["They rally around tha family",
 "With pockets full of shells"])
 happy_bday.sing_me_a_song()
 bulls_on_parade.sing_me_a_song()

Did not work for me. I am using python 3.8. Is it really that much of a difference? I finally got it to work, this way:(different apps, different fonts)

class Song(object):
    def _init_(self,lyrics):
        self.lyrics=lyrics

    def sing_me_a_song(self,lyrics):
        for line in lyrics:
            print(line)

happy_bday = Song()
song1 = ["Happy birthday to you", "I don't want to get sued", "So I'll stop right there"]
bulls_on_parade = Song()
song2 = ["They rally around tha family", "With pockets full of shells"]

happy_bday.sing_me_a_song(song1)

bulls_on_parade.sing_me_a_song(song2)

Before, powershell was giving me some errors. The following is a transcript of all the output from all the changes before I got it right:

PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Traceback (most recent call last):
  File "ex40.py", line 9, in <module>
    happy_bday = Song(["Happy birthday to you", "I don't want to get sued", "So I'll stop right there"])
TypeError: Song() takes no arguments
PS C:\Users\jharr\lpthw> python ex40.py                                                                                       File "ex40.py", line 13
    happy_bday.sing_me_a_song(song1)
    ^
SyntaxError: invalid syntax
PS C:\Users\jharr\lpthw> python ex40.py                                                                                       File "ex40.py", line 13
    happy_bday.sing_me_a_song()
    ^
SyntaxError: invalid syntax
PS C:\Users\jharr\lpthw> python ex40.py                                                                                       File "ex40.py", line 13
    happy_bday.sing_me_a_song()
    ^
SyntaxError: invalid syntax
PS C:\Users\jharr\lpthw> python ex40.py                                                                                       File "ex40.py", line 14
    happy_bday.sing_me_a_song(song1)
    ^
SyntaxError: invalid syntax
PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Traceback (most recent call last):
  File "ex40.py", line 9, in <module>
    happy_bday = Song([])
TypeError: Song() takes no arguments
PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Traceback (most recent call last):
  File "ex40.py", line 14, in <module>
    happy_bday.sing_me_a_song(song1)
TypeError: sing_me_a_song() takes 1 positional argument but 2 were given
PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Traceback (most recent call last):
  File "ex40.py", line 14, in <module>
    happy_bday.sing_me_a_song(song1)
  File "ex40.py", line 6, in sing_me_a_song
    for line in self.lyrics:
AttributeError: 'Song' object has no attribute 'lyrics'
PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Happy Happy birthday to you
I don't want to get sued
So I'll stop right there
Traceback (most recent call last):
  File "ex40.py", line 16, in <module>
    bulls_on_parage.sing_me_a_song(song2)
NameError: name 'bulls_on_parage' is not defined
PS C:\Users\jharr\lpthw> python ex40.py                                                                                     Happy Happy birthday to you
I don't want to get sued
So I'll stop right there
They rally around tha family
With pockets full of shells
PS C:\Users\jharr\lpthw>
PS C:\Users\jharr\lpthw> python ex40.py

It’s a little hard to diagnose, but the code you say doesn’t work should work, and, in fact, does work fine for me. You should definitely not need to pass the lyrics to the object again.

The errors

TypeError: Song() takes no argument

and

AttributeError: 'Song' object has no attribute 'lyrics'

suggest that somehow your __init__ method wasn’t registered and thus not executed. Python probably fell back to the parent method object.__init__() which doesn’t take any arguments.

Could there have been indentation errors in your file? That might explain those weird syntax and name errors too.

So, chances are your code that you actually had for the first attempt was different from mine, and I see the two things that would make your code different. You have:

def _init_(self, lyrics):

You have ONE underscore “init” ONE underscore. But in my book I have:

def __init__(self, lyrics):

I have DOUBLE underscore (called a dunder).

I suspect you did one underscore, that didn’t work, so you started hacking around it and ended up with the solution of moving the lyrics to the sing_me_a_song function. Then when that worked you declared the “book’s version doesn’t work” but it did, you just didn’t have the two underscores.

What’s interesting is your solution is actually a good sign that you can code since you worked around the problem until you had something that kind of worked anyway. The down side is that you made the two fatal flaws every programmer makes:

  1. Assuming everything you type is always correct, when you should assume you are always wrong first not the python version or a book that’s been used by millions of people.
  2. Rather than figure out how or why your version is wrong, you just hacked around it until it worked. Which is both the best possible feature to have as a programmer…and also kind of the worst.

Here’s what I suggest you do: Go back and rewrite this from scratch using my version of the book. Your python version is fine, my version works, just take the time to make it exactly the same. Then, get in the habit of continuing to do what you’re doing here. Do it the way I’ve got it, then do it again and try to find a different way to do the exercise. Keep doing that and you’ll start doing great at these.

1 Like

I did, in fact, first assume I was wrong. I could not have come to the conclusion of the double underscore. That is new to me. Did you mention the double underscore in the book? If you did, I apologize - I missed it. But, I am grateful you took the time to explain to me what i did wrong. Thank you so much Mr. Shaw!

Im really glad that worked and that I was the one wrong. Its simpler knowing I just have to discover what I did wrong. Thank you, again.

And here I was, wondering why __init__ was italic and not bold before you edited your post and put all the code in code blocks. :grin:

@zedshaw I’ve only been here a few weeks, but the kind of support I’ve seen you give here – for free – is astonishing. Kudos!

@florian I didnt see your response until after Shaw’s. I know he says dont copy and paste, but here I wouldnt have made that mistake. I also would not have learned anything and this book is for a learning experience. Anyway, I dont know how to close this post or mark it solved. Can I just ask another question here(I wouldnt think so) or do I make a new post?

@Jharris4854 Just in case, I wasn’t implying any fault on your side. :slight_smile: Just sort of smiling benignly upon my own blindness…

Fire away! I don’t know, a new thread if it’s not related to ex. 40?

Well, I’ll also throw out that none of this is your fault. Python has a lot of terrible typographic decisions that make it hard for beginners. The two biggest ones are:

  1. Using arbitrary number of underscores to denote types of things.
  2. Using arbitrary number of spaces to denote structure.

You just experienced the first, where its really hard to tell the difference between _ and __. An easy way to test this is, tell me how many underscores in this:

______.

Now, tell me how many dashes:

------

Dashes have a small bit of whitespace between them so you can count them out. Typographically underscores don’t so you can’t count them.

The space problem comes from things like this:

def funcA():
   # some code
   return var

   def funcB():
      return x

Beginners constantly make this mistake mostly because they don’t know to hit backspace to move to column 0, so they accidentally put a function inside a function, and python thinks that’s totally alright even though that should be a warning in the above code. The fix is then:

def funcA():
   # some code
   return var

def funcB():
   return x

But, in a language like JavaScript you don’t have this problem because they use bounding characters to denote structure:

function funcA() {
   // some code
   return var;
   }
   function funcB() {
      return x;
   }

That JS code is formatted the same as the Python, but it’ll still work just fine, and most people will realize the mistake right away, if they forget one of the } it makes an error:

function funcA() {
   // some code
   return var;
   
   function funcB() {
      return x;
   }

Try to run that and it’ll make an error, you can then easily see how you botched the balancing. In general I don’t find beginners making these errors with most other languages, just Python. Languages like JS have their own weird typo errors, but the design of other languages makes it easier to spot them because you get an error since the code just isn’t valid.

Folks,

I have reached ex 40 and seem to be getting an attribute error message as per photo below.

This is my code

class Song(object):

    def __init__(self, lyrics):

        self.lyrics=lyrics

def sing_me_a_song(self):

    for line in self.lyrics:

        print(line)

happy_bday = Song(["Happy birthday to you",

"I don't want to get sued",

"so i'll stop here"])

bulls_on_parade = Song([" They rally around the family",

                         "with pockets full of shells"])

happy_bday.sing_me_a_song()

bulls_on_parade.sing_me_a_song()

would some be kind enough to tell me what I am doing wrong?

Nothing sinister. You just need to indent the whole sing_me_a_song function so it becomes part of the class definition.

Wow, thank you florian, I did what Zed says about retyping the whole code if it doesn’t work and I must have done the same mistake twice haha. Much appreciate the help.

It’s actually one of the reasons why I’m kind of against Python for beginners. At first you think the indent thing makes it easier, but it makes the code more brittle since it relies on a trick of typography that’s difficult to judge and see (the space character).

Hi Zed,
I’m probably going off topic here but what do you recommend is a good programming language for a beginner to learn. I hope in future to get into automation, SCADA and things of similar nature. Would appreciate any direction you give me.
Thanks,

JavaScript is my favorite these days, because it is fun and can do anything, makes real apps on any platform, is easy to distribute, has tons and tons of free resources, and the JavaScript management infrastructure actually seems to want to push the language forward while keeping it backward compatible.

1 Like

Thanks for taking the time to reply to me. Looks like Java is going to me my next book after this. appreciate the advice.
Cheers