OBJECTS IN MIRROR ARE CLOSER THAN THEY APPEAR

Một phần của tài liệu Teach yourself python in 24 hours (Trang 219 - 414)

Hour

9 Objects at Rest 10 Defining Objects

11 Object-Oriented Programming Concepts 12 More OOP Concepts

13 Special Class Methods in Python 14 The Laboratory of Dr. Frankenstein 15 The Laboratory, Part II

16 Objects in Motion

Page 147

Hour 9

Objects at Rest

If you are uncertain as to the language being used in your class, by all means ask.

Audrey Thompson

In this hour we begin our grand tour of objects, occupying us for the central third of the book. This time, we will only be laying the groundwork for the chapters that follow, but at the end of this hour of study, you will be able to

• Define objects

• Understand variables as references to objects

• Understand the basics of OOP, or object-oriented programming

However, you will be doing very little programming. Instead, you will be learning about objects in general; the particular comes in the next chapter.

Page 148

Everything Is an Object

In Python, everything is an object, and objects are like boxes; they contain things. In the case of built-in objects, or the basic data types, the things contained are restricted. Numbers, for example, contain no methods, only a single value. Lists are objects, and contain both methods and values, but you cannot add methods to them. Tuples have no methods, but can contain all kinds of values.

Dictionaries have methods and contain values, but you can't add methods to them. So even though everything is an object in Python, there is a dichotomy between the basic data type objects and user- defined objects. User-defined objects, however, can contain whatever things you like. And ''things"

means anything. Your objects, called classes, can contain as many of the basic data types as you want, they can contain other objects, and they can contain functions. Inside objects, functions are called methods. A module, too, is an object, although not at quite the same level as is ordinarily thought of—they're not "first-class objects." But modules do allow you to put things inside of them:

basic data type variables, other objects, and methods.

When you write a Python program, you're writing a module. When you import a module into your program, those modules are known by the names of the files that they live in; you've seen this, for example, in the case of the string module. Everything that lives in the string.py file in which the string module lives must have its name qualified by the name of the module. So you've used objects and their methods plenty of times before, such as when you've used string.atoi(). But the name of the module your program runs in is always called "__main__", which is why we often put in the check to see if our program is named that, so that we can determine whether it is a module or a program:

if __name__ == "__main__":

. . .program processing or test code . . . else:

. . .module-specific stuff . . .

Even numbers are objects in Python. All objects must carry around with them a certain amount of information so that Python can recognize them as the types that they are. Thus, numbers carry around a little tag that says, "I'm a complex number," or whatever type is necessary. Whenever you type a number into your program and then use Python to run it, Python recognizes the number and puts it into a little box combining type information ("I'm a floating-point number") and the actual value that the number has. As you know, however, numbers don't have methods. Neither do strings or tuples.

Lists and dictionaries (also called a mapping type) do have methods, such as list.append() or dict.keys(). So even though everything is an object in Python, some distinctions can be drawn.

Guido and several other Python internals specialists are planning to erase these distinctions in Python 2.0, thus providing strings (and numbers and tuples) with methods,

Page 149

so that you will be able to do a "'23' .atoi()", for example. Python 2.0, however, is a long way off, so you don't need to worry about code breakage for a long time. Knowing the way Guido does things, though, I would bet that most code will still run with minimal or no changes. The methods for strings may arrive sooner, in Python 1.6 (due out in 2000), but you will still be able to use the string module.

The ''objectness" of everything is a very important part of Python's ability to do dynamic typing.

Without things being wrapped inside of containers, which carry tags for type info, Python wouldn't be able to tell, except very slowly, what kind of thing a thing is. Without that, Python would be just another statically typed language, of which we have plenty. We have plenty of slow languages, too.

Objects are collections of things. Any such collection that contains variables is said to contain, or have, state. Think of a light switch; it's either in the off state or the on state. Most variables have far more than the two states a light switch has, but this simply makes them more useful.

Any object that contains methods is said to have behavior. As with cats, any stimulus elicits a response, and that response is, obviously, behavior. With Python objects, though, you're not likely to be scratched—except metaphorically.

You can pull a cat's tail to stimulate it. Instead of pulling an object's tail, however, you can stimulate it in less irritating ways. Strict object-oriented terminology would have it that this

stimulation is called "sending a message" to the object. The cat might disagree. In Python, you don't send a message; you call a method.

In Python, some objects have state but no behavior. All the numeric data types have only state.

Strings and tuples have state; because they have no methods, they, too, are behaviorless objects.

Lists and dictionaries have both state and behavior: list. append (item) and

dict.keys() are examples of behavior. Finally, modules that have no variables at the top level are examples of objects with behavior but no state. If a cat had state but no behavior, I'm pretty sure it would be a dead cat—so I doubt if the object/cat metaphor stretches to cover these odd

combinations, other than to lead to the conclusion that the most interesting and lively objects are those which have both state and behavior.

We'll return to the properties of objects later, after we talk about variables some more.

Variables Are References to Objects

In many languages, a variable is the name given to a specific place in memory that contains a specific value. In Python, things are done differently. Values live wherever Python chooses to put them, wrapped inside their "object boxes," and a variable is just a

Page 150

reference to that object. This is important because it means many variables can refer to the same object. If the object changes, then every variable that refers to that object will reflect the change.

Remember that even integers are objects. To conserve memory and reduce the time necessary to build a new object whenever a new integer is referred to or assigned as the value of a variable, Python ''pre-boxes" the first 100 integers. Any variable that refers to the value 42, for example, refers to the same integer object. Creating a new integer object only requires using the new value—

for example, 1,000,000. When we speak of integers, there is ordinarily no problem, because you cannot change the integer object 1 to anything else.

Other objects, however, are mutable. Remember our discussion in earlier chapters of the

differences between mutable and immutable objects? Numbers, strings, and tuples are immutable, whereas lists, dictionaries and user-defined objects are mutable. When you change an immutable variable, you're changing what it refers to, not the object itself. Complex numbers, although they have two components that you can read individually, are just as immutable as any other number. Try these lines in your interpreter:

i = 3+4j i.real i.imag

i.real = 2.0

You should see an error:

>>> i.real = 2.0

Traceback (innermost last):

File "<stdin>", line 1, in ?

TypeError: object has read-only attributes

>>>

Variables that are references to immutable objects should, in theory, present no major confusion, because the components that make up such immutable objects cannot be modified. However, this changes when we work with lists, dictionaries, and user-definable objects.

From previous chapters, you should remember that functions can have default arguments—values supplied to be used when no value is given by the caller. A function can have a list as an argument;

if no list is supplied by the caller, it might require an empty list. Thus, you could be excused for beginning a function definition like this:

def myfunc(1=[]):

. . .

This would not be a good idea, as you can see from examining and then running the following short program.

Page 151

Listing 9.1 spam.py

1 #!/usr/local/bin/python 2 def spam(n, l=[]):

3 l.append(n) 4 return l 5

6 x = spam(42) 7 print x 8 y = spam(39) 9 print y

10 z = spam(9999, y) 11 print x, y, z

After running the program, you should see that x, y, and z all point to the same object—a list that started out as an empty list. The three variables have been spammed. You can ensure that a new list is created each time the function is called by changing the function definition for spam() to this.

Listing 9.2 Modifying spam()

1 def spam(n, il=[]):

2 flist = il[:]

3 flist.append(n) 4 return flist

Running the preceding program with these changes incorporated gives us the expected behavior, returning a new list every time. Our slicing operator ([:]) makes a copy of the empty list, if no list argument is given, and of any list that is supplied. Copies of objects, by definition, are not the same object as the original. You can check this for yourself, using Python's is operator, which returns 0 if two objects are not the same and non-0 if they are. Using the first version of our spam()

function, running x is y should yield 1, but with the modified function is should return a 0. Try

it using either the interpreter or IDLE, or modify the program to print the result of x is y.

On the other hand, if you have a program or module that creates an empty list outside of a function, and later on you create another variable that refers to an empty list, as shown in the following listing,

list1 = []

. . . list2 = []

. . .

Page 152

then the two lists are not the same object. The reason is that these assignment statements are executed when Python encounters them. Whenever a function definition is encountered, Python transforms all the instructions into a special form, called bytecodes, that is stored for later use.

Default arguments that are found in parameter lists are treated the same way that any assignment statement is treated, but it is only executed the first time the function definition is encountered. Thus, any object that is created by a default argument statement is created only once, and all other

executions of that function always refer to the single object.

For the most part, problems caused by Python's view of variables as objects will not arise. You're far more likely to cause your own problems by doing something like using i as an integer variable, assigning a string to it, and then later on attempting to use the value of i where only integers are allowed.

Object-Oriented Programming

Object-oriented programming, or OOP, is the art of programming with objects. We've already seen that objects can have state and behavior, but another property is a necessity to get useful work done.

It's called identity; all this means is that although you may have thousands of any given type of object, you can distinguish every individual instance of an object from all the others. Think of numbers: one 1 is indistinguishable from any other 1, which is why Python shares the 1 object it builds when it starts up among all the places 1 is needed in your program. But 1 is easily

distinguished from 2; 1 is not 2. ''1 is 1" is the basis for the identity property.

Although you must be able to distinguish objects from other objects, many objects share common state, common behavior, or both, to some degree. Objects that share common behavior and vary only in state may be made from a single template, called a class. You can think of a class as a cookie cutter. If you have a cookie cutter that cuts a particular shape of cookie, but no dough, the cutter is equivalent to a class. When you begin cutting out cookies, however, you are creating

instances of cookies; the cookies are the real objects. Classes are not objects, but potential objects.

Objects created using the class template are called instances, and the process of creating the real object is called instantiation. It follows, then, that when you tell Python to assign a value to a variable, what you're really doing with a line such as a = 42 is saying, "Instantiate the object 42 into a variable named a." You could think of a as a cookie with 42 chocolate chips in it. Or as a 42-egg omelet, for that matter.

Of course, cookies don't have much in the way of behavior. They get eaten, and that's really about all they do. Cats, on the other hand, have lots of behavior—mostly what they

Page 153

darn well please. Or don't please. Working with objects in computer programs is definitely easier than persuading your cat to get off the table, which is probably why computer geeks have a higher percentage of cats than the average. Cause and effect.

Now, with cookie cutters, every time you want to have a new shape, you have to start with a new piece of metal or plastic. It would be nice if you could start with an existing shape and then stretch it, push it around a little bit, and arrive at a completely new shape with less work than building it from scratch. You can't do it with cookie cutters, but you certainly can with objects. You can define a class that has behavior and state and use that class to produce instances. If you then decide you want other objects that are mostly similar, but with some differences, you can save work by starting with the original class and only adding or deleting behavior and/or state. When one class gets most of its behavior and state from another class, the new class is said to inherit from the original.

Inheritance is another primary property of objects, and it provides for the opportunity to reuse

already defined classes—classes that may have been written by you or by complete strangers. When you've imported modules into your programs before, you've been able to use behavior, written by someone else, stored in the imported module. Although this is a fairly limited form of software reuse, it is not technically inheritance. It does provide for similar savings of work; you're not duplicating or rewriting code that someone else has written. In later chapters, we will see better examples of reusability and inheritance. Some languages, by the way, elevate inheritance to such a level as to make it mandatory. Java is one of these. In Java, every class you define must inherit from some other class. In Python, like C++, classes are not required to inherit from something else;

objects don't have to have ancestors.

Two other useful properties of lesser importance are worth covering before we finish this hour.

These are encapsulation and polymorphism. They're not difficult to understand.

Encapsulation is simply the process of defining an interface to an object and then carrying through on implementation. You've used interfaces to objects before; every time you use a function, you call that function according to a defined interface. The string module atoi() function, for example, requires that you call it with at least one string argument, and it allows you to add an optional

integer argument. If you try to reverse the two arguments, atoi() complains, atoi()'s interface, then, can be defined by the two arguments and the return value, which is always an integer. Objects have interfaces, too, although they are almost always more complex than that of a function.

The second part of encapsulation is the implementation. Implementation is easy; when you call the string.atoi() function correctly according to its interface, everything that happens between the call and the return value is implementation—the stuff that does the real work. As a user of the string.atoi() function, you don't care how it does its work.

Page 154

You care only that it does work, and works correctly.

However, encapsulation is not that simple. A function normally has only one interface that's visible to the caller. Objects, or classes, are more complex; they contain state, or variables, and behaviors, or methods. Most languages that have classes provide a way to control who sees what parts of those classes. This allows programmers to completely hide some parts of a class from meddling by those they suspect of ill intent or to open up other members of a class so that other, trusted programmers can change the internal state without problems. Encapsulation, in a stricter OOP language than Python, then, is the set of choices you make in designing public and private state and data. The goal is to expose the interface and hide the implementation. It's how you package your class. The hard part is deciding what is interface and what is implementation. My personal experience is that most programmers, with the best intentions and the best research in the world, invariably turn out to have decided incorrectly when you need to get something done right now, no excuses.

In Python, all members of a class, whether variables or methods, can be accessed from outside the class by any programmer who takes the trouble to read your code. There is no way to prevent ''unauthorized entry" of a class in Python, as there is in C++, where you can unilaterally prohibit anyone from even seeing what members are there, much less changing their values. Python simply has a different approach to encapsulation than other languages. The way to expose the interface in Python is to tell people what methods and variables they should call or change. The way to hide the implementation in Python is to say in your documentation. "You don't care what's in this box over here." Encapsulation is by convention, not by proscription. The really big difference between the two styles is painfully obvious when you pass your code on to someone else, and they discover that they need a class of yours to do a job you didn't think of. If they know what they need and can read Python code, they can add what you forgot. Or maybe you did put it in, and you simply forgot to put it in your documentation. All they have to do is inspect your code, and if they need it, they have access to members of your class that in other languages would be hidden.

In the next few hours, you will gain a better understanding of how to design objects properly, which really is the essential truth of encapsulation. To design objects properly requires an understanding

Một phần của tài liệu Teach yourself python in 24 hours (Trang 219 - 414)

Tải bản đầy đủ (PDF)

(686 trang)