Why do some methods use dot notation and others don't?
The key word here is method. There is a slight difference between a function and a method.
Method
Is a function that is defined in the class of the given object. For example:
class Dog:
def bark(self):
print 'Woof woof!'
rufus = Dog()
rufus.bark() # called from the object
Function
A function is a globally defined procedure:
def bark():
print 'Woof woof!'
As for your question regarding the len
function, the globally defined function calls the object's __len__
special method. So in this scenario, it is an issue of readability.
Otherwise, methods are better when they apply only to certain objects. Functions are better when they apply to multiple objects. For example, how can you uppercase a number? You wouldn't define that as a function, you'd define it as only a method only in the string class.
What you call "dot notation" are class methods and they only work for classes that have the method defined by the class implementer. len
is a builtin function that takes one argument and returns the size of that object. A class may implement a method called len
if its wants to, but most don't. The builtin len
function has a rule that says if a class has a method called __len__
, it will use it, so this works:
>>> class C(object):
... def __len__(self):
... return 100
...
>>> len(C())
100
"help".upper
is the opposite. The string class defines a method called upper
, but that doesn't mean there has to be a function called upper
also. It turns out that there is an upper
function in the string
module, but generally you don't have to implement an extra function just because you implemented a class method.
This is the difference between a function and a method. If you are only just learning the basics, maybe simply accept that this difference exists, and that you will eventually understand it.
Still here? It's not even hard, actually. In object-oriented programming, methods are preferred over functions for many things, because that means one type of object can override its version of the method without affecting the rest of the system.
For example, let's pretend you had a new kind of string where accented characters should lose their accent when you call .upper()
. Instances of this type can subclass str
and behave exactly the same in every other aspect, basically for free; all they need to redefine is the upper
method (and even then, probably call the method of the base class and only change the logic when you handle an accented lowercase character). And software which expects to work on strings will just continue to work and not even know the difference if you pass in an object of this new type where a standard str
is expected.
A design principle in Python is that everything is an object. This means you can create your own replacements even for basic fundamental objects like object
, class
, and type
, i.e. extend or override the basic language for your application or platform.
In fact, this happened in Python 2 when unicode
strings were introduced to the language. A lot of application software continued to work exactly as before, but now with unicode
instances where previously the code had been written to handle str
instances. (This difference no longer exists in Python 3; or rather, the type which was called str
and was used almost everywhere is now called bytes
and is only used when you specifically want to handle data which is not text.)
Going back to our new upper
method, think about the opposite case; if upper
was just a function in the standard library, how would you even think about modifying software which needs upper
to behave differently? What if tomorrow your boss wants you to do the same for lower
? It would be a huge undertaking, and the changes you would have to make all over the code base would easily tend towards a spaghetti structure, as well as probably introduce subtle new bugs.
This is one of the cornerstones of object-oriented programming, but it probably only really makes ense when you learn the other two or three principles in a more structured introduction. For now, perhaps the quick and dirty summary is "methods make the implementation modular and extensible."