2008年5月7日星期三

Python: Dynamic Typing

Note: Lines beginning with ">>>" and "..." indicate input to Python (these are the default prompts of the interactive interpreter). Everything else is output from Python.

The following example demonstrates how a function can examine its own arguments and do different things depending on their types. The names of the types ("IntType" etc.) are defined in a module called "types", so we have to import them.


>>> from types import *
>>>
>>> def what (x):
... if type(x) == IntType:
... print "This is an int."
... else:
... print "This is something else."
...
>>> what(4)
This is an int.
>>>
>>> what("4")
This is something else.

This kind of dynamic typing has advantages as well as disadvantages.

The advantage is clearly that it gives a lot of freedom to the programmer and enables him to do things that wouldn't be possible otherwise. For example, you can write functions to which you can pass an integer as well as a string or a list or a dictionary or whatever else, and it will be able to transparently handle all of them in appropriate ways (or throw an exception if it cannot handle the type). You can do things like that in other languages, too, but usually you have to resort to (ab)use things like pointers, references or typecasts, which opens holes for programming errors, and it's just plain ugly.

The obvious disadvantage of dynamic typing is that the compiler cannot perform complete typechecks at compile-time, therefore it is possible that bugs creep in that are hard to find. On the other hand, Python's capabilities to handle types and runtime-errors (exceptions) in a very natural and convenient way prevents such problems most of the time. Furthermore, there are several other ways to assist in writing correct code, such as the assert statement and the doctest module for automated regression tests.

By the way, instead of importing the types module, you can compare the type of a variable with the type of a known constant. So, the above function could be rewritten like this:


>>> def what (x):
... if type(x) == type(1):
... print "This is an int."
... else:
... print "This is something else."

Beginning at Python 2.3, the names of built-in functions that create values of a specific type (int, float, str, list, tuple etc.) can be used as types, too. So the above conditional can be simply rewritten like this:


... if type(x) == int:

Here is a more complicated example. It defines a so-called dictionary (in other languages, these are called associative arrays or hashes). The keys of the dictionary are types, and the values are lambda functions that handle two arguments of that type.

By the way, you can use anything as keys in a dictionary, even complicated structured types if you want, as long as they are "immutable" (i.e. they cannot be changed, for obvious reasons).


>>> jobs = {
... IntType: lambda x, y: x**2 + y,
... StringType: lambda x, y: y.join(x.split())
... ListType: lambda x, y: [min(x), max(x), min(y), max(y)]
... }
>>>
>>> def something(a, b):
... if type(a) != type(b):
... raise "Arguments not of the same type"
... if not jobs.has_key(type(a)):
... raise "Don't know how to handle this type"
... return jobs[type(a)](a, b)
...
>>> print something(4, 5)
21
>>>
>>> print something("This is so useless.", " ... ")
This ... is ... so ... useless.
>>>
>>> print something([5, 3, 11, 9, 7], [20, 16, 8, 24, 14, 18])
[3, 11, 8, 24]
>>>
>>> print something(3, "7")
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in something
Arguments not of the same type

This is not a useful real-world example, of course, but it demonstrates some of the possibilities. Note that Python does not require a large amount of cryptic syntax. You can code powerful tasks in simple, readable statements.

没有评论: