|
## 9.>
Compared with other programming languages, Python’s>
In C++ terminology, normally>
(Lacking universally accepted terminology to talk about>
## 9.1. A Word About Names and Objects
Objects have individuality, and multiple names (in multiple scopes) can be bound to the same object. This is known as aliasing in other languages. This is usually not appreciated on a first glance at Python, and can be safely ignored when dealing with immutable basic types (numbers, strings, tuples). However, aliasing has a possibly surprising effect on the semantics of Python code involving mutable objects such as lists, dictionaries, and most other types. This is usually used to the benefit of the program, since aliases behave like pointers in some respects. For example, passing an object is cheap since only a pointer is passed by the implementation; and if a function modifies an object passed as an argument, the caller will see the change — this eliminates the need for two different argument passing mechanisms as in Pascal.
## 9.2. Python Scopes and Namespaces
A namespace is a mapping from names to objects.
Most namespaces are currently implemented as Python dictionaries, but that’s normally not noticeable in any way (except for performance), and it may change in the future.
Examples of namespaces are: the set of built-in names (containing functions such as abs(), and built-in exception names); the global names in a module; and the local names in a function invocation.
In a sense the set of attributes of an object also form a namespace.
The important thing to know about namespaces is that there is absolutely no> for instance, two different modules may both define a function maximize without confusion — users of the modules must prefix it with the module name.
9.3. A First Look at> Classes introduce a little bit of new syntax, three new object types, and some new semantics.
9.3.1.>
The simplest form of>
class>
.
.
.
Class definitions, like function definitions (def statements) must be executed before they have any effect. (You could conceivably place a>
In practice, the statements inside a>
When a>
When a>
9.3.2.> Class objects support two kinds of operations: attribute references and instantiation.
Attribute references use the standard syntax used for all attribute references in Python: obj.name. Valid attribute names are all the names that were in the> class MyClass:
"""A simple example> i = 12345
def f(self):
return 'hello world'
then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object, respectively.>doc is also a valid attribute, returning the docstring belonging to the>
Class instantiation uses function notation. Just pretend that the> x = MyClass()
creates a new instance of the> The instantiation operation (“calling” a>init(), like this:
def init(self):
self.data = []
When a>init() method,>init() for the newly-created> x = MyClass()
Of course, the init() method may have arguments for greater flexibility. In that case, arguments given to the>init(). For example,
class Complex:
... def init(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
x = Complex(3.0, -4.5)
x.r, x.i
(3.0, -4.5)
9.3.3. Instance Objects
Now what can we do with instance objects? The only operations understood by instance objects are attribute references. There are two kinds of valid attribute names, data attributes and methods.
data attributes correspond to “instance variables” in Smalltalk, and to “data members” in C++. Data attributes need not be declared; like local variables, they spring into existence when they are first assigned to. For example, if x is the instance of MyClass created above, the following piece of code will print the value 16, without leaving a trace:
x.counter = 1
while x.counter < 10:
x.counter = x.counter * 2
print x.counter
del x.counter
The other kind of instance attribute reference is a method. A method is a function that “belongs to” an object. (In Python, the term method is not unique to>
Valid method names of an instance object depend on its> 9.3.4. Method Objects
Usually, a method is called right after it is bound:
x.f()
In the MyClass example, this will return the string 'hello world'. However, it is not necessary to call a method right away: x.f is a method object, and can be stored away and called at a later time. For example:
xf = x.f
while True:
print xf()
will continue to print hello world until the end of time.
What exactly happens when a method is called? You may have noticed that x.f() was called without an argument above, even though the function definition for f() specified an argument. What happened to the argument? Surely Python raises an exception when a function that requires an argument is called without any — even if the argument isn’t actually used...
Actually, you may have guessed the answer: the special thing about methods is that the object is passed as the first argument of the function. In our example, the call x.f() is exactly equivalent to MyClass.f(x). In general, calling a method with a list of n arguments is equivalent to calling the corresponding function with an argument list that is created by inserting the method’s object before the first argument.
If you still don’t understand how methods work, a look at the implementation can perhaps clarify matters. When an instance attribute is referenced that isn’t a data attribute, its>
9.3.5.> Generally speaking, instance variables are for data unique to each instance and> class Dog:
kind = 'canine' #>
def __init__(self, name):
self.name = name # instance variable unique to each instance
d = Dog('Fido')
e = Dog('Buddy')
d.kind # shared by all dogs
'canine'
e.kind # shared by all dogs
'canine'
d.name # unique to d
'Fido'
e.name # unique to e
'Buddy'As discussed in A Word About Names and Objects, shared data can have possibly surprising effects with involving mutable objects such as lists and dictionaries. For example, the tricks list in the following code should not be used as a>
class Dog:
tricks = [] # mistaken use of a>
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick)
d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
d.tricks # unexpectedly shared by all dogs
['roll over', 'play dead']Correct design of the>
class Dog:
def __init__(self, name): self.name = name
self.tricks = [] # creates a new empty list for each dog
def add_trick(self, trick):
self.tricks.append(trick)
d = Dog('Fido')
e = Dog('Buddy')
d.add_trick('roll over')
e.add_trick('play dead')
d.tricks
['roll over']
e.tricks
['play dead']9.4. Random Remarks
Data attributes override method attributes with the same name; to avoid accidental name conflicts, which may cause hard-to-find bugs in large programs, it is wise to use some kind of convention that minimizes the chance of conflicts. Possible conventions include capitalizing method names, prefixing data attribute names with a small unique string (perhaps just an underscore), or using verbs for methods and nouns for data attributes.
Data attributes may be referenced by methods as well as by ordinary users (“clients”) of an object. In other words,> Clients should use data attributes with care — clients may mess up invariants maintained by the methods by stamping on their data attributes. Note that clients may add data attributes of their own to an instance object without affecting the validity of the methods, as long as name conflicts are avoided — again, a naming convention can save a lot of headaches here.
There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.
Often, the first argument of a method is called self. This is nothing more than a convention: the name self has absolutely no special meaning to Python. Note, however, that by not following the convention your code may be less readable to other Python programmers, and it is also conceivable that a>
Any function object that is a>
Function defined outside the> def f1(self, x, y):
return min(x, x+y)
class C:
f = f1
def g(self):
return 'hello world'
h = g
Now f, g and h are all attributes of> Methods may call other methods by using method attributes of the self argument:
class Bag:
def init(self):
self.data = []
def add(self, x):
self.data.append(x)
def addtwice(self, x):
self.add(x)
self.add(x)
Methods may reference global names in the same way as ordinary functions. The global scope associated with a method is the module containing its definition. (A> Each value is an object, and therefore has a>class.
9.5. Inheritance
Of course, a language feature would not be worthy of the name “class” without supporting inheritance. The syntax for a derived> class DerivedClassName(BaseClassName):
.
.
.
The name BaseClassName must be defined in a scope containing the derived>
class DerivedClassName(modname.BaseClassName):Execution of a derived>
There’s nothing special about instantiation of derived>
Derived>
An overriding method in a derived> Python has two built-in functions that work with inheritance:
Use isinstance() to check an instance’s type: isinstance(obj, int) will be True only if obj.class is int or some>
Use issubclass() to check> 9.5.1. Multiple Inheritance
Python supports a limited form of multiple inheritance as well. A> class DerivedClassName(Base1, Base2, Base3):
.
.
.
For old-style>
(To some people breadth first — searching Base2 and Base3 before the base>
For new-style>
With new-style>
9.6. Private Variables and> “Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g. _spam) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.
Since there is a valid use-case for> Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. For example:
class Mapping:
def init(self, iterable):
self.items_list = []
self.__update(iterable)
def update(self, iterable): for item in iterable:
self.items_list.append(item)
__update = update # private copy of original update() method
class MappingSubclass(Mapping):
def update(self, keys, values): # provides new signature for update()
# but does not break __init__()
for item in zip(keys, values):
self.items_list.append(item)
Note that the mangling rules are designed mostly to avoid accidents; it still is possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger.
Notice that code passed to exec, eval() or execfile() does not consider the>dict directly.
9.7. Odds and Ends
Sometimes it is useful to have a data type similar to the Pascal “record” or C “struct”, bundling together a few named data items. An empty> class Employee:
pass
john = Employee() # Create an empty employee record
Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
A piece of Python code that expects a particular abstract data type can often be passed a> Instance method objects have attributes, too: m.im_self is the instance object with the method m(), and m.im_func is the function object corresponding to the method.
9.8. Exceptions Are>
User-defined exceptions are> There are two new valid (semantic) forms for the raise statement:
raise> raise instance
In the first form, instance must be an instance of> raise instance.class, instance
A> class B:
pass
class C(B):
pass
class D(C):
pass
for c in [B, C, D]:
try:
raise c()
except D:
print "D"
except C:
print "C"
except B:
print "B"
Note that if the except clauses were reversed (with except B first), it would have printed B, B, B — the first matching except clause is triggered.
When an error message is printed for an unhandled exception, the exception’s> 9.9. Iterators
By now you have probably noticed that most container objects can be looped over using a for statement:
for element in [1, 2, 3]:
print element
for element in (1, 2, 3):
print element
for key in {'one':1, 'two':2}:
print key
for char in "123":
print char
for line in open("myfile.txt"):
print line,
This style of access is clear, concise, and convenient. The use of iterators pervades and unifies Python. Behind the scenes, the for statement calls iter() on the container object. The function returns an iterator object that defines the method next() which accesses elements in the container one at a time. When there are no more elements, next() raises a StopIteration exception which tells the for loop to terminate. This example shows how it all works:
s = 'abc'
it = iter(s)
it
it.next()
'a'
it.next()
'b'
it.next()
'c'
it.next()
Traceback (most recent call last):
File "", line 1, in ?
it.next()
StopIteration
Having seen the mechanics behind the iterator protocol, it is easy to add iterator behavior to your>iter() method which returns an object with a next() method. If the>iter() can just return self:
class Reverse:
"""Iterator for looping over a sequence backwards."""
def init(self, data):
self.data = data
self.index = len(data)
def iter(self):
return self
def next(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index]
rev = Reverse('spam')
iter(rev)
for char in rev:
... print char
...
m
a
p
s
9.10. Generators
Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called, the generator resumes where it left-off (it remembers all the data values and which statement was last executed). An example shows that generators can be trivially easy to create:
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
for char in reverse('golf'):
... print char
...
f
l
o
g
Anything that can be done with generators can also be done with>iter() and next() methods are created automatically.
Another key feature is that the local variables and execution state are automatically saved between calls. This made the function easier to write and much more clear than an approach using instance variables like self.index and self.data.
In addition to automatic method creation and saving program state, when generators terminate, they automatically raise StopIteration. In combination, these features make it easy to create iterators with no more effort than writing a regular function.
9.11. Generator Expressions
Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of brackets. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expressions are more compact but less versatile than full generator definitions and tend to be more memory friendly than equivalent list comprehensions.
Examples:
sum(i*i for i in range(10)) # sum of squares
285
xvec = [10, 20, 30]
yvec = [7, 5, 3]
sum(x*y for x,y in zip(xvec, yvec)) # dot product
260
from math import pi, sin
sine_table = dict((x, sin(x*pi/180)) for x in range(0, 91))
unique_words = set(word for line in page for word in line.split())
valedictorian = max((student.gpa, student.name) for student in graduates)
data = 'golf'
list(data for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']
Footnotes
[1] Except for one thing. Module objects have a secret read-only attribute called dict which returns the dictionary used to implement the module’s namespace; the name dict is an attribute but not a global name. Obviously, using this violates the abstraction of namespace implementation, and should be restricted to things like post-mortem debuggers. |
|