Python is a dynamic language: you can add/remove/modify attributes and functions of objects such that they suite your need. Although this is a bit bold, basically an object is no more than a dictionary. You can simply add attributes to one object, etc. Python also supports - more or less - the idea of duck typing:
"If it looks like a duck and quacks like a duck, it's a duck".
In other words, if you want to let an object look like another object a certain function expects, simply give it feathers and legs and remove its second head and the function will accept it.
This is one of the design principles of Python and it can be very useful: say you want to patch a function in a piece of code you can simply:
def better_function(param,eter):
#...
pass
somemodule.badfunction = better_function
and thus fix the code. Furthermore if there is a package that for instance does some nice things with webpages, you can easily modify it to do things with say printers, by rewriting a few methods.
The downside is indeed that you have less control about modules that would introduce bugs (on purpose; I am not claiming they do, although one cannot exclude malicious intent). There are some ways to protect your code, for instance making classes and objects immutable: you can overwrite the __setattr__
and __delattr__
functions that are called to set and delete an attribute and thus make it hard/impossible to alter an object, but this would destroy the dynamism of Python: Python programmers expect they can alter an object.
Heavy protection of objects/classes/... is however against the principles of a dynamic language. If you want a language that guarantees such things, you better use a statically typed language like haskell.