If you know other object-oriented languages such as Java, you will find some differences between them and Python regarding visibility. Classes in other object-oriented languages declare the visibility of their attributes. If an attribute is declared private, only that class can see it. If it is public, anyone using an object of that class can see it. And there is usually a protected visibility that says that code in that class and any subclass can see the attribute, but code outside those classes cannot. Of course, the same visibility restrictions can also be used on methods.
Python objects have a single pool of attributes. In other languages, each class has its own separate pool of attributes in the instance object. Suppose you want to use some private attribute exclusively in one class. In the other languages, each class can use the same private name as any other class, and all will be kept separate. Thus you can program a class without worrying too much about what private attribute names its superclasses are using. In Python, if two of the classes use the same name for different purposes, they will clobber each other's data and the program will probably crash.
These visibility restrictions are considered important to object-or_iented programming. One goal is to have objects provide encapsulation: An object is supposed to provide an interface to the outside users of the object and hide details from them internally. Thus, programmers are required to program to the interface. The implementation of the object can change, but its users will not have to change their code.
Python has no such visibility restrictions. All attributes and methods are visible to everyone. Anyone who wishes to use knowledge about the implementation of an object can do so. That can result in more efficient code. It can also result in a crash if the implementation changes. The language does nothing to prohibit programmers from "breaking encapsulation."
However, Python does provide "name mangling" to help hide names in classes. If you begin the name of an attribute of a class with two underscores, and you don't end it with any underscores, it is automatically rewritten to include the class name. Here's an example:
>>> class XX: ... def __init__(self): ... self.__x=0 ... >>> z=XX() >>> dir(z) ['_XX__x']
As you can see, attribute __x in class XX was renamed to _XX__x. It doesn't prevent anyone from accessing it, but it does make it more unpleasant, which should serve to discourage casual use. Just as important, this keeps the attributes used privately by one class separate from those used by another.
As in other object-oriented languages, a method declared in a subclass will hide a method in a superclass with the same name. Python stops looking for a method as soon as it finds one with the right name. Unlike some other object-oriented languages, there is no method overloading in Python. Method overloading allows you to declare several methods with the same name but different signatures; that is, different numbers or types of parameters. All those methods will be visible at the same time. The compiler will look at a method call and choose the correct method to execute for the argument list given in the call. Python has no such facility. There are no type declarations, so the types of parameters cannot be specified to help in choosing which method is being called, and the parameter-passing conventions are so loose, even the number of parameters would not be a good way to choose a method.