Implementation-specific private methods will use
_single_underscore_prefix. Names with a leading double underscore
will only be used in special cases, as they makes subclassing
difficult (such names are not easily seen by child classes).

Occasionally some run-in lowercase names are used, but mostly for
very short names or where we are implementing methods very similar to
existing ones in a base class (like runlines() where
runsource() and runcode() had established precedent).

The old IPython codebase has a big mix of classes and modules
prefixed with an explicit IP of ip. This is not necessary and
all new code should not use this prefix. The only case where this
approach is justified is for classes or functions which are expected
to be imported into external namespaces and a very generic name (like
Shell) that is likely to clash with something else. However, if a
prefix seems absolutely necessary the more specific IPY or
ipy are preferred.

In general, objects should declare, in their class, all attributes the
object is meant to hold throughout its life. While Python allows you to
add an attribute to an instance at any point in time, this makes the
code harder to read and requires methods to constantly use checks with
hasattr() or try/except calls. By declaring all attributes of the object
in the class header, there is a single place one can refer to for
understanding the object’s data interface, where comments can explain
the role of each variable and when possible, sensible deafaults can be
assigned.

If an attribute is meant to contain a mutable object, it should be set
to None in the class and its mutable value should be set in the
object’s constructor. Since class attributes are shared by all
instances, failure to do this can lead to difficult to track bugs. But
you should still set it in the class declaration so the interface
specification is complete and documented in one place.

A simple example:

classFoo(object):# X does..., sensible default given:x=1# y does..., default will be set by constructory=None# z starts as an empty list, must be set in constructorz=Nonedef__init__(self,y):self.y=yself.z=[]