Section 4.6. Standard Type Built-in Functions


4.6. Standard Type Built-in Functions

Along with generic operators, which we have just seen, Python also provides some BIFs that can be applied to all the basic object types: cmp(),repr(),str(),type(), and the single reverse or back quotes (``) operator, which is functionally equivalent to repr().

Table 4.4. Standard Type Built-in Functions

Function

Operation

cmp(obj1, obj2)

Compares obj1 and obj2, returns integer i where:

 

i < 0 if obj1 < obj2

 

i > 0 if obj1 > obj2

 

i == 0 if obj1 == obj2

repr(obj) or `obj`

Returns evaluatable string representation of obj

str(obj)

Returns printable string representation of obj

type(obj)

Determines type of obj and return type object


4.6.1. type()

We now formally introduce type(). In Python versions earlier than 2.2, type() is a BIF. Since that release, it has become a "factory function." We will discuss these later on in this chapter, but for now, you may continue to think of type() as a BIF. The syntax for type() is:

  type(object) type() takes an object and returns its type. The return value is a type object.   >>> type(4)                                    # int type   <type 'int'>   >>>   >>> type('Hello World!')                     # string type   <type 'string'>   >>>   >>> type(type(4))                              # type type   <type 'type'>


In the examples above, we take an integer and a string and obtain their types using the type() BIF; in order to also verify that types themselves are types, we call type() on the output of a type() call.

Note the interesting output from the type() function. It does not look like a typical Python data type, i.e., a number or string, but is something enclosed by greater-than and less-than signs. This syntax is generally a clue that what you are looking at is an object. Objects may implement a printable string representation; however, this is not always the case. In these scenarios where there is no easy way to "display" an object, Python "pretty-prints" a string representation of the object. The format is usually of the form: <object_something_or_another>. Any object displayed in this manner generally gives the object type, an object ID or location, or other pertinent information.

4.6.2. cmp()

The cmp() BIF CoMPares two objects, say, obj1 and obj2, and returns a negative number (integer) if obj1 is less than obj2, a positive number if obj1 is greater than obj2, and zero if obj1 is equal to obj2. Notice the similarity in return values as C's strcmp(). The comparison used is the one that applies for that type of object, whether it be a standard type or a user-created class; if the latter, cmp() will call the class's special __cmp__() method. More on these special methods in Chapter 13, on Python classes. Here are some samples of using the cmp() BIF with numbers and strings.

>>> a, b = -4, 12 >>> cmp(a,b) -1 >>> cmp(b,a) 1 >>> b = -4 >>> cmp(a,b) 0 >>> >>> a, b = 'abc', 'xyz' >>> cmp(a,b) -23 >>> cmp(b,a) 23 >>> b = 'abc' >>> cmp(a,b) 0


We will look at using cmp() with other objects later.

4.6.3. str() and repr() (and `` Operator)

The str() STRing and repr() REPResentation BIFs or the single back or reverse quote operator ( `` ) come in very handy if the need arises to either re-create an object through evaluation or obtain a human-readable view of the contents of objects, data values, object types, etc. To use these operations, a Python object is provided as an argument and some type of string representation of that object is returned. In the examples that follow, we take some random Python types and convert them to their string representations.

>>> str(4.53-2j) '(4.53-2j)' >>> >>> str(1) '1' >>> >>> str(2e10) '20000000000.0' >>> >>> str([0, 5, 9, 9]) '[0, 5, 9, 9]' >>> >>> repr([0, 5, 9, 9]) '[0, 5, 9, 9]' >>> >>> `[0, 5, 9, 9]` '[0, 5, 9, 9]'


Although all three are similar in nature and functionality, only repr() and `` do exactly the same thing, and using them will deliver the "official" string representation of an object that can be evaluated as a valid Python expression (using the eval() BIF). In contrast, str() has the job of delivering a "printable" string representation of an object, which may not necessarily be acceptable by eval(), but will look nice in a print statement. There is a caveat that while most return values from repr() can be evaluated, not all can:

>>> eval(`type(type))`)   File "<stdin>", line 1     eval(`type(type))`)                     ^ SyntaxError: invalid syntax


The executive summary is that repr( ) is Python-friendly while str() produces human-friendly output. However, with that said, because both types of string representations coincide so often, on many occasions all three return the exact same string.

Core Note: Why have both repr() and ``?

Occasionally in Python, you will find both an operator and a function that do exactly the same thing. One reason why both an operator and a function exist is that there are times where a function may be more useful than the operator, for example, when you are passing around executable objects like functions and where different functions may be called depending on the data item. Another example is the double-star ( ** ) and pow() BIF, which performs "x to the y power" exponentiation for x ** y or pow(x,y).


4.6.4. type() and isinstance()

Python does not support method or function overloading, so you are responsible for any "introspection" of the objects that your functions are called with. (Also see the Python FAQ 4.75.) Fortunately, we have the type() BIF to help us with just that, introduced earlier in Section 4.3.1.

What's in a name? Quite a lot, if it is the name of a type. It is often advantageous and/or necessary to base pending computation on the type of object that is received. Fortunately, Python provides a BIF just for that very purpose. type() returns the type for any Python object, not just the standard types. Using the interactive interpreter, let us take a look at some examples of what type() returns when we give it various objects.

>>> type('') <type 'str'> >>> >>> s = 'xyz' >>> type(s) <type 'str'> >>> >>> type(100) <type 'int'> >>> type(0+0j) <type 'complex'> >>> type(0L) <type 'long'> >>> type(0.0) <type 'float'> >>> >>> type([]) <type 'list'> >>> type(()) <type 'tuple'> >>> type({}) <type 'dict'> >>> type(type) <type 'type'> >>> >>> class Foo: pass            # new-style class ... >>> foo = Foo() >>> class Bar(object): pass    # new-style class ... >>> bar = Bar() >>> >>> type(Foo) <type 'classobj'> >>> type(foo) <type 'instance'> >>> type(Bar) <type 'type'> >>> type(bar) <class '__main__.Bar'>


Types and classes were unified in Python 2.2. You will see output different from that above if you are using a version of Python older than 2.2:

>>> type('') <type 'string'> >>> type(0L) <type 'long int'> >>> type({}) <type 'dictionary'> >>> type(type) <type 'builtin_function_or_method'> >>> >>> type(Foo)          # assumes Foo created as in above <type 'class'> >>> type(foo)          # assumes foo instantiated also <type 'instance'>


In addition to type(), there is another useful BIF called isinstance(). We cover it more formally in Chapter 13 (Object-Oriented Programming), but here we can introduce it to show you how you can use it to help determine the type of an object.

Example

We present a script in Example 4.1 that shows how we can use isinstance() and type() in a runtime environment. We follow with a discussion of the use of type() and how we migrated to using isinstance() instead for the bulk of the work in this example.

Example 4.1. Checking the Type (typechk.py)

The function displayNumType() takes a numeric argument and uses the type() built-in to indicate its type (or "not a number," if that is the case).

  1 #!/usr/bin/env python   2   3 def displayNumType(num):   4     print num, 'is',   5     if isinstance(num, (int, long, float, complex)):   6        print 'a number of type:', type(num).__name__   7     else:   8       print 'not a number at all!!'   9   10 displayNumType(-69)   11 displayNumType(9999999999999999999999L)   12 displayNumType(98.6)   13 displayNumType(-5.2+1.9j)   14 displayNumType('xxx')

Running typechk.py, we get the following output:

-69 is a number of type: int 9999999999999999999999 is a number of type: long 98.6 is a number of type: float (-5.2+1.9j) is a number of type: complex xxx is not a number at all!!


The Evolution of This Example
Original

The same function was defined quite differently in the first edition of this book:

def displayNumType(num):     print num, "is",     if type(num) == type(0):         print 'an integer'     elif type(num) == type(0L):         print 'a long'     elif type(num) == type(0.0):         print 'a float'     elif type(num) == type(0+0j):         print 'a complex number'     else:         print 'not a number at all!!'


As Python evolved in its slow and simple way, so must we. Take a look at our original conditional expression:

if type(num) == type(0)...


Reducing Number of Function Calls

If we take a closer look at our code, we see a pair of calls to type(). As you know, we pay a small price each time a function is called, so if we can reduce that number, it will help with performance.

An alternative to comparing an object's type with a known object's type (as we did above and in the example below) is to utilize the types module, which we briefly mentioned earlier in the chapter. If we do that, then we can use the type object there without having to "calculate it." We can then change our code to only having one call to the type() function:

>>> import types >>> if type(num) == types.IntType...


Object Value Comparison versus Object Identity Comparison

We discussed object value comparison versus object identity comparison earlier in this chapter, and if you realize one key fact, then it will become clear that our code is still not optimal in terms of performance. During runtime, there is always only one type object that represents an integer. In other words, type(0), type(42), type(-100) are always the same object: <type 'int'> (and this is also the same object as types.IntType).

If they are always the same object, then why do we have to compare their values since we already know they will be the same? We are "wasting time" extracting the values of both objects and comparing them if they are the same object, and it would be more optimal to just compare the objects themselves. Thus we have a migration of the code above to the following:

if type(num) is types.IntType... # or type(0)


Does that make sense? Object value comparison via the equal sign requires a comparison of their values, but we can bypass this check if the objects themselves are the same. If the objects are different, then we do not even need to check because that means the original variable must be of a different type (since there is only one object of each type). One call like this may not make a difference, but if there are many similar lines of code throughout your application, then it starts to add up.

Reduce the Number of Lookups

This is a minor improvement to the previous example and really only makes a difference if your application performs makes many type comparisons like our example. To actually get the integer type object, the interpreter has to look up the types name first, and then within that module's dictionary, find IntType. By using from-import, you can take away one lookup:

from types import IntType if type(num) is IntType ...


Convenience and Style

The unification of types and classes in 2.2 has resulted in the expected rise in the use of the isinstance() BIF. We formally introduce isinstance() in Chapter 13 (Object-Oriented Programming), but we will give you a quick preview now.

This Boolean function takes an object and one or more type objects and returns true if the object in question is an instance of one of the type objects. Since types and classes are now the same, int is now a type (object) and a class. We can use isinstance() with the built-in types to make our if statement more convenient and readable:

if isinstance(num, int)...


Using isinstance() along with type objects is now also the accepted style of usage when introspecting objects' types, which is how we finally arrive at our updated typechk.py application above. We also get the added bonus of isinstance() accepting a tuple of type objects to check against our object with instead of having an if-elif-else if we were to use only type().

4.6.5. Python Type Operator and BIF Summary

A summary of operators and BIFs common to all basic Python types is given in Table 4.5. The progressing shaded groups indicate hierarchical precedence from highest-to-lowest order. Elements grouped with similar shading all have equal priority. Note that these (and most Python) operators are available as functions via the operator module.

Table 4.5. Standard Type Operators and Built-in Functions

Operator/Function

Description

Result[a]

String representation

  

``

String representation

str

Built-in functions

  

cmp(obj1, obj2)

Compares two objects

int

repr(obj)

String representation

str

str(obj)

String representation

str

type(obj)

Determines object type

type

Value comparisons

  

<

Less than

bool

>

Greater than

bool

<=

Less than or equal to

bool

>=

Greater than or equal to

bool

==

Equal to

bool

!=

Not equal to

bool

<>

Not equal to

bool

Object comparisons

  

is

The same as

bool

is not

Not the same as

bool

Boolean operators

  

not

Logical negation

bool

and

Logical conjunction

bool

or

Logical disjunction

bool


[a] Boolean comparisons return either TRue or False.



Core Python Programming
Core Python Programming (2nd Edition)
ISBN: 0132269937
EAN: 2147483647
Year: 2004
Pages: 334
Authors: Wesley J Chun

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net