Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Strange Error: "TypeError: 'NoneType' object is not callable"

Tags:

python

I'm implementing a simple class to represent a 2D vector. Here's are the relevant bits:

class Vector:
  def __init__( self, x, y ):
    self.vec_repr = x, y

  def __add__( self, other ):
    new_x = self.x + other.x
    new_y = self.y + other.y
    return Vector( new_x, new_y )

  def __getattr__( self, name ):
    if name == "x":
      return self.vec_repr[0]
    elif name == "y":
      return self.vec_repr[1]

Later on, I have something like:

a = Vector( 1, 1 )
b = Vector( 2, 2 )
a + b

I get TypeError: 'NoneType' object is not callable. This is especially strange because the error is not marked as being on any particular line, so I don't know where to look!

Very strange, so I did some experimentation, and discovered that it occurs on the line a+b. Also, when I re-work the class to be as follows:

class Vector:
  def __init__( self, x, y ):
    self.x, self.y = x, y

  def __add__( self, other ):
    new_x = self.x + other.x
    new_y = self.y + other.y
    return Vector( new_x, new_y )

The error vanishes!

I see that there are lots of questions about an error similar to this - all seem to involve some function name being overwritten somewhere by a variable, but I don't see where this is happening!

As another clue, when I change the default return type of __getattr__() to something else - str, for example - the error morphs into TypeError: 'str' object is not callable

Any ideas as to what's going on? Is there some behavior of __getattr__() that I don't understand?

like image 843
Slubb Avatar asked Jul 31 '11 18:07

Slubb


1 Answers

The problem is that your __getattr__ doesn't return anything for attributes other than x and y and doesn't raise an AttributeError. Hence when the __add__ method is looked up, __getattr__ returns None and hence your error.

You could fix this by making __getattr__ return values for other attributes. In fact you have to ensure that __getattr__ calls the method from its superclass for all attributes that are not handled. But really __getattr__ is the wrong thing to use here. It should be used sparingly, and when there aren't more obvious, higher level solutions available. For example, __getattr__ is essential for dynamic dispatch. But in your case, the x and y values are well known and well defined before the code runs.

The right solution is to make x and y properties and not implement __getattr__ at all.

@property
def x(self):
    return self.vec_repr[0]

@property
def y(self):
    return self.vec_repr[1]
like image 145
David Heffernan Avatar answered Oct 08 '22 22:10

David Heffernan