3

I'm trying to define a simple class and instance in Python 2.7, but I'm running into trouble with __getattr__. Minimal working example below:

class MyClass:

    def __init__(self,value):
        self.a = value

    def __getattr__(self,name):
        return 'hello'

class MyOtherClass:

    def __init__(self,value):
        self.a = value

MyInstance = MyClass(6)

MyOtherInstance = MyOtherClass(6)

Now if I enter dir(MyInstance) I get:

TypeError: 'str' object is not callable

But if I enter dir(MyOtherInstance) I get:

['__doc__', '__init__', '__module__', 'a']

Likewise if I enter MyInstance I get:

TypeError: 'str' object is not callable

But if I enter MyOtherInstance I get:

<__main__.MyOtherClass instance at 0x0000000003458648>

The behaviour with MyOtherInstance is what I expect. Why am I not getting this behaviour with MyInstance?

  • I'm not able to reproduce this on Python 3.x , haven't tried 2.x – Matias Cicero Feb 19 '19 at 15:23
  • 3
    First guess: bad interaction with `__getattr__` and the fact that `MyClass` is an old-style class. – chepner Feb 19 '19 at 15:25
  • 4
    The problem goes away if you use `class MyClass(object):` instead. Moral of the story: don't use old-style classes. – chepner Feb 19 '19 at 15:27
  • Thanks. This is the first I've heard of old style classes. Can you link any resources on old versus new style classes? – FinrodFelagund Feb 19 '19 at 15:30
  • 3
    @FinrodFelagund If you have a choice, stick with Python 3 and don't worry about old-style classes; they only exist in Python 2. Even in Python 2, you shouldn't use them; always inherit from `object` (or another known new-style class). – chepner Feb 19 '19 at 15:36
  • 1
    See https://stackoverflow.com/questions/54867/what-is-the-difference-between-old-style-and-new-style-classes-in-python/16193572. – chepner Feb 19 '19 at 15:41
  • Note: If you must use Python 2, a slightly easier approach to getting new-style classes than explicitly inheriting from `object` every time is to put `__metaclass__ = type` at the top of every source file you write (after any `__future__` imports, but at top level, before any class definitions). That does nothing on Python 3 (completely ignored), but makes all classes defined afterwards in that module implicitly new-style on Python 2. – ShadowRanger Feb 19 '19 at 20:13

1 Answers1

3

The problem is that MyClass is an old-style class (i.e., it doesn't inherit explicitly from object or another new-style class), which means __getattr__ is being used for magic methods that don't trigger a call to __getattr__ in new-style classes.

To see this, change your class to

class MyClass:
    def __init__(self,value):
        self.a = value

    def __getattr__(self,name):
        print("Looking up %s" % (name,))
        return 'hello'

The bare use of MyInstance triggers a call to MyInstance.__repr__, but __repr__ evaluates to the string 'hello', not the class's __repr__ method.

>>> MyInstance
Looking up __repr__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

Similarly, dir(MyInstance) triggers a call to MyClass.__dir__, and __dir__ likewise is the string 'hello', not the appropriate method.

>>> dir(MyInstance)
Looking up __dir__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object is not callable

You don't have the same problem with MyOtherClass because you didn't override __getattr__.

Inheriting from object makes the problem go away; magic methods are looked up separately before falling back to __getattr__.

class MyClass(object):
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
chepner
  • 497,756
  • 71
  • 530
  • 681