views:

126

answers:

5

I'm just beginning to learn python. I wrote an example script to test OOP in python, but something very odd has happened. When I call a class method, Python is calling the function with one more parameter than given.

Here is the code:


1.  class Bar:
2.   num1,num2 = 0,0
3.   def __init__(num1,num2):
4.    num1,num2 = num1,num2
5.   def foo():
6.    if num1 > num2:
7.     print num1,'is greater than ',num2,'!'
8.    elif num1 is num2:
9.     print num1,' is equal to ',num2,'!'
10.   else:
11.    print num1,' is less than ',num2,'!'
12. a,b = 42,84
13. t = Bar(a,b)
14. t.foo
15. 
16. t.num1 = t.num1^t.num2
17. t.num2 = t.num2^t.num1
18. t.num1 = t.num1^t.num2
19. 
20. t.foo

And the error message I get:


python test.py
Traceback (most recent call last):
  File "test.py", line 13, in 
t = Bar(a,b)
TypeError: __init__() takes exactly 2 arguments (3 given) 

Can anyone help?
Thanks in advance

+1  A: 

Python identifiers are case-sensitive... bar != Bar...

Also, you need to explicitly declare self as the first argument of the __init__() method, something like:

def __init__(self, num1, num2):
   #...etc.

BTW, see bcherry's answer as he/she covers other typical Python beginner mistakes (such as not explicitly prefixing the instance variables with self....

mjv
Woops... Sorry about that, that was a mistake on my part... Fixed it.
Hussain
+7  A: 

The first argument passed to an instance method is the instance itself. Typically this is called self when defining the function:

  def __init__(self, num1, num2):

Consider reading the tutorial.

Ignacio Vazquez-Abrams
+1  A: 

In regards to that particular error message, you're missing the self argument on line 3. It should read:

def __init__(self, num1,num2):

Unlike instance methods in other OOP languages, in Python, you have to explicitly name the instance as the first argument when defining such a method. Then, when you call obj.some_method(), obj is automatically and implicitly passed as the first argument to some_method.

The first argument is idiomatically named self, but nothing is stopping you from naming it anything else.

Mark Rushakoff
+5  A: 

Couple things:

  1. Your class is named Bar but you're calling it as bar(a, b). Change that to Bar(a, b) to solve this problem.
  2. Classes need to inherit from something (if nothing, then object). You need class Bar(object):
  3. Instance methods, in Python, are always supplied one parameter, which is the object itself, before other passed parameters. So your def __init__(num1, num2): should be def __init__(self, num1, num2):, and the same for def foo().
  4. All of your instance variables need to be prefixed by self.. So num1 should be self.num1, etc.
  5. The ^ operator is boolean XOR. Not sure if this is what you want, it's often confused with the power operator, **.

Here's your example, cleaned up and fixed accordingly:

class Bar(object):
    num1, num2 = 0, 0
    def __init__(self, num1, num2):
        self.num1, self.num2 = num1, num2

    def foo(self):
        if self.num1 > self.num2:
            print self.num1,'is greater than ',self.num2,'!'
        elif self.num1 is self.num2:
            print self.num1,' is equal to ',self.num2,'!'
        else:
            print self.num1,' is less than ',self.num2,'!'

a, b = 42, 84
t = Bar(a, b)
t.foo()

t.num1 = t.num1 ^ t.num2
t.num2 = t.num2 ^ t.num1
t.num1 = t.num1 ^ t.num2

t.foo()

And the result:

42  is less than  84 !
84 is greater than  42 !
bcherry
A couple is two ;-) Yet all 4 points are right on! +1!
mjv
There were two when I started writing, then I found two more! Then I edited and found one more! :)
bcherry
I fixed it up, but it appears classes do not need to inherit. Perhaps we're using different versions of Python, 2.6.5 for windows here.
Hussain
Yeah, it's _technically_ optional, but best practices are that you should be explicit (inheriting from `object` happens, whether you're explicit or not).
bcherry
I would suggest that using 'is' for equality is dangerous. I think in Python, for integers, it's always the case that they are the same actual thing in memory, but for checking equality it's better form (in my opinion) to use '=='. Reserve the use of 'is' for cases where you want to ensure that the two things being compared are actually the same thing, instead of checking that they're two different things that happen to have the same value. (I hope that makes some sense!)
dash-tom-bang
Yeah, good point, I hadn't read that bit of the code in detail, and missed the use of `is`. But yes, simple values always match with `is` (numbers, strings, booleans, etc).
bcherry
@bcherry: None of the examples in the relevant section of the python tutorial show the class inheriting from object. http://docs.python.org/tutorial/classes.html
Alexandros Gezerlis
Interesting. Perhaps I've always just assumed this was how it's done because code I've worked with (written by serious Python talent) does it this way, so I've followed suit. Well then, consider my point entirely optional and/or unrelated. :)
bcherry
+3  A: 

a) By convention, the first parameter of a method is called self.

b) On line 4 you are self-assigning. Maybe you want to say self.num1, self.num2

c) If you want to call the method foo of t (lines 14 and 20) you should add parentheses at the end: t.foo()

d) The indentation is idiomatically given by 4 spaces, which makes reading much easier.

EDIT: You might want to look at chapters 15-18 of Allen Downey's book "Think Python: How to Think Like a Computer Scientist". This book is very short, nicely written, and easy to read. It is freely available here.

EDIT2: I hadn't noticed this before, but as dash-tom-bang pointed out in a comment below, in this context it is best if (on line 8) you compared for equality using == instead of is.

Alexandros Gezerlis
Thanks, your suggestions worked.
Hussain
It's worth noting that, unlike some/many? other languages, the 'self' parameter is explicit in Python. So your class member methods need to have a 'self' parameter as their first one, in addition to the other parameters that you pass to the method. Then- dereferencing self also needs to be explicit, as mentioned in point b above.
dash-tom-bang