As a matter of terminology, methods are attached to class instances (or classes themselves, for classmethod
s and staticmethod
s), functions aren't.
In practice, in Python 3, the distinction is much weaker than it used to be. Originally, in Python 2, "methodness" was more important; defining a function within a class made it an unbound method if referenced from the class, and a bound method if referenced on the instances.
In Python 3, the concept of unbound methods is gone; when referenced from the class itself, you get a plain function, not a method (bound or unbound); you only get a bound method when you reference the method from an instance of the class.
Essentially, there are really two categories of things now:
- Functions which can't be bound as methods (because they don't implement the descriptor protocol to produce bound methods; applies solely to built-in functions in CPython)
- Functions which can be bound as methods (includes all functions defined in Python itself)
Everything in category 2 is a function which can act as a method if attached to a class, then referenced from an instance of said class. The nomenclature describes the intent, but as a matter of implementation, there is very little difference left.
Note that even in Python 2, the two category approach was the same, it's just that the descriptor protocol for functions was invoked even when loaded from the class itself (to bypass it and get the raw function without creating an unbound method, you had to do ClassName.__dict__['methodname']
). So it's always been the case that methods are just the result of binding functions as a matter of implementation.