Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Python Cookbook 2Nd Edition Jun 1002005 [Electronic resources] - نسخه متنی

David Ascher, Alex Martelli, Anna Ravenscroft

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید

Introduction


Credit: Alex Martelli, author of
Python in a Nutshell
(O'Reilly)

Object-oriented programming (OOP) is among Python's
greatest strengths. Python's OOP features continue
to improve steadily and gradually, just like Python in general. You
could already write better object-oriented programs in Python 1.5.2
(the ancient, long-stable version that was new when I first began to
work with Python) than in any other popular language (excluding, of
course, Lisp and its variants: I doubt there's
anything you can't do well in Lisp-like languages,
as long as you can stomach parentheses-heavy concrete syntax). For a
few years now, since the release of Python 2.2, Python OOP has become
substantially better than it was with 1.5.2. I am constantly amazed
at the systematic progress Python achieves without sacrificing
solidity, stability, and backwards-compatibility.

To get the most out of Python's great OOP features,
you should use them the Python way, rather than trying to mimic C++,
Java, Smalltalk, or other languages you may be familiar with. You can
do a lot of mimicry, thanks to Python's power.
However, you'll get better mileage if you invest
time and energy in understanding the Python way. Most of the
investment is in increasing your understanding of OOP itself: what
is OOP, what does it buy you, and which
underlying mechanisms can your object-oriented programs use? The rest
of the investment is in understanding the specific mechanisms that
Python itself offers.

One caveat is in order. For such a
high-level language, Python is quite explicit about the OOP
mechanisms it uses behind the curtains: they're
exposed and available for your exploration and tinkering. Exploration
and understanding are good, but beware the temptation to tinker. In
other words, don't use unnecessary black magic just
because you can. Specifically, don't use black magic
in production code. If you can meet your goals with simplicity (and
most often, in Python, you can), then keep your code simple.
Simplicity pays off in readability, maintainability, and, more often
than not, performance, too. To describe something as clever is
not considered a compliment in the Python
culture.


So what is OOP all about? First of
all, it's about keeping some
state (data) and some
behavior (code) together in handy packets.
"Handy packets" is the key here.
Every program has state and behaviorprogramming paradigms
differ only in how you view, organize, and package them. If the
packaging is in terms of objects that typically comprise state and
behavior, you're using OOP. Some object-oriented
languages force you to use OOP for everything, so you end up with
many objects that lack either state or behavior. Python, however,
supports multiple paradigms. While everything in Python is an object,
you package things as OOP objects only when you want to. Other
languages try to force your programming style into a predefined mold
for your own good, while Python empowers you to make and express your
own design choices.

With OOP, once you have specified how an object is composed, you can
instantiate as many objects of that kind as you need. When you
don't want to create multiple objects, consider
using other Python constructs, such as modules. In this chapter,
you'll find recipes for Singleton, an
object-oriented design pattern that eliminates the multiplicity of
instantiation, and Borg, an idiom that makes multiple instances share
state. But if you want only one instance, in Python
it's often best to use a module, not an OOP
object.


To describe how an object is made, use the
class statement:

class SomeName(object):
"" You usually define data and code here (in the class body). ""

SomeName

is a
class object. It's a
first-class object, like every Python object,
meaning that you can put it in lists and dictionaries, pass it as an
argument to functions, and so on. You don't
have to include the (object)
part in the class header
clauseclass SomeName: by itself is also
valid Python syntaxbut normally you
should include that part, as
we'll see later.


When you want a new instance of a
class, call the class object as if it were a function. Each call
returns a new instance object:

anInstance = SomeName( )
another = SomeName( )

anInstance
and
another are two distinct instance
objects
, instances of the SomeName class.
(See Recipe 4.18 for a
class that does little more than this and yet is already quite
useful.) You can freely bind (i.e., assign or
set) and access (i.e., get)
attributes (i.e., state) of an instance
object:

anInstance.someNumber = 23 * 45
print anInstance.someNumber # emits: 1035

Instances of an "empty" class like
SomeName have no behavior, but they may have state.
Most often, however, you want instances to have behavior. Specify the
behavior you want by defining methods (with
def statements, just like you define functions)
inside the class
body:

class Behave(object):
def _ _init_ _(self, name):
self.name = name
def once(self):
print "Hello,", self.name
def rename(self, newName)
self.name = newName
def repeat(self, N):
for i in range(N): self.once( )

You define methods with the same
def statement Python uses to define functions,
exactly because methods are essentially
functions. However, a method is an attribute of a class object, and
its first formal argument is (by universal convention) named
self. self always refers to the
instance on which you call the method.

The method with the special name
_ _init_ _ is also known as the
constructor (or more properly the
initializer) for instances of the class.
Python calls this special method to initialize each newly created
instance with the arguments that you passed when calling the class
(except for self, which you do not pass explicitly
since Python supplies it automatically). The body of _
_init_ _
typically binds attributes on the newly created
self instance to appropriately initialize the
instance's state.

Other methods implement the behavior of instances of the class.
Typically, they do so by accessing instance attributes. Also, methods
often rebind instance attributes, and they may call other methods.
Within a class definition, these actions are always done with the
self.something syntax. Once you instantiate the
class, however, you call methods on the instance, access the
instance's attributes, and even rebind them, using
the theobject.something
syntax:

beehive = Behave("Queen Bee")
beehive.repeat(3)
beehive.rename("Stinger")
beehive.once( )
print beehive.name
beehive.name = 'See, you can rebind it "from the outside" too,
if you want'
beehive.repeat(2)


self


No true difference exists between what I described as the
self.something syntax and the
theobject.something syntax: the former is simply a
special case of the latter, when the name of reference
theobject happens to be self!

If you're new to OOP in Python, you should try, in
an interactive Python environment, the example snippets I have shown
so far and those I'm going to show in the rest of
this Introduction. One of the best interactive Python environments
for such exploration is the GUI shell supplied as part of the free
IDLE development environment that comes with
Python.

In addition to the constructor
(_ _init_ _), your class may have other special
methods, meaning methods with names that start and end with two
underscores. Python calls the special methods of a class when
instances of the class are used in various operations and built-in
functions. For example, len(x) returns
x._ _len_ _( ); a+b normally
returns a._ _add_ _(b); a[b]
returns a._ _getitem_ _(b). Therefore, by defining
special methods in a class, you can make instances of that class
interchangeable with objects of built-in types, such as numbers,
lists, and dictionaries.


Each operation and built-in function can try several special methods
in some specific order. For example, a+b
first tries a._ _add_ _(b),
but, if that doesn't pan out, the operation also
gives object b a say in the matter, by
next trying b._ _radd_ _(a). This kind of
intrinsic structuring among special methods, that operations and
built-in functions can provide, is an important added value of such
functions and operations with respect to pure OO notation such as
someobject.somemethod(arguments).

The ability to handle different objects in
similar ways, known as polymorphism, is a
major advantage of OOP. Thanks to polymorphism, you can call the same
method on various objects, and each object can implement the method
appropriately. For example, in addition to the
Behave class, you might have another class that
implements a repeat method with rather different
behavior:

class Repeater(object):
def repeat(self, N): print N*"*-*"

You can mix instances of Behave and
Repeater at will, as long as the only method you
call on each such instance is repeat:

aMix = beehive, Behave('John'), Repeater( ), Behave('world')
for whatever in aMix: whatever.repeat(3)




Other
languages require inheritance, or the formal definition and
implementation of interfaces, in order to enable such polymorphism.
In Python, all you need is to have methods with the same
signature (i.e., methods of the same name,
callable with the same arguments). This signature-based
polymorphism
allows a style of programming
that's quite similar to generic
programming
(e.g., as supported by C++'s
template classes and functions), without syntax
cruft and without conceptual complications.






Python
also uses inheritance, which is mostly a handy,
elegant, structured way to reuse code. You can define a class by
inheriting from another (i.e., subclassing the
other class) and then adding or redefining (known as
overriding) some methods:

class Subclass(Behave):
def once(self): print '(%s)' % self.name
subInstance = Subclass("Queen Bee")
subInstance.repeat(3)



The Subclass
class overrides only the once method, but you can
also call the repeat method on
subInstance, since
Subclass inherits that method from the
Behave superclass. The body of the
repeat method calls once
n times on the specific instance, using whatever
version of the once method the instance has. In this
case, each call uses the method from the Subclass
class, which prints the name in parentheses, not the original version
from the Behave class, which prints the name after a
greeting. The idea of a method calling other methods on the same
instance and getting the appropriately overridden version of each is
important in every object-oriented language, including Python. It is
also known as the Template Method Design
Pattern.



The
method of a subclass often overrides a method from the superclass,
but also needs to call the method of the superclass as part of its
own operation. You can do this in Python by explicitly getting the
method as a class attribute and passing the instance as the first
argument:

class OneMore(Behave):
def repeat(self, N): Behave.repeat(self, N+1)
zealant = OneMore("Worker Bee")
zealant.repeat(3)



The OneMore class
implements its own repeat method in terms of the
method with the same name in its superclass, Behave,
with a slight change. This approach, known as
delegation, is pervasive in all programming.
Delegation involves implementing some functionality by letting
another existing piece of code do most of the work, often with some
slight variation. An overriding method often is best implemented by
delegating some of the work to the same method in the superclass. In
Python, the syntax Classname.method(self, . .
.) delegates to
Classname's version of the method.
A vastly preferable way to perform superclass
delegation, however, is to use Python's built-in
super:

class OneMore(Behave):
def repeat(self, N): super(OneMore, self).repeat(N+1)

This super construct is equivalent to the explicit
use of Behave.repeat in this simple case, but it
also allows class OneMore to be used smoothly with
multiple inheritance. Even if
you're not interested in multiple inheritance at
first, you should still get into the habit of using
super instead of explicit delegation to your base
class by namesuper costs nothing and it may
prove very useful to you in the future.



Python does fully support multiple
inheritance: one class can inherit from several other classes. In
terms of coding, this feature is sometimes just a minor one that lets
you use the mix-in class idiom, a convenient way to supply
functionality across a broad range of classes. (See Recipe 6.20 and Recipe 6.12, for unusual but powerful
examples of using the mix-in idiom.) However, multiple inheritance is
particularly important because of its implications for
object-oriented analysisthe way you conceptualize your problem
and your solution in the first place. Single inheritance pushes you
to frame your problem space via taxonomy (i.e., mutually exclusive
classification). The real world doesn't work like
that. Rather, it resembles Jorge Luis Borges'
explanation in The Analytical Language of John
Wilkins
, from a purported Chinese encyclopedia,
The Celestial Emporium of Benevolent
Knowledge
. Borges explains that all animals are divided
into:

  • Those that belong to the Emperor

  • Embalmed ones

  • Those that are trained

  • Suckling pigs

  • Mermaids

  • Fabulous ones

  • Stray dogs

  • Those included in the present classification

  • Those that tremble as if they were mad

  • Innumerable ones

  • Those drawn with a very fine camelhair brush

  • Others

  • Those that have just broken a flower vase

  • Those that from a long way off look like flies


You get the point: taxonomy forces you to pigeonhole, fitting
everything into categories that aren't truly
mutually exclusive. Modeling aspects of the real world in your
programs is hard enough without buying into artificial constraints
such as taxonomy. Multiple inheritance frees you from these
constraints.

Ah, yes, that
(object) thingI had
promised to come back to it later. Now that you've
seen Python's notation for inheritance, you realize
that writing class X(object)
means that class X inherits from class
object. If you just write class
Y
:, you're saying that Y
doesn't inherit from
anythingY, so to speak,
"stands on its own". For backwards
compatibility, Python allows you to request such a rootless
class
, and, if you do, then Python makes class
Y an "old-style"
class, also known as a classic class, meaning
a class that works just like all classes used to work in the Python
versions of old. Python is very keen on backwards-compatibility.

For many elementary uses, you
won't notice the difference between classic classes
and the new-style classes that are recommended for all new Python
code you write. However, it's important to
underscore that classic classes are a legacy
feature, not recommended for new code. Even
within the limited compass of elementary OOP features that I cover in
this Introduction, you will already feel some of the limitations of
classic classes: for example, you cannot use super
within classic classes, and in practice, you should not do any
serious use of multiple inheritance with them. Many important
features of today's Python OOP, such as the
property built-in, can't work
completely, if they even work at all, with old-style classes.


In
practice, even if you're maintaining a large body of
legacy Python code, the next time you need to do any substantial
maintenance on that code, you should take the little effort required
to ensure all classes are new style: it's a small
job, and it will ease your future maintenance burden quite a bit.
Instead of explicitly having all your classes inherit from
object, an equivalent alternative is to add the
following assignment statement close to the start of every module
that defines any
classes:

_ _metaclass_ _ = type

The built-in type
is the metaclass of object and of every other
new-style class and built-in type.
That's why inheriting from object
or any built-in type makes a class new style: the
class you're coding gets the same metaclass as its
base. A class without bases can get its metaclass from the
module-global _ _metaclass_ _ variable, which is
why the "state"ment I suggest
suffices to ensure that any classes without explicit bases are made
new-style. Even if you never make any other use of explicit
metaclasses (a rather advanced subject that is, nevertheless,
mentioned in several of this chapter's recipes),
this one simple use of them will stand you in good stead.

What Is a Metaclass?




Metaclasses do not mean
"deep, dark black magic". When you
execute any class statement, Python performs the
following steps:

Remember the class name as a string, say
n, and the class bases as a tuple, say
b.

Execute the body of the class, recording all names that the body
binds as keys in a new dictionary d, each
with its associated value (e.g., each statement such as def
f(self)
just sets d['f'] to the function
object the def statement builds).

Determine the appropriate metaclass, say
M, by inheritance or by looking for name
_ _metaclass_ _ in d
and in the globals:

if '_ _metaclass_ _' in d: M = d['_ _metaclass_ _']
elif b: M = type(b[0])
elif '_ _metaclass_ _' in globals( ): M = globals( )['_ _metaclass_ _']
else: M = types.ClassType

types.ClassType is the metaclass of old-style
classes, so this code implies that a class without bases is old style
if the name '_ _metaclass_ _' is not set in the
class body nor among the global variables of the current module.

Call M(n, b, d) and record the result as a
variable with name n in whatever scope the
class statement executed.

So, some metaclass M is
always involved in the execution of any
class statement. The metaclass is normally
type for new-style classes,
types.ClassType for old-style classes. You can set
it up to use your own custom metaclass
(normally a subclass of type), and
that is where you may reasonably feel that
things are getting a bit too advanced. However, understanding that a
class statement, such as:

class Someclass(Somebase):
_ _metaclass_ _ = type
x = 23

is exactly equivalent to the assignment statement:

Someclass = type('Someclass', (Somebase,), {'x': 23})

does help a lot in understanding the exact semantics of the
class statement.

/ 394