Based partly on the Tutorial, section 9. Other references: see the list at http://www.python.org/doc/newstyle/
We often hear this:
There is no real "privacy" in Python, in the sense of C++ or Java "private" fields or methods. All instance variables and methods are public.
On the other hand, starting a variable name with two underscores
(__myvar)
creates a sort-of private variable: the name is mangled
so that it only makes sense within that class.
All Python methods (and Java) are what C++ calls "virtual" methods. That is, the actual type of the object whose method is called, rather than its declared type, determines which method is run.
In Python, it really couldn't be otherwise, since variables don't have type declarations.
Suppose we have x = an instance of MyClass. Then,
x.f(a, b, c) is a method call.x.f is a method object,
i.e., a function, and can be assigned to a variable:
y = x.f y(a, b, c)
Although methods are usually defined using "def" inside a class definition, externally defined functions can also be assigned and become methods, and so can the value of a lambda expression:
def foo(): ... class Gummy: f = foo # without arguments g = lambda self, x, y: ...
→ Example: circle.py
A superclass is named in parentheses after the class name in its declaration:
class MyClass (HigherClass): ...
If no superlcass is named, then in Python 2 there is no superclass,
but in Python 3, the superclass defaults to object.
class Tops: ...
Exception
is a class, and programmers can define subclasses of
Exception.
→ Example: old_style_accounts.py; session I/O
HigherClass.foo(self, x, y)instead of
self.foo(x, y)This is ugly. What if the superclass is changed, or changes its name?
So if we have this class hierarchy:
A
/ \
/ \
B C
\ /
\ /
D
The MRO of D is D, B, A, C.
The problem here is that if A defines a method and C overrides it, shouldn't we use C's method instead of A's, since C is a more specific class?
These two "warts" — the method resolution order and the syntax for calling a method of the superclass — are now fixed in what are called "new-style classes." Old-style classes only exist in Python 2; Python 3 uses new-style classes.
A new-style class is distinguished by the fact that it is a subclass
of a type.
For example,
dict, list, and float
are types. An old-style
class cannot be derived from a type. The highest (most general) type
is named object.
Consequently, we can define a new-style class
by making it a subclass of object or some other type:
class X (object): ... class Y (dict): ...
class Z (X, Y):
def foo (self, a, b):
r = super(Z, self).foo(a, b)
...
The meaning of "super(Z, self)" is "call the next method in the
method resolution order after the method for the class Z"
(which is the method we are currently in). Consequently,
there's no problem if the names of the superclasses change.
Suppose we have
object
|
|
A
/ \
/ \
B C
\ /
\ /
D
The MRO of class D is D, B, C, A, object. Consequently, if
C defines a more specific method than A, the method for C will
be found first.
The exact algorithm for the new-style MRO seems to be complex, so perhaps it's best to call the help function to be sure, or to look at the attribute __mro__ which is defined for new-style classes:
help(D) D.__mro__
→ Example: accounts.py
→ Example: old_new.py
There is much more in new-style classes that I do not yet understand:
__new__ method
One feature seems to be lacking: the "generic function,
multi-method" approach of Common Lisp. Multi-method calls
are written foo(a, b, c)
instead of a.foo(b, c), and the
method dispatch can depend not only on the
class of a, but
also the classes of b and c.
(Actually, in Lisp, the multi-method call would be written
(foo a b c), like any other Lisp function call.)
If mylist = [1, 2, 3], then the iteration
for x in mylist: ...
is supported, behind the scenes, by objects known as iterators.
What actually happens in the for statement is that it obtains an
iterator for mylist by calling mylist.__iter__().
The iterator has a next()
method which returns the next method
in the sequence if there is one;
otherwise, it raises a StopIterator
exception. The for statement catches this exception and terminates
itself.
You can define an iterator like this:
class X:
def __iter__ (self):
return ...
The value returned must be an object — perhaps of the same class X, perhaps a different, related class — which has a next() method which behaves as described above.
→ Example: iter_generator.py
Generators are functions which use the
yield statement to return values. When called
again, they resume at the same point and deliver the next
value. They can serve as iterators.
Their __iter__ and next methods
are created automatically.
→ Example: iter_generator.py
→ Example: generators.py
Generator expressions are written like list comprehensions, but using ()'s instead of []'s. They generate a sequence one element at a time, as needed, without storing all of them in memory. They are less flexible than generator definitions, but more convenient to write when they can do the job.
→ Example: iter_generator.py
Use new-style (Python 3) classes for all of these. But note that in Python 3, you don't have to declare the superclasses as `(object)`, because that is the default.