Search code examples
pythonoopsemanticshasattr

Is hasattr() a method?


Is hasattr() a method? It does take an object instance as a parameter, but it is not used as object.hasttr(). I would say it is not a method then?


Solution

  • Everything in Python is an object, even functions and methods. So the fact that hasattr() takes an object is nothing special, so do str() and input() and sum().

    A method is a function that has been bound to an object by accessing it as an attribute; so "foo bar".split gives you the bound str.split method:

    >>> "foo bar".split
    <built-in method split of str object at 0x7f84bef6a730>
    

    This one happens to be “built-in” because it is implemented as part of the Python runtime, but it’s no different from a function on a class written in Python.

    Calling it returns the result of the method as applied to the string it is bound to:

    >>> method = "foo bar".split
    >>> method()
    ['foo', 'bar']
    

    I can also use it directly, unbound:

    >>> str.split("spam ham")
    ['spam', 'ham']
    

    hasattr() is not bound. It's a function. You can't bind it like you could with Python functions even if your tried*.

    Python functions, added to a class, become methods automatically when you access them as an attribute on an instance of the class:

    >>> class KnightsWhoSayNi:
    ...     pass
    ...
    >>> def speak(self): return "Ni!"
    ...
    >>> speak
    <function speak at 0x10fe65f28>
    >>> knight = KnightsWhoSayNi()
    >>> hasattr(knight, "speak")  # using that function you mentioned!
    False
    >>> KnightsWhoSayNi.speak = speak  # adding the function to the class
    >>> knight.speak   # now it exists! As a method...
    <bound method speak of <__main__.KnightsWhoSayNi object at 0x10fe71080>>
    >>> knight.speak()
    'Ni!'
    

    Note how speak() was given an argument named self, but I didn't have to pass it in. Because the method is bound to a specific instance of the class, that's taken care of automatically. The first argument passed in is the instance. The name doesn't even matter, but self is the convention, best stick to that.

    As an exercise, you could try the above example yourself. Then try adding hasattr to the class. You'll find that you cant use it like a method, it won't become bound via knight.hasattr`, the instance won't be passed in as the first argument, you will still have to pass in two arguments for it to work at all.

    If you wanted to go off the deep end, you could learn about how binding works in the Python Descriptors HOW-TO. Don’t worry if that feels too big of a step, that’s quite advanced.


    * To pre-empt not-picking: You can emulate binding by using functools.partial() or functools.partialmethod(), but that’s not quite the same thing.