Python

March 16, 2017 | Author: Edith Soto | Category: N/A
Share Embed Donate


Short Description

Download Python...

Description

www.it-ebooks.info

Learning Python

www.it-ebooks.info

www.it-ebooks.info

FOURTH EDITION

Learning Python

Beijing • Cambridge • Farnham • Köln • Sebastopol • Taipei • Tokyo

www.it-ebooks.info

Learning Python, Fourth Edition

Editor: Production Editor: Copyeditor: Production Services: Printing History:

Indexer: Cover Designer: Interior Designer: Illustrator:

www.it-ebooks.info

www.it-ebooks.info

www.it-ebooks.info

Table of Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xxxi

Part I. Getting Started 1. A Python Q&A Session . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

vii

www.it-ebooks.info

2. How Python Runs Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3. How You Run Programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35

viii | Table of Contents

www.it-ebooks.info

Part II. Types and Operations 4. Introducing Python Object Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Table of Contents | ix

www.it-ebooks.info

5. Numeric Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

6. The Dynamic Typing Interlude . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143

x | Table of Contents

www.it-ebooks.info

7. Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

8. Lists and Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197

Table of Contents | xi

www.it-ebooks.info

9. Tuples, Files, and Everything Else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

Part III. Statements and Syntax 10. Introducing Python Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

xii | Table of Contents

www.it-ebooks.info

11. Assignments, Expressions, and Prints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279

12. if Tests and Syntax Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311

Table of Contents | xiii

www.it-ebooks.info

13. while and for Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327

14. Iterations and Comprehensions, Part 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351

xiv | Table of Contents

www.it-ebooks.info

15. The Documentation Interlude . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375

Part IV. Functions 16. Function Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395

Table of Contents | xv

www.it-ebooks.info

17. Scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407

18. Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435

xvi | Table of Contents

www.it-ebooks.info

19. Advanced Function Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463

20. Iterations and Comprehensions, Part 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485

Table of Contents | xvii

www.it-ebooks.info

Part V. Modules 21. Modules: The Big Picture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529

22. Module Coding Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 543

xviii | Table of Contents

www.it-ebooks.info

23. Module Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 561

24. Advanced Module Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583

Table of Contents | xix

www.it-ebooks.info

Part VI. Classes and OOP 25. OOP: The Big Picture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 611

26. Class Coding Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 625

xx | Table of Contents

www.it-ebooks.info

27. A More Realistic Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 643

28. Class Coding Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681

Table of Contents | xxi

www.it-ebooks.info

29. Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705

xxii | Table of Contents

www.it-ebooks.info

30. Designing with Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737

31. Advanced Class Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773

Table of Contents | xxiii

www.it-ebooks.info

Part VII. Exceptions and Tools 32. Exception Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 825

33. Exception Coding Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 835

xxiv | Table of Contents

www.it-ebooks.info

34. Exception Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 857

35. Designing with Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873

Table of Contents | xxv

www.it-ebooks.info

Part VIII. Advanced Topics 36. Unicode and Byte Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 895

xxvi | Table of Contents

www.it-ebooks.info

37. Managed Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941

Table of Contents | xxvii

www.it-ebooks.info

38. Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 983

xxviii | Table of Contents

www.it-ebooks.info

39. Metaclasses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051

Part IX. Appendixes A. Installation and Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089 B. Solutions to End-of-Part Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139

Table of Contents | xxix

www.it-ebooks.info

www.it-ebooks.info

Preface

xxxi

www.it-ebooks.info

About This Fourth Edition

print

Coverage for Both 3.0 and 2.6

print nonlocal

xxxii | Preface

print format

www.it-ebooks.info

format with

New Chapters

Preface | xxxiii

www.it-ebooks.info

Changes to Existing Material

map

zip __contains__ __bool__

format exec

xxxiv | Preface

__index__

www.it-ebooks.info

Specific Language Extensions in 2.6 and 3.0

list

Extension

Covered in chapter(s)

The print function in 3.0

11

The nonlocal x,y statement in 3.0

17

The str.format method in 2.6 and 3.0

7

String types in 3.0: str for Unicode text, bytes for binary data

7, 36

Text and binary file distinctions in 3.0

9, 36

Class decorators in 2.6 and 3.0: @private('age')

31, 38

New iterators in 3.0: range, map, zip

14, 20

Dictionary views in 3.0: D.keys, D.values, D.items

8, 14

Division operators in 3.0: remainders, / and //

5

Set literals in 3.0: {a, b, c}

5

Set comprehensions in 3.0: {x**2 for x in seq}

4, 5, 14, 20

Dictionary comprehensions in 3.0: {x: x**2 for x in seq}

4, 8, 14, 20

Binary digit-string support in 2.6 and 3.0: 0b0101, bin(I)

5

The fraction number type in 2.6 and 3.0: Fraction(1, 3)

5

Function annotations in 3.0: def f(a:99, b:str)->int

19

Keyword-only arguments in 3.0: def f(a, *b, c, **d)

18, 20

Extended sequence unpacking in 3.0: a, *b = seq

11, 13

Relative import syntax for packages enabled in 3.0: from .

23

Context managers enabled in 2.6 and 3.0: with/as

33, 35

Exception syntax changes in 3.0: raise, except/as, superclass

33, 34

Preface | xxxv

www.it-ebooks.info

Extension

Covered in chapter(s)

Exception chaining in 3.0: raise e2 from e1

33

Reserved word changes in 2.6 and 3.0

11

New-style class cutover in 3.0

31

Property decorators in 2.6 and 3.0: @property

37

Descriptor use in 2.6 and 3.0

31, 38

Metaclass use in 2.6 and 3.0

31, 39

Abstract base classes support in 2.6 and 3.0

28

Specific Language Removals in 3.0

Removed

Replacement

Covered in chapter(s)

reload(M)

imp.reload(M) (or exec)

3, 22

apply(f, ps, ks)

f(*ps, **ks)

18

`X`

repr(X)

5

X Y

X != Y

5

long

int

5

9999L

9999

5

D.has_key(K)

K in D (or D.get(key) != None)

8

raw_input

input

3, 10

old input

eval(input())

3

xrange

range

14

file

open (and io module classes)

9

X.next

X.__next__, called by next(X)

14, 20, 29

X.__getslice__

X.__getitem__ passed a slice object

7, 29

X.__setslice__

X.__setitem__ passed a slice object

7, 29

reduce

functools.reduce (or loop code)

14, 19

execfile(filename)

exec(open(filename).read())

3

exec open(filename)

exec(open(filename).read())

3

0777

0o777

5

print x, y

print(x, y)

11

xxxvi | Preface

www.it-ebooks.info

Removed

Replacement

Covered in chapter(s)

print >> F, x, y

print(x, y, file=F)

11

print x, y,

print(x, y, end=' ')

11

u'ccc'

'ccc'

7, 36

'bbb' for byte strings

b'bbb'

7, 9, 36

raise E, V

raise E(V)

32, 33, 34

except E, X:

except E as X:

32, 33, 34

def f((a, b)):

def f(x): (a, b) = x

11, 18, 20

file.xreadlines

for line in file: (or X=iter(file))

13, 14

D.keys(), etc. as lists

list(D.keys()) (dictionary views)

8, 14

map(), range(), etc. as lists

list(map()), list(range()) (built-ins)

14

map(None, ...)

zip (or manual code to pad results)

13, 20

X=D.keys(); X.sort()

sorted(D) (or list(D.keys()))

4, 8, 14

cmp(x, y)

(x > y) - (x < y)

29

X.__cmp__(y)

__lt__, __gt__, __eq__, etc.

29

X.__nonzero__

X.__bool__

29

X.__hex__, X.__oct__

X._index__

29

Sort comparison functions

Use key=transform or reverse=True

8

Dictionary , =

Compare sorted(D.items()) (or loop code)

8, 9

types.ListType

list (types is for nonbuilt-in names only)

9

__metaclass__ = M

class C(metaclass=M):

28, 31, 39

__builtin__

builtins (renamed)

17

Tkinter

tkinter (renamed)

18, 19, 24, 29, 30

sys.exc_type, exc_value

sys.exc_info()[0], [1]

34, 35

function.func_code

function.__code__

19, 38

__getattr__ run by built-ins

Redefine __X__ methods in wrapper classes

30, 37, 38

-t, –tt command-line switches

Inconsistent tabs/spaces use is always an error

10, 12

from ... *, within a function

May only appear at the top level of a file

22

import mod, in same package

from . import mod, package-relative form

23

class MyException:

class MyException(Exception):

34

exceptions module

Built-in scope, library manual

34

thread, Queue modules

_thread, queue (both renamed)

17

anydbm module

dbm (renamed)

27

cPickle module

_pickle (renamed, used automatically)

9

os.popen2/3/4

subprocess.Popen (os.popen retained)

14

String-based exceptions

Class-based exceptions (also required in 2.6)

32, 33, 34

Preface | xxxvii

www.it-ebooks.info

Removed

Replacement

Covered in chapter(s)

String module functions

String object methods

7

Unbound methods

Functions (staticmethod to call via instance)

30, 31

Mixed type comparisons, sorts

Nonnumeric mixed type comparisons are errors

5, 9

About The Third Edition

The Third Edition’s Python Language Changes

xxxviii | Preface

www.it-ebooks.info

B if A else C with as try except finally

sorted sum any all enumerate

distutils unittest

doctest True

False

sys.exc_info apply

reduce

apply

The Third Edition’s Python Training Changes

for

Preface | xxxix

www.it-ebooks.info

The Third Edition’s Structural Changes

The Third Edition’s Scope Changes

xl | Preface

www.it-ebooks.info

About This Book

This Book’s Prerequisites

This Book’s Scope and Other Books

Preface | xli

www.it-ebooks.info

xlii | Preface

www.it-ebooks.info

This Book’s Style and Structure

Preface | xliii

www.it-ebooks.info

__name__

xliv | Preface

www.it-ebooks.info

Book Updates ^H^H^H

About the Programs in This Book

Using Code Examples

Preface | xlv

www.it-ebooks.info

Font Conventions

Constant width

Constant width bold

Constant width italic

xlvi | Preface

www.it-ebooks.info

% C:\Python30> % >>> ...

#

Safari® Books Online

How to Contact Us

Preface | xlvii

www.it-ebooks.info

Acknowledgments

xlviii | Preface

www.it-ebooks.info

Preface | xlix

www.it-ebooks.info

www.it-ebooks.info

PART I

Getting Started

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 1

A Python Q&A Session

Why Do People Use Python?

3

www.it-ebooks.info

Software Quality

4 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

Developer Productivity

Is Python a “Scripting Language”?

import this

Is Python a “Scripting Language”? | 5

www.it-ebooks.info

6 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

OK, but What’s the Downside?

Who Uses Python Today?

Who Uses Python Today? | 7

www.it-ebooks.info

8 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

What Can I Do with Python?

Systems Programming

GUIs

What Can I Do with Python? | 9

www.it-ebooks.info

Internet Scripting

Component Integration

10 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

Database Programming

pickle

Rapid Prototyping

Numeric and Scientific Programming

What Can I Do with Python? | 11

www.it-ebooks.info

Gaming, Images, Serial Ports, XML, Robots, and More

xml

How Is Python Supported?

12 | Chapter 1: A Python Q&A Session

xmlrpclib

www.it-ebooks.info

What Are Python’s Technical Strengths?

It’s Object-Oriented

It’s Free

What Are Python’s Technical Strengths? | 13

www.it-ebooks.info

It’s Portable

14 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

It’s Powerful

What Are Python’s Technical Strengths? | 15

www.it-ebooks.info

It’s Mixable

It’s Easy to Use

16 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

It’s Easy to Learn

It’s Named After Monty Python

How Does Python Stack Up to Language X?

How Does Python Stack Up to Language X? | 17

www.it-ebooks.info

Chapter Summary

18 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

Test Your Knowledge: Quiz

import this

Test Your Knowledge: Answers

Test Your Knowledge: Answers | 19

www.it-ebooks.info

import this

Python Is Engineering, Not Art

20 | Chapter 1: A Python Q&A Session

www.it-ebooks.info

Test Your Knowledge: Answers | 21

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 2

How Python Runs Programs

Introducing the Python Interpreter

23

www.it-ebooks.info

Program Execution

The Programmer’s View

print('hello world') print(2 ** 100)

print

print

24 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

Program Execution | 25

www.it-ebooks.info

print hello world 1267650600228229401496703205376

C:\temp> python script0.py hello world 1267650600228229401496703205376

Python’s View

Byte code compilation

26 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

The Python Virtual Machine (PVM)

Performance implications

Program Execution | 27

www.it-ebooks.info

Development implications

eval

exec

28 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

Execution Model Variations

Python Implementation Alternatives

CPython

Jython

Execution Model Variations | 29

www.it-ebooks.info

IronPython

Execution Optimization Tools

The Psyco just-in-time compiler

30 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

The Shedskin C++ translator

Execution Model Variations | 31

www.it-ebooks.info

Frozen Binaries

32 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

Other Execution Options

Future Possibilities?

Execution Model Variations | 33

www.it-ebooks.info

Chapter Summary

Test Your Knowledge: Quiz 1.#code#that#executes#other#code 2.#what#you#actually#write#for#the#program 3.#Is#what#python#compiles 4.#Python#Virtual#Machine,#the#engine# that#interprets#

Test Your Knowledge: Answers the#byte#code.#

5.#IronPython,#JPython#

34 | Chapter 2: How Python Runs Programs

www.it-ebooks.info

CHAPTER 3

How You Run Programs

exec

The Interactive Prompt

python

35

www.it-ebooks.info

% python Python 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] ... Type "help", "copyright", "credits" or "license" for more information. >>>

python

PATH /usr/local/bin/python C:\Python30\python

/usr/bin/python

C:\misc> c:\python30\python Python 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] ... Type "help", "copyright", "credits" or "license" for more information. >>>

cd c:\python30 C:\misc> cd C:\Python30 C:\Python30> python Python 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] ... Type "help", "copyright", "credits" or "license" for more information. >>>

python

36 | Chapter 3: How You Run Programs

www.it-ebooks.info

Running Code Interactively >>> >>> print

print

% python >>> print('Hello world!') Hello world! >>> print(2 ** 8) 256

print >>> 2 ** 8

>>> lumberjack = 'okay' >>> lumberjack 'okay' >>> 2 ** 8 256 >>> %

lumberjack

2 ** 8

print

The Interactive Prompt | 37

www.it-ebooks.info

print

Why the Interactive Prompt?

Experimenting

'Spam!' * 8

>>> 'Spam!' * 8 'Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!'

*

38 | Chapter 3: How You Run Programs

>>>

www.it-ebooks.info

>>> X Traceback (most recent call last): File "", line 1, in NameError: name 'X' is not defined

Testing

>>> import os >>> os.getcwd() 'c:\\Python30'

Using the Interactive Prompt

os.system

The Interactive Prompt | 39

www.it-ebooks.info

print print print print

...

...

>>>

40 | Chapter 3: How You Run Programs

... sys

>>>

www.it-ebooks.info

Entering multiline statements

for

if

>>> for x in 'spam': ... print(x) ...

>>> for x in 'spam': ... print(x) ... print('done') File "", line 3 print('done') ^ SyntaxError: invalid syntax

System Command Lines and Files

System Command Lines and Files | 41

www.it-ebooks.info

python

A First Script

import sys print(sys.platform) print(2 ** 100) x = 'Spam!' print(x * 8)

print x sys.platform sys

42 | Chapter 3: How You Run Programs

www.it-ebooks.info

# #

Running Files with Command Lines python % python script1.py win32 1267650600228229401496703205376 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!

PATH print

% python script1.py > saveit.txt

System Command Lines and Files | 43

www.it-ebooks.info

C:\Python30> python script1.py win32 1267650600228229401496703205376 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!

PATH D:\temp> C:\python30\python script1.py win32 1267650600228229401496703205376 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!

D:\temp> script1.py

D:\other> python c:\code\otherscript.py

PATH D:\other> C:\Python30\python c:\code\otherscript.py

Using Command Lines and Files

44 | Chapter 3: How You Run Programs

www.it-ebooks.info

python script1.py

python script1

import import script1

python d:\tests\spam.py import spam print print print print

System Command Lines and Files | 45

www.it-ebooks.info

Unix Executable Scripts (#!)

#!

chmod +x file.py

#!/usr/local/bin/python print('The Bright Side ' + 'of Life...')

#

chmod +x brian % brian The Bright Side of Life...

python

brian.py #!

python brian.py #!

46 | Chapter 3: How You Run Programs

www.it-ebooks.info

C:\misc> python brian The Bright Side of Life...

#!

The Unix env Lookup Trick #!/usr/bin/env python ...script goes here...

PATH

PATH

Clicking File Icons

#!

Clicking Icons on Windows

Clicking File Icons | 47

www.it-ebooks.info

import sys print(sys.platform) print(2 ** 100) x = 'Spam!' print(x * 8)

C:\misc> c:\python30\python script1.py win32 1267650600228229401496703205376

48 | Chapter 3: How You Run Programs

www.it-ebooks.info

The input Trick

input raw_input import sys print(sys.platform) print(2 ** 100) x = 'Spam!' print(x * 8) input()

input

Clicking File Icons | 49

www.it-ebooks.info

input print input input('Press Enter to exit') nextinput = input() python spam.py < input.txt

print

input

raw_input()

input() input

input

raw_input input

Other Icon-Click Limitations input

input

50 | Chapter 3: How You Run Programs

eval(input())

www.it-ebooks.info

try

Module Imports and Reloads

import

input C:\misc> c:\python30\python >>> import script1 win32 1267650600228229401496703205376 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!

Module Imports and Reloads | 51

www.it-ebooks.info

>>> import script1 >>> import script1

reload imp >>> from imp import reload >>> reload(script1) win32 65536 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam! >>>

from reload

print 2 ** 16 import

reload

reload reload import reload

import reload reload

52 | Chapter 3: How You Run Programs

www.it-ebooks.info

reload imp imp.reload(M)

from imp import reload import from

import imp reload(M)

reload reload

reload reload

from from import reload

reload

import

module.attribute

The Grander Module Story: Attributes

import

from

reload

title = "The Meaning of Life"

title

Module Imports and Reloads | 53

www.it-ebooks.info

title import % python >>> import myfile >>> print(myfile.title) The Meaning of Life

object.attribute title

myfile

myfile.title from % python >>> from myfile import title >>> print(title) The Meaning of Life

from

import from

title import

myfile.title

from

title

a = 'dead' b = 'parrot' c = 'sketch' print(a, b, c)

print

import

from .py

54 | Chapter 3: How You Run Programs

import

www.it-ebooks.info

% python threenames.py dead parrot sketch

import

from

import from

% python >>> import threenames dead parrot sketch >>> >>> threenames.b, threenames.c ('parrot', 'sketch') >>> >>> from threenames import a, b, c >>> b, c ('parrot', 'sketch')

dir

>>> dir(threenames) ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'a', 'b', 'c']

dir

a b

c

dir

Modules and namespaces

import

Module Imports and Reloads | 55

www.it-ebooks.info

from from

import

from

from from

import and reload Usage Notes import

reload

reload reload

reload from

reload

56 | Chapter 3: How You Run Programs

www.it-ebooks.info

Using exec to Run Module Files exec(open('module.py').read()) exec C:\misc> c:\python30\python >>> exec(open('script1.py').read()) win32 65536 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam! ...change script1.py in a text edit window... >>> exec(open('script1.py').read()) win32 4294967296 Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam!

exec exec exec

exec

exec

from x

exec

>>> x = 999 >>> exec(open('script1.py').read()) ...same outout... >>> x 'Spam!'

sys.path PYTHONPATH

sys PYTHONPATH

Using exec to Run Module Files | 57

www.it-ebooks.info

import

execfile('module.py') exec(open('module.py')) exec(open('module.py').read())

exec

exec

The IDLE User Interface exec

IDLE Basics

58 | Chapter 3: How You Run Programs

www.it-ebooks.info

>>>

yum tkinter idle

The IDLE User Interface | 59

www.it-ebooks.info

Using IDLE

60 | Chapter 3: How You Run Programs

www.it-ebooks.info

The IDLE User Interface | 61

www.it-ebooks.info

-n idle.py -n cd

import

Advanced IDLE Tools

62 | Chapter 3: How You Run Programs

www.it-ebooks.info

Other IDEs

Other IDEs | 63

www.it-ebooks.info

Other Launch Options

Embedding Calls

64 | Chapter 3: How You Run Programs

www.it-ebooks.info

#include ... Py_Initialize(); PyRun_SimpleString("x = 'brave ' + 'sir robin'");

Frozen Binary Executables

Text Editor Launch Options

Other Launch Options | 65

www.it-ebooks.info

Still Other Launch Options

os.popen os.system

Future Possibilities?

Which Option Should I Use?

66 | Chapter 3: How You Run Programs

www.it-ebooks.info

Debugging Python Code

print print print

#

print

print

Which Option Should I Use? | 67

www.it-ebooks.info

Chapter Summary exec

Test Your Knowledge: Quiz

unix%open%terminal,%use%IDE3GUI %type%python in%the%directory%you%have%python

icon%clicking,%within%the%text%file%you are%using,%by%using%IDE,%command modules%run%only%once,%even%if% changes%are%made%

open/new%file%then%run3>run%module some%tools%are%not%available a%module%is%a%namespace,%a%package 68 | Chapter 3: How You Run Programs

of%variables.%

www.it-ebooks.info

Test Your Knowledge: Answers python

PATH cd python

C:\Python30\python

exec #!

input

Test Your Knowledge: Answers | 69

www.it-ebooks.info

Test Your Knowledge: Part I Exercises

>>>

"Hello

World!" cd PATH PATH

print('Hello module world!')

python module1.py

exec

>>>

70 | Chapter 3: How You Run Programs

www.it-ebooks.info

#! #!

2 ** 500

1 / 0

L = [1, 2] L.append(L) L

Test Your Knowledge: Part I Exercises | 71

www.it-ebooks.info

help

72 | Chapter 3: How You Run Programs

www.it-ebooks.info

PART II

Types and Operations

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 4

Introducing Python Object Types

75

www.it-ebooks.info

Why Use Built-in Types?

76 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

Python’s Core Data Types

Object type

Example literals/creation

Numbers

1234, 3.1415, 3+4j, Decimal, Fraction

Strings

'spam', "guido's", b'a\x01c'

Lists

[1, [2, 'three'], 4]

Dictionaries

{'food': 'spam', 'taste': 'yum'}

Tuples

(1, 'spam', 4, 'U')

Files

myfile = open('eggs', 'r')

Sets

set('abc'), {'a', 'b', 'c'}

Other core types

Booleans, types, None

Program unit types

Functions, modules, classes (Part IV, Part V, Part VI)

Implementation-related types

Compiled code, stack tracebacks (Part IV, Part VII)

def class import

lambda

>>> 'spam'

const

Why Use Built-in Types? | 77

www.it-ebooks.info

Numbers

+

78 | Chapter 4: Introducing Python Object Types

*

**

www.it-ebooks.info

>>> 123 + 222 345 >>> 1.5 * 4 6.0 >>> 2 ** 100 1267650600228229401496703205376

>>> len(str(2 ** 1000000)) 301030

>>> 3.1415 * 2 6.2830000000000004 >>> print(3.1415 * 2) 6.283

repr

str print

>>> import math >>> math.pi 3.1415926535897931 >>> math.sqrt(85) 9.2195444572928871

math random >>> import random >>> random.random() 0.59268735266273953 >>> random.choice([1, 2, 3, 4]) 1

Numbers | 79

www.it-ebooks.info

Strings

Sequence Operations len >>> >>> 4 >>> 'S' >>> 'p'

S = 'Spam' len(S) S[0] S[1]

S

>>> S[-1] 'm' >>> S[-2] 'a'

80 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> S[-1] 'm' >>> S[len(S)-1] 'm'

>>> S 'Spam' >>> S[1:3] 'pa'

X[I:J] X

I

J

S

>>> S[1:] 'pam' >>> S 'Spam' >>> S[0:3] 'Spa' >>> S[:3] 'Spa' >>> S[:-1] 'Spa' >>> S[:] 'Spam'

>>> S Spam' >>> S + 'xyz'

Strings | 81

www.it-ebooks.info

'Spamxyz' >>> S 'Spam' >>> S * 8 'SpamSpamSpamSpamSpamSpamSpamSpam'

+

+

Immutability

>>> S 'Spam' >>> S[0] = 'z' ...error text omitted... TypeError: 'str' object does not support item assignment >>> S = 'z' + S[1:] >>> S 'zpam'

Type-Specific Methods

82 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

find −1

replace

>>> S.find('pa') 1 >>> S 'Spam' >>> S.replace('pa', 'XYZ') 'SXYZm' >>> S 'Spam'

>>> line = 'aaa,bbb,ccccc,dd' >>> line.split(',') ['aaa', 'bbb', 'ccccc', 'dd'] >>> S = 'spam' >>> S.upper() 'SPAM' >>> S.isalpha() True >>> line = 'aaa,bbb,ccccc,dd\n' >>> line = line.rstrip() >>> line 'aaa,bbb,ccccc,dd'

>>> '%s, eggs, and %s' % ('spam', 'SPAM!') 'spam, eggs, and SPAM!' >>> '{0}, eggs, and {1}'.format('spam', 'SPAM!') 'spam, eggs, and SPAM!'

len(X) X[0]

aString.upper()

Strings | 83

www.it-ebooks.info

Getting Help dir S >>> dir(S) ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum','isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

dir help >>> help(S.replace) Help on built-in function replace: replace(...) S.replace (old, new[, count]) -> str Return a copy of S with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.

help

help(S)

84 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

dir

help

Other Ways to Code Strings

>>> S = 'A\nB\tC' >>> len(S) 5 >>> ord('\n') 10 >>> S = 'A\0B\0C' >>> len(S) 5

>>> msg = """ aaaaaaaaaaaaa bbb'''bbbbbbbbbb""bbbbbbb'bbbb cccccccccccccc""" >>> msg '\naaaaaaaaaaaaa\nbbb\'\'\'bbbbbbbbbb""bbbbbbb\'bbbb\ncccccccccccccc'

r str bytes str str bytes

Pattern Matching

Strings | 85

www.it-ebooks.info

re >>> import re >>> match = re.match('Hello[ \t]*(.*)world', 'Hello >>> match.group(1) 'Python '

Python world')

>>> match = re.match('/(.*)/(.*)/(.*)', '/usr/home/lumberjack') >>> match.groups() ('usr', 'home', 'lumberjack')

Lists

Sequence Operations

>>> L = [123, 'spam', 1.23] >>> len(L) 3

>>> L[0] 123 >>> L[:-1] [123, 'spam'] >>> L + [4, 5, 6] [123, 'spam', 1.23, 4, 5, 6]

86 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> L [123, 'spam', 1.23]

Type-Specific Operations

>>> L.append('NI') >>> L [123, 'spam', 1.23, 'NI'] >>> L.pop(2) 1.23 >>> L [123, 'spam', 'NI']

append pop

del

insert

remove

>>> M = ['bb', 'aa', 'cc'] >>> M.sort() >>> M ['aa', 'bb', 'cc'] >>> M.reverse() >>> M ['cc', 'bb', 'aa']

sort reverse

Bounds Checking

>>> L [123, 'spam', 'NI'] >>> L[99] ...error text omitted... IndexError: list index out of range

Lists | 87

www.it-ebooks.info

>>> L[99] = 1 ...error text omitted... IndexError: list assignment index out of range

append

Nesting

>>> M = [[1, 2, [4, 5, [7, 8, >>> M [[1, 2, 3], [4,

3], 6], 9]] 5, 6], [7, 8, 9]]

>>> M[1] [4, 5, 6] >>> M[1][2] 6

Comprehensions

88 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> col2 = [row[1] for row in M] >>> col2 [2, 5, 8] >>> M [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

row row[1]

M

>>> [row[1] + 1 for row in M] [3, 6, 9] >>> [row[1] for row in M if row[1] % 2 == 0] [2, 8]

if

%

>>> diag = [M[i][i] for i in [0, 1, 2]] >>> diag [1, 5, 9] >>> doubles = [c * 2 for c in 'spam'] >>> doubles ['ss', 'pp', 'aa', 'mm']

map

filter

sum

Lists | 89

www.it-ebooks.info

>>> G = (sum(row) for row in M) >>> next(G) 6 >>> next(G) 15

map list >>> list(map(sum, M)) [6, 15, 24]

>>> {sum(row) for row in M} {24, 6, 15} >>> {i : sum(M[i]) for i in range(3)} {0: 6, 1: 15, 2: 24}

>>> [ord(x) for x in 'spaam'] [115, 112, 97, 97, 109] >>> {ord(x) for x in 'spaam'} {112, 97, 115, 109} >>> {x: ord(x) for x in 'spaam'} {'a': 97, 'p': 112, 's': 115, 'm': 109}

Dictionaries

Mapping Operations

>>> D = {'food': 'Spam', 'quantity': 4, 'color': 'pink'}

90 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> D['food'] 'Spam'

'

'

>>> D['quantity'] += 1 ' ' >>> D {'food': 'Spam', 'color': 'pink', 'quantity': 5}

>>> >>> >>> >>>

D = {} D['name'] = 'Bob' D['job'] = 'dev' D['age'] = 40

>>> D {'age': 40, 'job': 'dev', 'name': 'Bob'} >>> print(D['name']) Bob

Nesting Revisited

>>> rec = {'name': {'first': 'Bob', 'last': 'Smith'}, 'job': ['dev', 'mgr'], 'age': 40.5}

Dictionaries | 91

www.it-ebooks.info

>>> rec['name'] {'last': 'Smith', 'first': 'Bob'}

'

'

>>> rec['name']['last'] 'Smith' >>> rec['job'] ['dev', 'mgr'] >>> rec['job'][-1] 'mgr'

'

'

>>> rec['job'].append('janitor') ' >>> rec {'age': 40.5, 'job': ['dev', 'mgr', 'janitor'], 'name': {'last': 'Smith', 'first': 'Bob'}}

>>> rec = 0

rec pickle

92 | Chapter 4: Introducing Python Object Types

shelve

www.it-ebooks.info

Sorting Keys: for Loops

>>> D = {'a': 1, 'b': 2, 'c': 3} >>> D {'a': 1, 'c': 3, 'b': 2}

keys sort

for for

>>> Ks = list(D.keys()) >>> Ks ['a', 'c', 'b'] >>> Ks.sort() >>> Ks ['a', 'b', 'c'] >>> for key in Ks: print(key, '=>', D[key]) a => 1 b => 2 c => 3

sorted sorted >>> D {'a': 1, 'c': 3, 'b': 2} >>> for key in sorted(D): print(key, '=>', D[key]) a => 1 b => 2 c => 3

for for

Dictionaries | 93

www.it-ebooks.info

key

for

while for

>>> for c in 'spam': print(c.upper()) S P A M

while >>> x = 4 >>> while x > 0: print('spam!' * x) x -= 1 spam!spam!spam!spam! spam!spam!spam! spam!spam! spam!

Iteration and Optimization for

iter

next

sorted keys next

94 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> squares = [x ** 2 for x in [1, 2, 3, 4, 5]] >>> squares [1, 4, 9, 16, 25]

for >>> squares = [] >>> for x in [1, 2, 3, 4, 5]: squares.append(x ** 2) >>> squares [1, 4, 9, 16, 25]

map filter

for

time

timeit

profile

Missing Keys: if Tests >>> D {'a': 1, 'c': 3, 'b': 2} >>> D['e'] = 99 >>> D {'a': 1, 'c': 3, 'b': 2, 'e': 99} >>> D['f'] ...error text omitted... KeyError: 'f'

in

Dictionaries | 95

www.it-ebooks.info

if for

if

>>> 'f' in D False >>> if not 'f' in D: print('missing') missing

if if if else

elif

get

has_key try if else if

>>> >>> 0 >>> >>> 0

value = D.get('x', 0) value value = D['x'] if 'x' in D else 0 value

Tuples

>>> T = (1, 2, 3, 4) >>> len(T) 4 >> T + (5, 6) (1, 2, 3, 4, 5, 6) >>> T[0] 1

96 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> T.index(4) 3 >>> T.count(4) 1

>>> T[0] = 2 ...error text omitted... TypeError: 'tuple' object does not support item assignment

>>> T = ('spam', 3.0, [11, 22, 33]) >>> T[1] 3.0 >>> T[2][1] 22 >>> T.append(4) AttributeError: 'tuple' object has no attribute 'append'

Why Tuples?

Files open 'w' >>> >>> 6 >>> 6 >>>

f = open('data.txt', 'w') f.write('Hello\n') f.write('world\n') f.close()

Files | 97

www.it-ebooks.info

'r'

>>> f = open('data.txt') >>> text = f.read() >>> text 'Hello\nworld\n' >>> print(text) Hello world >>> text.split() ['Hello', 'world']

read readline seek for dir

help

>>> dir(f) [ ...many names omitted... 'buffer', 'close', 'closed', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines'] >>>help(f.seek) ...try it and see...

bytes >>> data = open('data.bin', 'rb').read() >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> data[4:8] b'spam'

98 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

Other File-Like Tools open

Other Core Types

set {...} >>> X = set('spam') >>> Y = {'h', 'a', 'm'} >>> X, Y ({'a', 'p', 's', 'm'}, {'a', 'h', 'm'}) >>> X & Y {'a', 'm'} >>> X | Y {'a', 'p', 's', 'h', 'm'} >>> X – Y {'p', 's'} >>> {x ** 2 for x in [1, 2, 3, 4]} {16, 1, 4, 9}

>>> 1 / 3 0.33333333333333331 >>> (2/3) + (1/2)

Other Core Types | 99

www.it-ebooks.info

1.1666666666666665 >>> import decimal >>> d = decimal.Decimal('3.141') >>> d + 1 Decimal('4.141') >>> decimal.getcontext().prec = 2 >>> decimal.Decimal('1.00') / decimal.Decimal('3.00') Decimal('0.33') >>> from fractions import Fraction >>> f = Fraction(2, 3) >>> f + 1 Fraction(5, 3) >>> f + Fraction(1, 2) Fraction(7, 6)

True

False

None >>> 1 > 2, 1 < 2 (False, True) >>> bool('spam') True >>> X = None >>> print(X) None >>> L = [None] * 100 >>> L [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, ...a list of 100 Nones...]

How to Break Your Code’s Flexibility type

L

>>> type(L) >>> type(type(L))

>>> type(L)

100 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

>>> type(type(L))

>>> if type(L) == type([]): print('yes') yes >>> if type(L) == list: print('yes') yes >>> if isinstance(L, list): print('yes') yes

User-Defined Classes

>>> class Worker: def __init__(self, name, pay): self.name = name

Other Core Types | 101

www.it-ebooks.info

self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay *= (1.0 + percent)

name

self >>> bob = Worker('Bob Smith', 50000) >>> sue = Worker('Sue Jones', 60000) >>> bob.lastName() 'Smith' >>> sue.lastName() 'Jones' >>> sue.giveRaise(.10) >>> sue.pay 66000.0

name

And Everything Else

102 | Chapter 4: Introducing Python Object Types

Worker pay

pay

www.it-ebooks.info

class

Chapter Summary

Test Your Knowledge: Quiz

Test Your Knowledge: Quiz | 103

www.it-ebooks.info

Test Your Knowledge: Answers None

'spam' open

+

104 | Chapter 4: Introducing Python Object Types

www.it-ebooks.info

CHAPTER 5

Numeric Types

Numeric Type Basics

105

www.it-ebooks.info

Numeric Literals

Literal

Interpretation

1234, −24, 0, 99999999999999

Integers (unlimited size)

1.23, 1., 3.14e-10, 4E210, 4.0e+210

Floating-point numbers

0177, 0x9ff, 0b101010

Octal, hex, and binary literals in 2.6

0o177, 0x9ff, 0b101010

Octal, hex, and binary literals in 3.0

3+4j, 3.0+4.0j, 3J

Complex number literals

e

106 | Chapter 5: Numeric Types

E

www.it-ebooks.info

l

L L

l

L

0x 0 9

0X

A F 0o 0 7

0O

0 0o 0b

0B

0 1 hex(I) oct(I)

bin(I) int(str, base)

imaginarypart imaginarypart

j

J

realpart+imaginarypart realpart

complex(real, imag)

Numeric Type Basics | 107

www.it-ebooks.info

Built-in Numeric Tools

+ - * / >> ** & pow abs round int hex bin random math

as_integer_ratio is_integer bit_length

Python Expression Operators

X X + Y

+ X

Y X

Y

Y + − * / %

= y

Magnitude comparison, set subset and superset;

x == y, x != y

Value equality operators

x | y

Bitwise OR, set union

x ^ y

Bitwise XOR, set symmetric difference

x & y

Bitwise AND, set intersection

x > y

Shift x left or right by y bits

x + y

Addition, concatenation;

x – y

Subtraction, set difference

x * y

Multiplication, repetition;

x % y

Remainder, format;

x / y, x // y

Division: true and floor

−x, +x

Negation, identity

˜x

Bitwise NOT (inversion)

x ** y

Power (exponentiation)

x[i]

Indexing (sequence, mapping, others)

x[i:j:k]

Slicing

x(...)

Call (function, method, class, other callable)

x.attr

Attribute reference

(...)

Tuple, expression, generator expression

[...]

List, list comprehension

{...}

Dictionary, set, set and dictionary comprehensions

Numeric Type Basics | 109

www.it-ebooks.info

X != Y

X Y

X != Y `X` str

repr(X)

repr

X // Y X / Y

[...]

(...)

{...}

yield if

if else send(...) yield X < Y < Z

X < Y and Y < X X[I:J:K] X[slice(I, J, K)]

sorted(dict.items())

110 | Chapter 5: Numeric Types

www.it-ebooks.info

Mixed operators follow operator precedence

A * B + C * D

X + Y * Z X

(Y * Z) +

* A * B

C * D

Parentheses group subexpressions

X + Y * Z (X + Y) * Z X + (Y * Z)

+

X

Y *

Mixed types are converted up

40 + 3.14

Numeric Type Basics | 111

www.it-ebooks.info

>>> int(3.1415) 3 >>> float(3) 3.0

Preview: Operator overloading and polymorphism

+

112 | Chapter 5: Numeric Types

[i]

www.it-ebooks.info

+ +

Numbers in Action

Variables and Basic Expressions a

b

a

b

% python >>> a = 3 >>> b = 4

#

#

Numbers in Action | 113

www.it-ebooks.info

a

b

3

>>> a + 1, a (4, 2) >>> b * 3, b (12, 2.0) >>> a % 2, b (1, 16) >>> 2 + 4.0, (6.0, 16.0)

4

– 1 / 2 ** 2 2.0 ** b

a

b

>>> c * 2 Traceback (most recent call last): File "", line 1, in ? NameError: name 'c' is not defined

>>> b / 2 + a 5.0 >>> print(b / (2.0 + a)) 0.8

/ +

b // 2 + a + / 2.0

114 | Chapter 5: Numeric Types

www.it-ebooks.info

a

3.0

+ 4 / 5

0

0.8

Numeric Display Formats print print >>> b / (2.0 + a) 0.80000000000000004 >>> print(b / (2.0 + a)) 0.8

print

print

>>> 1 / 2.0 0.5

print >>> num = 1 / 3.0 >>> num 0.33333333333333331 >>> print(num) 0.333333333333 >>> '%e' % num '3.333333e-001' >>> '%4.2f' % num '0.33' >>> '{0:4.2f}'.format(num) '0.33'

Numbers in Action | 115

www.it-ebooks.info

str and repr Display Formats print repr

str

>>> num = 1 / 3 >>> repr(num) '0.33333333333333331' >>> str(num) '0.333333333333'

repr str print str

repr

str

Comparisons: Normal and Chained

>>> 1 < True >>> 2.0 True >>> 2.0 True >>> 2.0 False

2 >= 1 == 2.0 != 2.0

B C)

116 | Chapter 5: Numeric Types

A

C

(A < B < C) (A < B and B <

www.it-ebooks.info

>>> X = 2 >>> Y = 4 >>> Z = 6

Y >>> X < Y < Z True >>> X < Y and Y < Z True

>>> X < Y > Z False >>> X < Y and Y > Z False >>> 1 < 2 < 3.0 < 4 True >>> 1 > 2 > 3.0 > 4 False

>>> 1 == 2 < 3 False

0 < 3

1 == 2 False True

True

False

Division: Classic, Floor, and True

X / Y

X // Y

Numbers in Action | 117

www.it-ebooks.info

/

//

/ //

/ //

C:\misc> C:\Python30\python >>> >>> 10 / 4 2.5 >>> 10 // 4 2 >>> 10 / 4.0 2.5 >>> 10 // 4.0 2.0 C:\misc> C:\Python26\python >>> >>> 10 / 4 2 >>> 10 // 4 2 >>> 10 / 4.0 2.5 >>> 10 // 4.0 2.0

// / //

118 | Chapter 5: Numeric Types

www.it-ebooks.info

Supporting either Python / // float

/

X = Y // Z X = Y / float(Z)

/

__future__

float C:\misc> C:\Python26\python >>> from __future__ import division >>> 10 / 4 2.5 >>> 10 // 4 2

Floor versus truncation //

math >>> >>> 2 >>> -3 >>> 2 >>> -2

import math math.floor(2.5) math.floor(-2.5) math.trunc(2.5) math.trunc(-2.5)

C:\misc> c:\python30\python >>> 5 / 2, 5 / −2 (2.5, −2.5) >>> 5 // 2, 5 // −2 (2, −3) >>> 5 / 2.0, 5 / −2.0 (2.5, −2.5)

Numbers in Action | 119

www.it-ebooks.info

>>> 5 // 2.0, 5 // −2.0 (2.0, −3.0)

/ C:\misc> c:\python26\python >>> 5 / 2, 5 / −2 (2, −3) >>> 5 // 2, 5 // −2 (2, −3) >>> 5 / 2.0, 5 / −2.0 (2.5, −2.5) >>> 5 // 2.0, 5 // −2.0 (2.0, −3.0)

math.trunc C:\misc> c:\python30\python >>> import math >>> 5 / −2 −2.5 >>> 5 // −2 -3 >>> math.trunc(5 / −2) −2 C:\misc> c:\python26\python >>> import math >>> 5 / float(−2) −2.5 >>> 5 / −2, 5 // −2 (−3, −3) >>> math.trunc(5 / float(−2)) −2

Why does truncation matter? >>> (5 / 2), (5 / 2.0), (5 / −2.0), (5 / −2) (2.5, 2.5, −2.5, −2.5) >>> (5 // 2), (5 // 2.0), (5 // −2.0), (5 // −2) (2, 2.0, −3.0, −3) >>> (9 / 3), (9.0 / 3), (9 // 3), (9 // 3.0) (3.0, 3.0, 3, 3.0)

>>> (5 / 2), (5 / 2.0), (5 / −2.0), (5 / −2) (2, 2.5, −2.5, −3)

120 | Chapter 5: Numeric Types

round

www.it-ebooks.info

>>> (5 // 2), (5 // 2.0), (5 // −2.0), (5 // −2) (2, 2.0, −3.0, −3) >>> (9 / 3), (9.0 / 3), (9 // 3), (9 // 3.0) (3, 3.0, 3, 3.0)

/ //

while /

from

Integer Precision

>>> 999999999999999999999999999999 + 1 1000000000000000000000000000000

>>> 999999999999999999999999999999 + 1 1000000000000000000000000000000L

>>> 2 ** 200 1606938044258990275541962092341162602522202993782792835301376 >>> 2 ** 200 1606938044258990275541962092341162602522202993782792835301376L

Numbers in Action | 121

www.it-ebooks.info

Complex Numbers

j

J + 2

−3

2 + −3j >>> 1j * 1J (-1+0j) >>> 2 + 1j * 3 (2+3j) >>> (2 + 1j) * 3 (6+3j)

cmath math

Hexadecimal, Octal, and Binary Notation

>>> (1, >>> (1, >>> (1,

0o1, 0o20, 0o377 16, 255) 0x01, 0x10, 0xFF 16, 255) 0b1, 0b10000, 0b11111111 16, 255)

0o377 255 >>> oct(64), hex(64), bin(64) ('0100', '0x40', '0b1000000')

122 | Chapter 5: Numeric Types

0xFF

0b11111111

www.it-ebooks.info

oct

hex

bin

int >>> int('64'), int('100', 8), int('40', 16), int('1000000', 2) (64, 64, 64, 64) >>> int('0x40', 16), int('0b1000000', 2) (64, 64)

eval

>>> eval('64'), eval('0o100'), eval('0x40'), eval('0b1000000') (64, 64, 64, 64)

>>> '{0:o}, {1:x}, {2:b}'.format(64, 64, 64) '100, 40, 1000000' >>> '%o, %x, %X' % (64, 255, 255) '100, ff, FF'

>>> (1, >>> (1,

0o1, 0o20, 0o377 16, 255) 01, 020, 0377 16, 255)

010 0o010

>>> X = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF >>> X 5192296858534827628530496329220095L >>> oct(X)

Numbers in Action | 123

www.it-ebooks.info

'017777777777777777777777777777777777777L' >>> bin(X) '0b1111111111111111111111111111111111111111111111111111111111 ...and so on...

Bitwise Operations

>>> >>> 4 >>> 3 >>> 1

x = 1 x >> X = 0b0001 >>> X >> bin(X >> bin(X | 0b010) '0b11' >>> bin(X & 0b1) '0b1' >>> X = 0xFF >>> bin(X) '0b11111111' >>> X ^ 0b10101010 85 >>> bin(X ^ 0b10101010) '0b1010101' >>> int('1010101', 2) 85 >>> hex(85) '0x55'

124 | Chapter 5: Numeric Types

0001 0011

www.it-ebooks.info

bit_length

bin

len

>>> X = 99 >>> bin(X), X.bit_length() ('0b1100011', 7) >>> bin(256), (256).bit_length() ('0b100000000', 9) >>> len(bin(256)) - 2 9

Other Built-in Numeric Tools pow

abs

math >>> import math >>> math.pi, math.e (3.1415926535897931, 2.7182818284590451) >>> math.sin(2 * math.pi / 180) 0.034899496702500969 >>> math.sqrt(144), math.sqrt(2) (12.0, 1.4142135623730951) >>> pow(2, 4), 2 ** 4 (16, 16) >>> abs(-42.0), sum((1, 2, 3, 4)) (42.0, 10) >>> min(3, 1, 2, 4), max(3, 1, 2, 4) (1, 4)

sum

min

max

Numbers in Action | 125

www.it-ebooks.info

>>> math.floor(2.567), math.floor(-2.567) (2, −3) >>> math.trunc(2.567), math.trunc(−2.567) (2, −2) >>> int(2.567), int(−2.567) (2, −2) >>> round(2.567), round(2.467), round(2.567, 2) (3, 2, 2.5699999999999998) >>> '%.1f' % 2.567, '{0:.2f}'.format(2.567) ('2.6', '2.57')

(3, 2, 2.57)

print round

>>> (1 / 3), round(1 / 3, 2), ('%.2f' % (1 / 3)) (0.33333333333333331, 0.33000000000000002, '0.33')

>>> import math >>> math.sqrt(144) 12.0 >>> 144 ** .5 12.0 >>> pow(144, .5) 12.0 >>> math.sqrt(1234567890) 35136.418286444619 >>> 1234567890 ** .5 35136.418286444619 >>> pow(1234567890, .5) 35136.418286444619

math abs

round

builtins

126 | Chapter 5: Numeric Types

__builtin__

www.it-ebooks.info

random

>>> import random >>> random.random() 0.44694718823781876 >>> random.random() 0.28970426439292829 >>> random.randint(1, 10) 5 >>> random.randint(1, 10) 4 >>> random.choice(['Life of Brian', 'Holy Grail', 'Meaning of Life']) 'Life of Brian' >>> random.choice(['Life of Brian', 'Holy Grail', 'Meaning of Life']) 'Holy Grail'

random

Other Numeric Types

Decimal Type Decimal

Other Numeric Types | 127

www.it-ebooks.info

The basics

>>> 0.1 + 0.1 + 0.1 - 0.3 5.5511151231257827e-17

>>> print(0.1 + 0.1 + 0.1 - 0.3) 5.55111512313e-17

>>> from decimal import Decimal >>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3') Decimal('0.0')

Decimal decimal str

>>> Decimal('0.1') + Decimal('0.10') + Decimal('0.10') - Decimal('0.30') Decimal('0.00')

decimal.Decimal.from_float(1.25)

Setting precision globally decimal

>>> import decimal >>> decimal.Decimal(1) / decimal.Decimal(7) Decimal('0.1428571428571428571428571429') >>> decimal.getcontext().prec = 4 >>> decimal.Decimal(1) / decimal.Decimal(7) Decimal('0.1429')

128 | Chapter 5: Numeric Types

www.it-ebooks.info

>>> 1999 + 1.33 2000.3299999999999 >>> >>> decimal.getcontext().prec = 2 >>> pay = decimal.Decimal(str(1999 + 1.33)) >>> pay Decimal('2000.33')

Decimal context manager with C:\misc> C:\Python30\python >>> import decimal >>> decimal.Decimal('1.00') / decimal.Decimal('3.00') Decimal('0.3333333333333333333333333333') >>> >>> with decimal.localcontext() as ctx: ... ctx.prec = 2 ... decimal.Decimal('1.00') / decimal.Decimal('3.00') ... Decimal('0.33') >>> >>> decimal.Decimal('1.00') / decimal.Decimal('3.00') Decimal('0.3333333333333333333333333333')

with

Fraction Type Fraction

The basics Fraction

Decimal

Other Numeric Types | 129

www.it-ebooks.info

Decimal Fraction >>> from fractions import Fraction >>> x = Fraction(1, 3) >>> y = Fraction(4, 6) >>> x Fraction(1, 3) >>> y Fraction(2, 3) >>> print(y) 2/3

Fraction >>> x + y Fraction(1, 1) >>> x – y Fraction(-1, 3) >>> x * y Fraction(2, 9)

Fraction >>> Fraction('.25') Fraction(1, 4) >>> Fraction('1.25') Fraction(5, 4) >>> >>> Fraction('.25') + Fraction('1.25') Fraction(3, 2)

Numeric accuracy

>>> a = 1 / 3.0 >>> b = 4 / 6.0 >>> a 0.33333333333333331 >>> b 0.66666666666666663 >>> a + b 1.0 >>> a - b -0.33333333333333331 >>> a * b 0.22222222222222221

Fraction 130 | Chapter 5: Numeric Types

www.it-ebooks.info

Decimal

>>> 0.1 + 0.1 + 0.1 - 0.3 5.5511151231257827e-17 >>> from fractions import Fraction >>> Fraction(1, 10) + Fraction(1, 10) + Fraction(1, 10) - Fraction(3, 10) Fraction(0, 1) >>> from decimal import Decimal >>> Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3') Decimal('0.0')

>>> 1 / 3 0.33333333333333331 >>> Fraction(1, 3) Fraction(1, 3) >>> import decimal >>> decimal.getcontext().prec = 2 >>> decimal.Decimal(1) / decimal.Decimal(3) Decimal('0.33')

>>> (1 / 3) + (6 / 12) 0.83333333333333326 >>> Fraction(6, 12) Fraction(1, 2) >>> Fraction(1, 3) + Fraction(6, 12) Fraction(5, 6) >>> decimal.Decimal(str(1/3)) + decimal.Decimal(str(6/12)) Decimal('0.83') >>> 1000.0 / 1234567890 8.1000000737100011e-07 >>> Fraction(1000, 1234567890) Fraction(100, 123456789)

Conversions and mixed types from_float

Other Numeric Types | 131

www.it-ebooks.info

float

Fraction *

>>> (2.5).as_integer_ratio() (5, 2) >>> f = 2.5 >>> z = Fraction(*f.as_integer_ratio()) >>> z Fraction(5, 2) >>> x Fraction(1, 3) >>> x + z Fraction(17, 6) >>> float(x) 0.33333333333333331 >>> float(z) 2.5 >>> float(x + z) 2.8333333333333335 >>> 17 / 6 2.8333333333333335 >>> Fraction.from_float(1.75) Fraction(7, 4) >>> Fraction(*(1.75).as_integer_ratio()) Fraction(7, 4)

Fraction

>>> x Fraction(1, 3) >>> x + 2 Fraction(7, 3) >>> x + 2.0 2.3333333333333335 >>> x + (1./3) 0.66666666666666663 >>> x + (4./3) 1.6666666666666665 >>> x + Fraction(4, 3) Fraction(5, 3)

132 | Chapter 5: Numeric Types

www.it-ebooks.info

>>> 4.0 / 3 1.3333333333333333 >>> (4.0 / 3).as_integer_ratio() (6004799503160661, 4503599627370496) >>> x Fraction(1, 3) >>> a = x + Fraction(*(4.0 / 3).as_integer_ratio()) >>> a Fraction(22517998136852479, 13510798882111488) >>> 22517998136852479 / 13510798882111488. 1.6666666666666667 >>> a.limit_denominator(10) Fraction(5, 3)

Fraction

Sets

Set basics in Python 2.6

set >>> x = set('abcde') >>> y = set('bdxyz')

Other Numeric Types | 133

www.it-ebooks.info

>>> x set(['a', 'c', 'b', 'e', 'd'])

>>> 'e' in x True >>> x – y set(['a', 'c', 'e']) >>> x | y set(['a', 'c', 'b', 'e', 'd', 'y', 'x', 'z']) >>> x & y set(['b', 'd']) >>> x ^ y set(['a', 'c', 'e', 'y', 'x', 'z']) >>> x > y, x < y (False, False)

add update

remove

dir

set

x

y >>> z = x.intersection(y) >>> z set(['b', 'd']) >>> z.add('SPAM') >>> z set(['b', 'd', 'SPAM']) >>> z.update(set(['X', 'Y'])) >>> z set(['Y', 'X', 'b', 'd', 'SPAM']) >>> z.remove('b') >>> z set(['Y', 'X', 'd', 'SPAM'])

len for

>>> for item in set('abc'): print(item * 3) ... aaa

134 | Chapter 5: Numeric Types

www.it-ebooks.info

ccc bbb

>>> S = set([1, 2, 3]) >>> S | set([3, 4]) set([1, 2, 3, 4]) >>> S | [3, 4] TypeError: unsupported operand type(s) for |: 'set' and 'list' >>> S.union([3, 4]) set([1, 2, 3, 4]) >>> S.intersection((1, 3, 5)) set([1, 3]) >>> S.issubset(range(-5, 5)) True

Set literals in Python 3.0 set

set([1, 2, 3, 4]) {1, 2, 3, 4}

set

C:\Misc> c:\python30\python >>> set([1, 2, 3, 4]) {1, 2, 3, 4} >>> set('spam') {'a', 'p', 's', 'm'} >>> {1, 2, 3, 4}

Other Numeric Types | 135

www.it-ebooks.info

{1, 2, 3, 4} >>> S = {'s', 'p', 'a', 'm'} >>> S.add('alot') >>> S {'a', 'p', 's', 'm', 'alot'}

>>> S1 = {1, 2, 3, 4} >>> S1 & {1, 3} {1, 3} >>> {1, 5, 3, 6} | S1 {1, 2, 3, 4, 5, 6} >>> S1 - {1, 3, 4} {2} >>> S1 > {1, 3} True

{} >>> S1 - {1, 2, 3, 4} set() >>> type({}) >>> S = set() >>> S.add(1.23) >>> S {1.23}

>>> {1, 2, 3} | {3, 4} {1, 2, 3, 4} >>> {1, 2, 3} | [3, 4] TypeError: unsupported operand type(s) for |: 'set' and 'list' >>> {1, >>> {1, >>> {1,

{1, 2, 3}.union([3, 4]) 2, 3, 4} {1, 2, 3}.union({3, 4}) 2, 3, 4} {1, 2, 3}.union(set([3, 4])) 2, 3, 4}

>>> {1, 2, 3}.intersection((1, 3, 5)) {1, 3} >>> {1, 2, 3}.issubset(range(-5, 5)) True

Immutable constraints and frozen sets

136 | Chapter 5: Numeric Types

set

www.it-ebooks.info

>>> S {1.23} >>> S.add([1, 2, 3]) TypeError: unhashable type: 'list' >>> S.add({'a':1}) TypeError: unhashable type: 'dict' >>> S.add((1, 2, 3)) >>> S {1.23, (1, 2, 3)} >>> S | {(4, 5, 6), (1, 2, 3)} {1.23, (4, 5, 6), (1, 2, 3)} >>> (1, 2, 3) in S True >>> (1, 4, 3) in S False

frozenset

set

Set comprehensions in Python 3.0

>>> {x ** 2 for x in [1, 2, 3, 4]} {16, 1, 4, 9}

x ** 2

>>> {x for x in 'spam'} {'a', 'p', 's', 'm'} >>> {c * 4 for c in 'spam'} {'ssss', 'aaaa', 'pppp', 'mmmm'} >>> {c * 4 for c in 'spamham'}

Other Numeric Types | 137

www.it-ebooks.info

{'ssss', 'aaaa', 'hhhh', 'pppp', 'mmmm'} >>> S = {c * 4 for c in 'spam'} >>> S | {'mmmm', 'xxxx'} {'ssss', 'aaaa', 'pppp', 'mmmm', 'xxxx'} >>> S & {'mmmm', 'xxxx'} {'mmmm'}

if

Why sets?

list >>> >>> {1, >>> >>> [1,

L = [1, 2, 1, 3, 2, 4, 5] set(L) 2, 3, 4, 5} L = list(set(L)) L 2, 3, 4, 5]

set >>> engineers = {'bob', 'sue', 'ann', 'vic'} >>> managers = {'tom', 'sue'} >>> 'bob' in engineers True >>> engineers & managers

138 | Chapter 5: Numeric Types

www.it-ebooks.info

{'sue'} >>> engineers | managers {'vic', 'sue', 'tom', 'bob', 'ann'} >>> engineers – managers {'vic', 'bob', 'ann'} >>> managers – engineers {'tom'} >>> engineers > managers False >>> {'bob', 'sue'} < engineers True >>> (managers | engineers) > managers True >>> managers ^ engineers {'vic', 'bob', 'ann', 'tom'} >>> (managers | engineers) - (managers ^ engineers) {'sue'}

Booleans bool True

False

bool True

True False False

bool int True

str

True False repr True

False 1

0 bool

False

1

0 while True:

while 1:

Other Numeric Types | 139

www.it-ebooks.info

flag = False True True

False

1

0

1 bool

True True + 4

5

>>> type(True) >>> isinstance(True, int) True >>> True == 1 True >>> True is 1 False >>> True or False True >>> True + 4 5

and

Numeric Extensions

140 | Chapter 5: Numeric Types

False

0

or

1

www.it-ebooks.info

Chapter Summary

Test Your Knowledge: Quiz 2 * (3 + 4) 2 * 3 + 4 2 + 3 * 4 1 + 2.0 + 3

Test Your Knowledge: Answers 14 10

14

math

math math.sqrt(N)

Test Your Knowledge: Answers | 141

www.it-ebooks.info

X ** 2

pow(X, 2) 0.5

int(N)

math.trunc(N)

X ** .5

round(N, digits) math.floor(N)

float(I) / oct(I)

hex(I) bin(I) %

format

int(S, base) 8 16

142 | Chapter 5: Numeric Types

2

eval(S)

www.it-ebooks.info

CHAPTER 6

The Dynamic Typing Interlude

The Case of the Missing Declaration Statements a = 3 a a

143

www.it-ebooks.info

Variables, Objects, and References a = 3 a

a

a

>>> a = 3

3 a a

144 | Chapter 6: The Dynamic Typing Interlude

3

www.it-ebooks.info

0

Types Live with Objects, Not Variables

The Case of the Missing Declaration Statements | 145

www.it-ebooks.info

>>> a = 3 >>> a = 'spam' >>> a = 1.23

a a a = 'spam'

a a

3 3 int 'spam'

str

Objects Are Garbage-Collected a

3 >>> a = 3 >>> a = 'spam'

x

146 | Chapter 6: The Dynamic Typing Interlude

www.it-ebooks.info

>>> >>> >>> >>>

x x x x

= = = =

42 'shrubbery' 3.1415 [1, 2, 3]

x x

x 'shrubbery'

42

x

gc

The Case of the Missing Declaration Statements | 147

www.it-ebooks.info

Shared References

>>> a = 3 >>> b = a

b

a 3 a

b

b

>>> a = 3 >>> b = a >>> a = 'spam'

'spam' b b

a 3 b

b

a

>>> a = 3 >>> b = a >>> a = a + 2

148 | Chapter 6: The Dynamic Typing Interlude

'spam'

www.it-ebooks.info

a 3

a

b a

5

+

b 3

Shared References and In-Place Changes

>>> L1 = [2, 3, 4] >>> L2 = L1

Shared References | 149

www.it-ebooks.info

L1

2 3 L1[0] L1

4

2

L1

L2

a

b

>>> L1 = 24

L1

L2

>>> L1 = [2, 3, 4] >>> L2 = L1 >>> L1[0] = 24 >>> L1 [24, 3, 4] >>> L2 [24, 3, 4]

L1 L1 L1 L2

L1 L2

list copy >>> L1 = [2, 3, 4] >>> L2 = L1[:] >>> L1[0] = 24 >>> L1 [24, 3, 4] >>> L2 [2, 3, 4]

L1 L1

150 | Chapter 6: The Dynamic Typing Interlude

L2

L2

www.it-ebooks.info

X.copy()

copy

import copy X = copy.copy(Y) X = copy.deepcopy(Y)

class

Shared References and Equality

>>> x = 42 >>> x = 'shrubbery'

42 42

>>> L >>> M >>> L True >>> L True

= [1, 2, 3] = L == M is M

== is

True

Shared References | 151

www.it-ebooks.info

is False

>>> L >>> M >>> L True >>> L False

= [1, 2, 3] = [1, 2, 3] == M

>>> X >>> Y >>> X True >>> X True

= 42 = 42 == Y

is M

is Y

X

Y

==

is

is getrefcount

sys 1

>>> import sys >>> sys.getrefcount(1) 837

is

Dynamic Typing Is Everywhere

152 | Chapter 6: The Dynamic Typing Interlude

www.it-ebooks.info

for

Chapter Summary

Test Your Knowledge: Quiz A A = "spam" B = A B = "shrubbery"

A A = ["spam"] B = A B[0] = "shrubbery"

A A = ["spam"] B = A[:] B[0] = "shrubbery"

Test Your Knowledge: Quiz | 153

www.it-ebooks.info

Test Your Knowledge: Answers A

"spam"

B

"shrubbery"

B

A

B

"spam" B A

B = B +

'shrubbery' B

A

A B

["shrubbery"] B B

A

A

A ["spam"]

B

B ==

is B

A

154 | Chapter 6: The Dynamic Typing Interlude

www.it-ebooks.info

CHAPTER 7

Strings

155

www.it-ebooks.info

Operation

Interpretation

S = ''

Empty string

S = "spam's"

Double quotes, same as single

S = 's\np\ta\x00m'

Escape sequences

S = """..."""

Triple-quoted block strings

S = r'\temp\spam'

Raw strings

S = b'spam'

Byte strings in 3.0 (Chapter 36)

S = u'spam'

Unicode strings in 2.6 only (Chapter 36)

S1 + S2

Concatenate, repeat

S * 3 S[i]

Index, slice, length

S[i:j] len(S) "a %s parrot" % kind

String formatting expression

"a {0} parrot".format(kind)

String formatting method in 2.6 and 3.0

S.find('pa')

String method calls: search,

S.rstrip()

remove whitespace,

S.replace('pa', 'xx')

replacement,

S.split(',')

split on delimiter,

S.isdigit()

content test,

S.lower()

case conversion,

S.endswith('spam')

end test,

'spam'.join(strlist)

delimiter join,

S.encode('latin-1')

Unicode encoding, etc.

for x in S: print(x)

Iteration, membership

'spam' in S [c * 2 for c in S] map(ord, S)

re

156 | Chapter 7: Strings

www.it-ebooks.info

str

str bytes bytearray

bytes

unicode str bytearray

String Literals 'spa"m' "spa'm" '''... spam ...''' """... spam ...""" "s\tp\na\0m" r"C:\new\test.spm"

String Literals | 157

www.it-ebooks.info

b'sp\x01am' u'eggs\u0020spam'

Single- and Double-Quoted Strings Are the Same

>>> 'shrubbery', "shrubbery" ('shrubbery', 'shrubbery')

>>> 'knight"s', "knight's" ('knight"s', "knight's")

+

>>> title = "Meaning " 'of' " Life" >>> title 'Meaning of Life'

>>> 'knight\'s', "knight\"s" ("knight's", 'knight"s')

Escape Sequences Represent Special Bytes

\

158 | Chapter 7: Strings

www.it-ebooks.info

>>> s = 'a\nb\tc'

\n \t print >>> s 'a\nb\tc' >>> print(s) a b c

len >>> len(s) 5

Escape

Meaning

\newline

Ignored (continuation line)

\\

Backslash (stores one \)

\'

Single quote (stores ')

\"

Double quote (stores ")

\a

Bell

\b

Backspace

\f

Formfeed

\n

Newline (linefeed)

\r

Carriage return

\t

Horizontal tab

\v

Vertical tab

\xhh

Character with hex value hh (at most 2 digits)

\ooo

Character with octal value ooo (up to 3 digits)

\0

Null: binary 0 character (doesn’t end string)

String Literals | 159

www.it-ebooks.info

a

Escape

Meaning

\N{ id }

Unicode database ID

\uhhhh

Unicode 16-bit hex

\Uhhhhhhhh

Unicode 32-bit hexa

\other

Not an escape (keeps both \ and other)

The \Uhhhh... escape sequence takes exactly eight hexadecimal digits (h); both \u and \U can be used only in Unicode string literals.

>>> s = 'a\0b\0c' >>> s 'a\x00b\x00c' >>> len(s) 5

>>> s = '\001\002\x03' >>> s '\x01\x02\x03' >>> len(s) 3

>>> S = "s\tp\na\x00m" >>> S 's\tp\na\x00m' >>> len(S) 7 >>> print(S) s p a m

b

'rb' 'wb'

bytes str

struct

160 | Chapter 7: Strings

www.it-ebooks.info

\ >>> x = "C:\py\code" >>> x 'C:\\py\\code' >>> len(x) 10

\\

\

Raw Strings Suppress Escapes

myfile = open('C:\new\text.dat', 'w')

\n

\t

r

r myfile = open(r'C:\new\text.dat', 'w')

myfile = open('C:\\new\\text.dat', 'w')

>>> path = r'C:\new\text.dat' >>> path 'C:\\new\\text.dat' >>> print(path)

String Literals | 161

www.it-ebooks.info

C:\new\text.dat >>> len(path) 15

print len print(path)

re 'C:/new/ text.dat'

r"...\"

r'1\nb\tc\\'[:-1] '1\\nb\\tc\\'

Triple Quotes Code Multiline Block Strings

>>> mantra = """Always look ... on the bright ... side of life.""" >>> >>> mantra 'Always look\n on the bright\nside of life.'

162 | Chapter 7: Strings

r'1\nb\tc' + '\\'

www.it-ebooks.info

... \n

>>> print(mantra) Always look on the bright side of life.

X = 1 """ import os print(os.getcwd()) """ Y = 2

Strings in Action

Strings in Action | 163

www.it-ebooks.info

Basic Operations + * % python >>> len('abc') 3 >>> 'abc' + 'def' 'abcdef' >>> 'Ni!' * 4 'Ni!Ni!Ni!Ni!'

len

>>> print('------- ...more... ---') >>> print('-' * 80)

+ *

+

'abc'+9

9 for

in in

str.find() >>> myjob = "hacker" >>> for c in myjob: print(c, end=' ') ...

164 | Chapter 7: Strings

www.it-ebooks.info

h a c k e r >>> "k" in myjob True >>> "z" in myjob False >>> 'spam' in 'abcspamdef' True

for c

Indexing and Slicing

>>> S = 'spam' >>> S[0], S[−2] ('s', 'a') >>> S[1:3], S[1:], S[:−1] ('pa', 'pam', 'spa')

S S[0] 's'

S[−2]

Strings in Action | 165

www.it-ebooks.info

S[1:3] S[1:] S[:−1]

S[i]

S[0] S[−2] S[i:j]

S[1:3] S[1:]

166 | Chapter 7: Strings

S[len(S)−2]

www.it-ebooks.info

S[:3] S[:−1] S[:] S

Extended slicing: the third limit and slice objects

X[I:J:K] X

I

X[1:10:2]

J

K

X X[::2]

>>> S = 'abcdefghijklmnop' >>> S[1:10:2] 'bdfhj' >>> S[::2] 'acegikmo'

"hello"[::−1] "olleh"

>>> S = 'hello' >>> S[::−1] 'olleh'

S[5:1:−1]

Strings in Action | 167

www.it-ebooks.info

>>> S = 'abcedfg' >>> S[5:1:−1] 'fdec'

for

>>> 'spam'[1:3] 'pa' >>> 'spam'[slice(1, 3)] 'pa' >>> 'spam'[::-1] 'maps' >>> 'spam'[slice(None, None, −1)] 'maps'

Why You Will Care: Slices

argv sys import sys print(sys.argv) % python echo.py −a −b −c ['echo.py', '−a', '−b', '−c']

sys.argv[1:] ['−a', '−b', '−c']

\n line[:−1]

168 | Chapter 7: Strings

www.it-ebooks.info

line.rstrip

String Conversion Tools

>>> "42" + 1 TypeError: cannot concatenate 'str' and 'int' objects

+

>>> int("42"), str(42) (42, '42') >>> repr(42) '42'

int

str repr

print >>> print(str('spam'), repr('spam')) ('spam', "'spam'")

int

str +

>>> S = "42" >>> I = 1 >>> S + I TypeError: cannot concatenate 'str' and 'int' objects >>> int(S) + I 43

Strings in Action | 169

www.it-ebooks.info

>>> S + str(I) '421'

>>> str(3.1415), float("1.5") ('3.1415', 1.5) >>> text = "1.234E-10" >>> float(text) 1.2340000000000001e-010

eval int float

Character code conversions ord chr

>>> ord('s') 115 >>> chr(115) 's'

>>> >>> >>> '6' >>> >>> '7'

S = '5' S = chr(ord(S) + 1) S S = chr(ord(S) + 1) S

int >>> int('5') 5 >>> ord('5') - ord('0') 5

170 | Chapter 7: Strings

www.it-ebooks.info

>>> >>> >>> ... ... ... >>> 13

B = '1101' I = 0 while B != '': I = I * 2 + (ord(B[0]) - ord('0')) B = B[1:] I

I >> int('1101', 2) 13 >>> bin(13) '0b1101'

Changing Strings >>> S = 'spam' >>> S[0] = "x" Raises an error!

>>> S = S + 'SPAM!' >>> S 'spamSPAM!' >>> S = S[:4] + 'Burger' + S[−1] >>> S 'spamBurger!'

S S

replace

Strings in Action | 171

www.it-ebooks.info

>>> S = 'splot' >>> S = S.replace('pl', 'pamal') >>> S 'spamalot'

>>> 'That is %d %s bird!' % (1, 'dead') That is 1 dead bird! >>> 'That is {0} {1} bird!'.format(1, 'dead') 'That is 1 dead bird!'

bytearray bytearray

ord

String Methods

count

172 | Chapter 7: Strings

chr

www.it-ebooks.info

object.attribute

attribute

object function(arguments) argument

function function

object.method(arguments) method object

arguments

help decode S

S.capitalize()

S.ljust(width [, fill])

S.center(width [, fill])

S.lower()

S.count(sub [, start [, end]])

S.lstrip([chars])

S.encode([encoding [,errors]])

S.maketrans(x[, y[, z]])

S.endswith(suffix [, start [, end]])

S.partition(sep)

S.expandtabs([tabsize])

S.replace(old, new [, count])

S.find(sub [, start [, end]])

S.rfind(sub [,start [,end]])

S.format(fmtstr, *args, **kwargs)

S.rindex(sub [, start [, end]])

S.index(sub [, start [, end]])

S.rjust(width [, fill])

S.isalnum()

S.rpartition(sep)

S.isalpha()

S.rsplit([sep[, maxsplit]])

S.isdecimal()

S.rstrip([chars])

S.isdigit()

S.split([sep [,maxsplit]])

String Methods | 173

www.it-ebooks.info

S.isidentifier()

S.splitlines([keepends])

S.islower()

S.startswith(prefix [, start [, end]])

S.isnumeric()

S.strip([chars])

S.isprintable()

S.swapcase()

S.isspace()

S.title()

S.istitle()

S.translate(map)

S.isupper()

S.upper()

S.join(iterable)

S.zfill(width)

String Method Examples: Changing Strings

>>> S = 'spammy' >>> S = S[:3] + 'xx' + S[5:] >>> S 'spaxxy'

replace >>> S = 'spammy' >>> S = S.replace('mm', 'xx') >>> S 'spaxxy'

replace

>>> 'aa$bb$cc$dd'.replace('$', 'SPAM') 'aaSPAMbbSPAMccSPAMdd'

replace

174 | Chapter 7: Strings

www.it-ebooks.info

find >>> S = 'xxxxSPAMxxxxSPAMxxxx' >>> where = S.find('SPAM') >>> where 4 >>> S = S[:where] + 'EGGS' + S[(where+4):] >>> S 'xxxxEGGSxxxxSPAMxxxx'

find −1 in

find replace

>>> S = 'xxxxSPAMxxxxSPAMxxxx' >>> S.replace('SPAM', 'EGGS') 'xxxxEGGSxxxxEGGSxxxx' >>> S.replace('SPAM', 'EGGS', 1) 'xxxxEGGSxxxxSPAMxxxx'

replace

replace

>>> S = 'spammy' >>> L = list(S) >>> L ['s', 'p', 'a', 'm', 'm', 'y']

list

>>> L[3] = 'x' >>> L[4] = 'x' >>> L ['s', 'p', 'a', 'x', 'x', 'y']

join

String Methods | 175

www.it-ebooks.info

>>> S = ''.join(L) >>> S 'spaxxy'

join join

>>> 'SPAM'.join(['eggs', 'sausage', 'ham', 'toast']) 'eggsSPAMsausageSPAMhamSPAMtoast'

bytearray list join

String Method Examples: Parsing Text

>>> line = 'aaa bbb ccc' >>> col1 = line[0:3] >>> col3 = line[8:] >>> col1 'aaa' >>> col3 'ccc'

>>> line = 'aaa bbb ccc' >>> cols = line.split() >>> cols ['aaa', 'bbb', 'ccc']

split

176 | Chapter 7: Strings

www.it-ebooks.info

>>> line = 'bob,hacker,40' >>> line.split(',') ['bob', 'hacker', '40']

>>> line = "i'mSPAMaSPAMlumberjack" >>> line.split("SPAM") ["i'm", 'a', 'lumberjack']

Other Common String Methods in Action

>>> line = "The knights who say Ni!\n" >>> line.rstrip() 'The knights who say Ni!' >>> line.upper() 'THE KNIGHTS WHO SAY NI!\n' >>> line.isalpha() False >>> line.endswith('Ni!\n') True >>> line.startswith('The') True

in endswith >>> line 'The knights who say Ni!\n' >>> line.find('Ni') != −1 True >>> 'Ni' in line True >>> sub = 'Ni!\n' >>> line.endswith(sub) True >>> line[-len(sub):] == sub True

format

String Methods | 177

www.it-ebooks.info

help(S.method)

method

S

re

re

The Original string Module (Gone in 3.0) string

string string

string X X.method(arguments)

string string.method(X, arguments)

>>> S = 'a+b+c+' >>> x = S.replace('+', 'spam') >>> x 'aspambspamcspam'

string >>> import string >>> y = string.replace(S, '+', 'spam') >>> y 'aspambspamcspam'

178 | Chapter 7: Strings

www.it-ebooks.info

string import from string

String Formatting Expressions

% %

String Formatting Expressions | 179

www.it-ebooks.info

%

% %

%d

%

%d

'dead'

>>> 'That is %d %s bird!' % (1, 'dead') That is 1 dead bird!

>>> exclamation = "Ni" >>> "The knights who say %s!" % exclamation 'The knights who say Ni!' >>> "%d %s %d you" % (1, 'spam', 4) '1 spam 4 you' >>> "%s -- %s -- %s" % (42, 3.14159, [1, 2, 3]) '42 -- 3.14159 -- [1, 2, 3]'

"Ni" %s %

%s %s %s

180 | Chapter 7: Strings

1 %s

www.it-ebooks.info

Advanced String Formatting Expressions % printf printf %e %f

Code

Meaning

s

String (or any object’s str(X) string)

r

s, but uses repr, not str

c

Character

d

Decimal (integer)

i

Integer

u

Same as d (obsolete: no longer unsigned)

o

Octal integer

x

Hex integer

X

x, but prints uppercase

e

Floating-point exponent, lowercase

E

Same as e, but prints uppercase

f

Floating-point decimal

F

Floating-point decimal

g

Floating-point e or f

G

Floating-point E or F

%

Literal %

%g

%[(name)][flags][width][.precision]typecode

% −

+

0 width

precision

*

String Formatting Expressions | 181

www.it-ebooks.info

>>> x = 1234 >>> res = "integers: ...%d...%−6d...%06d" % (x, x, x) >>> res 'integers: ...1234...1234 ...001234'

%e %f

%g %E

%e

>>> x = 1.23456789 >>> x 1.2345678899999999 >>> '%e | %f | %g' % (x, x, x) '1.234568e+00 | 1.234568 | 1.23457' >>> '%E' % x '1.234568E+00'

str >>> '%−6.2f | %05.2f | %+06.1f' % (x, x, x) '1.23 | 01.23 | +001.2' >>> "%s" % x, str(x) ('1.23456789', '1.23456789')

* % >>> '%f, %.2f, %.*f' % (1/3.0, 1/3.0, 4, 1/3.0) '0.333333, 0.33, 0.3333'

Dictionary-Based String Formatting Expressions

>>> "%(n)d %(x)s" % {"n":1, "x":"spam"} '1 spam'

182 | Chapter 7: Strings

www.it-ebooks.info

(n)

(x)

>>> reply = """ Greetings... Hello %(name)s! Your age squared is %(age)s """ >>> values = {'name': 'Bob', 'age': 40} >>> print(reply % values) Greetings... Hello Bob! Your age squared is 40

vars >>> food = 'spam' >>> age = 40 >>> vars() {'food': 'spam', 'age': 40, ...many more... }

>>> "%(age)d %(food)s" % vars() '40 spam'

%x

%o

String Formatting Method Calls

String Formatting Method Calls | 183

www.it-ebooks.info

The Basics format

{1} {food}

>>> template = '{0}, {1} and {2}' >>> template.format('spam', 'ham', 'eggs') 'spam, ham and eggs' >>> template = '{motto}, {pork} and {food}' >>> template.format(motto='spam', pork='ham', food='eggs') 'spam, ham and eggs' >>> template = '{motto}, {0} and {food}' >>> template.format('ham', motto='spam', food='eggs') 'spam, ham and eggs'

>>> '{motto}, {0} and {food}'.format(42, motto=3.14, food=[1, 2]) '3.14, 42 and [1, 2]'

%

format format

>>> X = '{motto}, {0} and {food}'.format(42, motto=3.14, food=[1, 2]) >>> X '3.14, 42 and [1, 2]' >>> X.split(' and ') ['3.14, 42', '[1, 2]'] >>> Y = X.replace('and', 'but under no circumstances') >>> Y '3.14, 42 but under no circumstances [1, 2]'

Adding Keys, Attributes, and Offsets %

184 | Chapter 7: Strings

www.it-ebooks.info

sys >>> import sys >>> 'My {1[spam]} runs {0.platform}'.format(sys, {'spam': 'laptop'}) 'My laptop runs win32' >>> 'My {config[spam]} runs {sys.platform}'.format(sys=sys, config={'spam': 'laptop'}) 'My laptop runs win32'

%

>>> somelist = list('SPAM') >>> somelist ['S', 'P', 'A', 'M'] >>> 'first={0[0]}, third={0[2]}'.format(somelist) 'first=S, third=A' >>> 'first={0}, last={1}'.format(somelist[0], somelist[-1]) 'first=S, last=M' >>> parts = somelist[0], somelist[-1], somelist[1:3] >>> 'first={0}, last={1}, middle={2}'.format(*parts) "first=S, last=M, middle=['P', 'A']"

Adding Specific Formatting %

{fieldname!conversionflag:formatspec}

fieldname conversionflag

r s

a

repr str

ascii

String Formatting Method Calls | 185

www.it-ebooks.info

formatspec

formatspec [[fill]align][sign][#][0][width][.precision][typecode]

align

< > =

^ formatspec

{} * % bin

{0:10} {1:10}

platform

>>> '{0:10} = {1:10}'.format('spam', 123.4567) 'spam = 123.457' >>> '{0:>10} = {1:10} = {1[item]:>> T[1][0] = 'spam' >>> T (1, ['spam', 3], 4)

Why Lists and Tuples?

Files

Files | 229

www.it-ebooks.info

open open

Operation

Interpretation

output = open(r'C:\spam', 'w')

Create output file ('w' means write)

input = open('data', 'r')

Create input file ('r' means read)

input = open('data')

Same as prior line ('r' is the default)

aString = input.read()

Read entire file into a single string

aString = input.read(N)

Read up to next N characters (or bytes) into a string

aString = input.readline()

Read next line (including \n newline) into a string

aList = input.readlines()

Read entire file into list of line strings (with \n)

output.write(aString)

Write a string of characters (or bytes) into file

output.writelines(aList)

Write all line strings in a list into file

output.close()

Manual close (done for you when file is collected)

output.flush()

Flush output buffer to disk without closing

anyFile.seek(N)

Change file position to offset N for next operation

for line in open('data'): use line

File iterators read line by line

open('f.txt', encoding='latin-1')

Python 3.0 Unicode text files (str strings)

open('f.bin', 'rb')

Python 3.0 binary bytes files (bytes strings)

Opening Files open 'w'

b

230 | Chapter 9: Tuples, Files, and Everything Else

'r' 'a'

www.it-ebooks.info

+

open

Using Files open

for

print

int float str pickle struct close close

Files | 231

www.it-ebooks.info

close

close

with as

flush open seek

Files in Action \n readline

>>> >>> 16 >>> 18 >>>

readline

myfile = open('myfile.txt', 'w') myfile.write('hello text file\n') myfile.write('goodbye text file\n') myfile.close()

>>> myfile = open('myfile.txt') >>> myfile.readline() 'hello text file\n' >>> myfile.readline() 'goodbye text file\n' >>> myfile.readline() ''

write \n

232 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

read >>> open('myfile.txt').read() 'hello text file\ngoodbye text file\n' >>> print(open('myfile.txt').read()) hello text file goodbye text file

>>> for line in open('myfile'): ... print(line, end='') ... hello text file goodbye text file

open

Text and binary files in Python 3.0 open

str bytes

unicode

codecs.open

Files | 233

www.it-ebooks.info

bytes

str

bytes

>>> data = open('data.bin', 'rb').read() >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> data[4:8] b'spam' >>> data[0] 115 >>> bin(data[0]) '0b1110011'

\n

Storing and parsing Python objects in files

write >>> >>> >>> >>> >>> >>> >>> >>> >>> >>>

X, Y, Z = 43, 44, 45 S = 'Spam' D = {'a': 1, 'b': 2} L = [1, 2, 3] F = open('datafile.txt', 'w') F.write(S + '\n') F.write('%s,%s,%s\n' % (X, Y, Z)) F.write(str(L) + '$' + str(D) + '\n') F.close()

print >>> chars = open('datafile.txt').read() >>> chars

234 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

"Spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n" >>> print(chars) Spam 43,44,45 [1, 2, 3]${'a': 1, 'b': 2}

>>> F = open('datafile.txt') >>> line = F.readline() >>> line 'Spam\n' >>> line.rstrip() 'Spam'

rstrip line[:−1] \n

>>> line = F.readline() >>> line '43,44,45\n' >>> parts = line.split(',') >>> parts ['43', '44', '45\n']

split

>>> int(parts[1]) 44 >>> numbers = [int(P) for P in parts] >>> numbers [43, 44, 45]

int

rstrip

\n

int

eval >>> line = F.readline() >>> line

Files | 235

www.it-ebooks.info

"[1, 2, 3]${'a': 1, 'b': 2}\n" >>> parts = line.split('$') >>> parts ['[1, 2, 3]', "{'a': 1, 'b': 2}\n"] >>> eval(parts[0]) [1, 2, 3] >>> objects = [eval(P) for P in parts] >>> objects [[1, 2, 3], {'a': 1, 'b': 2}]

Storing native Python objects with pickle eval eval

pickle pickle

>>> >>> >>> >>> >>>

D = {'a': 1, 'b': 2} F = open('datafile.pkl', 'wb') import pickle pickle.dump(D, F) F.close()

pickle >>> F = open('datafile.pkl', 'rb') >>> E = pickle.load(F) >>> E {'a': 1, 'b': 2}

pickle pickle >>> open('datafile.pkl', 'rb').read() b'\x80\x03}q\x00(X\x01\x00\x00\x00aq\x01K\x01X\x01\x00\x00\x00bq\x02K\x02u.'

pickle pickle shelve

pickle help shelve

236 | Chapter 9: Tuples, Files, and Everything Else

pickle

www.it-ebooks.info

shelve

pickle

bytes str

bytes cPickle pickle _pickle pickle

pickle

Storing and parsing packed binary data in files

struct

'wb' struct

>>> F = open('data.bin', 'wb') >>> import struct >>> data = struct.pack('>i4sh', 7, 'spam', 8) >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> F.write(data) >>> F.close()

bytes

>>> F = open('data.bin', 'rb') >>> data = F.read() >>> data b'\x00\x00\x00\x07spam\x00\x08'

Files | 237

www.it-ebooks.info

>>> values = struct.unpack('>i4sh', data) >>> values (7, 'spam', 8)

struct 'wb'

help 'rb'

File context managers

with open(r'C:\misc\data.txt') as myfile: for line in myfile: ...use line here...

try/finally

myfile = open(r'C:\misc\data.txt') try: for line in myfile: ...use line here... finally: myfile.close()

Other File Tools seek flush

dir file

238 | Chapter 9: Tuples, Files, and Everything Else

help

www.it-ebooks.info

open

sys

sys.stdout

os

os.popen

subprocess.Popen

open file open

file

open file

open file

io type(F)

F

Type Categories Revisited

Type Categories Revisited | 239

www.it-ebooks.info

str

bytes

bytearray

frozenset

set

Object type

Category

Mutable?

Numbers (all)

Numeric

No

Strings

Sequence

No

Lists

Sequence

Yes

Dictionaries

Mapping

Yes

Tuples

Sequence

No

Files

Extension

N/A

Sets

Set

Yes

frozenset

Set

No

bytearray (3.0)

Sequence

Yes

Why You Will Care: Operator Overloading

class MySequence: def __getitem__(self, index): # Called on self[index], others def __add__(self, other): # Called on self + other

240 | Chapter 9: Tuples, Files, and Everything Else

unicode

www.it-ebooks.info

__setitem__ self[index]=value

Object Flexibility

>>> L = ['abc', [(1, 2), ([3], 4)], 5] >>> L[1] [(1, 2), ([3], 4)] >>> L[1][1] ([3], 4) >>> L[1][1][0] [3] >>> L[1][1][0][0] 3

References Versus Copies

References Versus Copies | 241

www.it-ebooks.info

X

L D

X X

>>> X = [1, 2, 3] >>> L = ['a', X, 'b'] >>> D = {'x':X, 'y':2}

X L

>>> X[1] = 'surprise' >>> L ['a', [1, 'surprise', 3], 'b'] >>> D {'x': [1, 'surprise', 3], 'y': 2}

242 | Chapter 9: Tuples, Files, and Everything Else

D

www.it-ebooks.info

L[:] copy

X.copy() list

list(L)

copy

>>> L = [1,2,3] >>> D = {'a':1, 'b':2}

>>> A = L[:] >>> B = D.copy()

>>> A[1] = 'Ni' >>> B['c'] = 'spam' >>> >>> L, D ([1, 2, 3], {'a': 1, 'b': 2}) >>> A, B ([1, 'Ni', 3], {'a': 1, 'c': 'spam', 'b': 2})

References Versus Copies | 243

www.it-ebooks.info

>>> X = [1, 2, 3] >>> L = ['a', X[:], 'b'] >>> D = {'x':X[:], 'y':2}

L X

D X

L

D

X

L

D

X copy

copy

import copy Y

copy(Y)

Comparisons, Equality, and Truth

>>> L1 >>> L2 >>> L1 (True,

= [1, ('a', 3)] = [1, ('a', 3)] == L2, L1 is L2 False)

L1

L2

== is L1

L2

== is

>>> S1 = 'spam' >>> S2 = 'spam'

244 | Chapter 9: Tuples, Files, and Everything Else

X = copy.deep

www.it-ebooks.info

>>> S1 == S2, S1 is S2 (True, True)

==

is 'spam' S1 >>> S1 >>> S2 >>> S1 (True,

S2

is

= 'a longer string' = 'a longer string' == S2, S1 is S2 False)

== is

>>> L1 = [1, ('a', 3)] >>> L2 = [1, ('a', 2)] >>> L1 < L2, L1 == L2, L1 > L2 (False, False, True)

L1

L2

3

2

"abc" < "ac" (key, value) (key, value) 1 < 'spam'

Comparisons, Equality, and Truth | 245

www.it-ebooks.info

Python 3.0 Dictionary Comparisons

C:\misc> c:\python26\python >>> D1 = {'a':1, 'b':2} >>> D2 = {'a':1, 'b':3} >>> D1 == D2 False >>> D1 < D2 True

items

sorted

C:\misc> c:\python30\python >>> D1 = {'a':1, 'b':2} >>> D2 = {'a':1, 'b':3} >>> D1 == D2 False >>> D1 < D2 TypeError: unorderable types: dict() < dict() >>> list(D1.items()) [('a', 1), ('b', 2)] >>> sorted(D1.items()) [('a', 1), ('b', 2)] >>> sorted(D1.items()) < sorted(D2.items()) True >>> sorted(D1.items()) > sorted(D2.items()) False

The Meaning of True and False in Python True

False 0

1

246 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

Object

Value

"spam"

True

""

False

[]

False

{}

False

1

True

0.0

False

None

False

if X:

X

if X != '': if

The None object None

None NULL

None >>> L = [None] * 100 >>> >>> L [None, None, None, None, None, None, None, ... ]

None

Comparisons, Equality, and Truth | 247

www.it-ebooks.info

None

None

The bool type bool True

False

1 1

True 1

0

0

False

0 True 1

False

0 if bool

>>> bool(1) True >>> bool('spam') True >>> bool({}) False

if

Python’s Type Hierarchies

248 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

Python’s Type Hierarchies | 249

www.it-ebooks.info

Type Objects type type(X)

X if

dict list str tuple int float complex bytes type set file open

types

isinstance type([1]) == type([]) type([1]) == list isinstance([1], list) import types def f(): pass type(f) == types.FunctionType

isinstance

type(X)

__class__

Other Types in Python

250 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

4 [1,2]

open

def

lambda re

re.compile()

Built-in Type Gotchas

Assignment Creates References, Not Copies L M

L

L M

>>> L = [1, 2, 3] >>> M = ['X', L, 'Y'] >>> M ['X', [1, 2, 3], 'Y'] >>> L[1] = 0 >>> M ['X', [1, 0, 3], 'Y']

>>> L = [1, 2, 3] >>> M = ['X', L[:], 'Y'] >>> L[1] = 0 >>> L [1, 0, 3] >>> M ['X', [1, 2, 3], 'Y']

Built-in Type Gotchas | 251

www.it-ebooks.info

Repetition Adds One Level Deep X

L

Y

L >>> L = [4, 5, 6] >>> X = L * 4 >>> Y = [L] * 4 >>> X [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6] >>> Y [[4, 5, 6], [4, 5, 6], [4, 5, 6], [4, 5, 6]]

L

Y L

>>> L[1] = 0 >>> X [4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6] >>> Y [[4, 0, 6], [4, 0, 6], [4, 0, 6], [4, 0, 6]]

Beware of Cyclic Data Structures [...] >>> L = ['grail'] >>> L.append(L) >>> L ['grail', [...]]

252 | Chapter 9: Tuples, Files, and Everything Else

www.it-ebooks.info

Immutable Types Can’t Be Changed In-Place

T = (1, 2, 3) T[2] = 4 T = T[:2] + (4,)

Chapter Summary open pickle

struct

Chapter Summary | 253

www.it-ebooks.info

Test Your Knowledge: Quiz (4, 5, 6) (1, 5, 6) open

Test Your Knowledge: Answers len

index

T = (4, 5, 6) T = (1,) + T[1:]

open pickle struct copy

copy.deepcopy(X) X aList[:] aDict.copy()

254 | Chapter 9: Tuples, Files, and Everything Else

'r'

www.it-ebooks.info

1

True 0

False

Test Your Knowledge: Part II Exercises

X=1;X X,Y,Z 2 ** 16 2 / 5, 2 / 5.0 "spam" + "eggs" S = "ham" "eggs " + S S * 5 S[:0] "green %s and %s" % ("eggs", S) 'green {0} and {1}'.format('eggs', S) ('x',)[0] ('x', 'y')[1] L = [1,2,3] + [4,5,6] L, L[:], L[:0], L[−2], L[−2:] ([1,2,3] + [4,5,6])[2:4] [L[2], L[3]] L.reverse(); L L.sort(); L L.index(4) {'a':1, 'b':2}['b'] D = {'x':1, 'y':2, 'z':3}

Test Your Knowledge: Part II Exercises | 255

www.it-ebooks.info

D['w'] = 0 D['x'] + D['w'] D[(1,2,3)] = 4 list(D.keys()), list(D.values()), (1,2,3) in D [[]], ["",[],(),{},None]

L L=[0,1,2,3]

L[4] L[−1000:100] L[3:1] L[3:1]=['?'] del

L L[2]=[]

L[2:3]=[] del del L[0] del L[1:]

L[1:2]=1

>>> X = 'spam' >>> Y = 'eggs' >>> X, Y = Y, X

X >>> D = {} >>> D[1] = 'a' >>> D[2] = 'b'

>>> D[(1, 2, 3)] = 'c' >>> D {1: 'a', 2: 'b', (1, 2, 3): 'c'}

256 | Chapter 9: Tuples, Files, and Everything Else

Y

www.it-ebooks.info

D 'b'

'a' D['d'] D['d']='spam'

'c' 'd'

+ +

+

+ append keys

append

S S[0][0][0][0][0]

S = "spam"

['s', 'p', 'a', 'm'] S

S = "spam" "slam"

"Hello file world!"

open write \n

Test Your Knowledge: Part II Exercises | 257

www.it-ebooks.info

www.it-ebooks.info

PART III

Statements and Syntax

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 10

Introducing Python Statements

Python Program Structure Revisited

261

www.it-ebooks.info

Python’s Statements break

continue

del

Statement

Role

Example

Assignment

Creating references

a, *b = 'good', 'bad', 'ugly'

Calls and other expressions

Running functions

log.write("spam, ham")

print calls

Printing objects

print('The Killer', joke)

if/elif/else

Selecting actions

if "python" in text: print(text)

for/else

Sequence iteration

for x in mylist: print(x)

while/else

General loops

while X > Y: print('hello')

pass

Empty placeholder

while True: pass

break

Loop exit

while True: if exittest(): break

continue

Loop continue

while True: if skiptest(): continue

def

Functions and methods

def f(a, b, c=1, *d): print(a+b+c+d[0])

return

Functions results

def f(a, b, c=1, *d): return a+b+c+d[0]

yield

Generator functions

def gen(n): for i in n: yield i*2

global

Namespaces

x = 'old' def function(): global x, y; x = 'new'

nonlocal

Namespaces (3.0+)

def outer(): x = 'old' def function(): nonlocal x; x = 'new'

import

Module access

import sys

from

Attribute access

from sys import stdin

class

Building objects

class Subclass(Superclass): staticData = [] def method(self): pass

262 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

Statement

Role

Example

try/except/ finally

Catching exceptions

try:

raise

Triggering exceptions

raise EndSearch(location)

assert

Debugging checks

assert X > Y, 'X too small'

with/as

Context managers (2.6+)

with open('data') as myfile: process(myfile)

del

Deleting references

del del del del

action() except: print('action error')

data[k] data[i:j] obj.attr variable

print

yield

print

yield

print

nonlocal print exec

try except

try finally except

finally

try with as from __future__ import with_statement

Python Program Structure Revisited | 263

www.it-ebooks.info

A Tale of Two ifs

if if (x > y) { x = 1; y = 2; }

if x > y: x = 1 y = 2

What Python Adds :

Header line: Nested statement block

264 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

What Python Removes

Parentheses are optional if (x < y)

if x < y

End of line is end of statement

x = 1;

x = 1

A Tale of Two ifs | 265

www.it-ebooks.info

End of indentation is end of block

begin end then endif if (x > y) { x = 1; y = 2; }

if x > y: x = 1 y = 2

Why Indentation Syntax?

266 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

while while (x > 0) {

while (x > 0) { --------; --------;

while (x > 0) { --------; --------; --------; --------;

while (x > 0) { --------; --------; --------; --------; --------; --------; }

}

if (x) if (y) statement1; else statement2;

A Tale of Two ifs | 267

www.it-ebooks.info

if

else if (y)

if

else

if (x)

if x:

if y:

statement1

else:

statement2

if

else if x

268 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

A Few Special Cases

Statement rule special cases

a = 1; b = 2; print(a + b)

print

()

[]

{}

mlist = [111, 222, 333]

A Tale of Two ifs | 269

www.it-ebooks.info

X = (A + B + C + D)

if (A == 1 and B == 2 and C == 3): print('spam' * 3)

X = A + B + \ C + D

Block rule special case

if x > y: print(x)

if

print else

if

270 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

if

A Quick Example: Interactive Loops

A Simple Interactive Loop

while True: reply = input('Enter text:') if reply == 'stop': break print(reply.upper())

while while while True input

if if

A Quick Example: Interactive Loops | 271

www.it-ebooks.info

break while

while while

Enter text:spam SPAM Enter text:42 42 Enter text:stop

raw_input print

input print

Doing Math on User Inputs

>>> reply = '20' >>> reply ** 2 ...error text omitted... TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

272 | Chapter 10: Introducing Python Statements

www.it-ebooks.info

>>> int(reply) ** 2 400

while True: reply = input('Enter text:') if reply == 'stop': break print(int(reply) ** 2) print('Bye')

if print

Enter text:2 4 Enter text:40 1600 Enter text:stop Bye

print

print

Handling Errors by Testing Inputs Enter text:xxx ...error text omitted... ValueError: invalid literal for int() with base 10: 'xxx'

int isdigit >>> S = '123' >>> T = 'xxx' >>> S.isdigit(), T.isdigit() (True, False)

if while True: reply = input('Enter text:')

A Quick Example: Interactive Loops | 273

www.it-ebooks.info

if reply == 'stop': break elif not reply.isdigit(): print('Bad!' * 8) else: print(int(reply) ** 2) print('Bye')

if if elif else else if elif

else

if

if if

Enter text:5 25 Enter text:xyz Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! Enter text:10 100 Enter text:stop

Handling Errors with try Statements try try while True: reply = input('Enter text:') if reply == 'stop': break try: num = int(reply) except: print('Bad!' * 8) else: print(int(reply) ** 2) print('Bye')

274 | Chapter 10: Introducing Python Statements

print while

www.it-ebooks.info

try

try

except

else try

try

except

else try except else if

try

else try

if try

else try else

try else

try

if

break try try

try float

Nesting Code Three Levels Deep

while True: reply = input('Enter text:') if reply == 'stop': break elif not reply.isdigit(): print('Bad!' * 8) else: num = int(reply) if num < 20: print('low') else: print(num ** 2) print('Bye')

A Quick Example: Interactive Loops | 275

www.it-ebooks.info

if

else while

Enter text:19 low Enter text:20 400 Enter text:spam Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! Enter text:stop Bye

Chapter Summary

Test Your Knowledge: Quiz

try

276 | Chapter 10: Introducing Python Statements

if

www.it-ebooks.info

Test Your Knowledge: Answers

try

Test Your Knowledge: Answers | 277

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 11

Assignments, Expressions, and Prints

Assignment Statements

279

www.it-ebooks.info

= for

Assignment Statement Forms

Operation

Interpretation

spam = 'Spam'

Basic form

spam, ham = 'yum', 'YUM'

Tuple assignment (positional)

[spam, ham] = ['yum', 'YUM']

List assignment (positional)

a, b, c, d = 'spam'

Sequence assignment, generalized

a, *b = 'spam'

Extended sequence unpacking (Python 3.0)

spam = ham = 'lunch'

Multiple-target assignment

spams += 42

Augmented assignment (equivalent to spams = spams + 42)

= spam

'yum'

'YUM'

a

's' b

280 | Chapter 11: Assignments, Expressions, and Prints

'p'

ham

www.it-ebooks.info

a

a 's'

b b

'pam'

spam ham = 'lunch'

'lunch' spam = ham

ham

ham

spam += 42

spam = spam + 42

Sequence Assignments % python >>> nudge = 1 >>> wink = 2 >>> A, B = nudge, wink >>> A, B (1, 2) >>> [C, D] = [nudge, wink] >>> C, D (1, 2)

Assignment Statements | 281

www.it-ebooks.info

>>> >>> >>> >>> (2,

nudge = 1 wink = 2 nudge, wink = wink, nudge nudge, wink 1)

>>> [a, b, c] = (1, 2, 3) >>> a, c (1, 3) >>> (a, b, c) = "ABC" >>> a, c ('A', 'C')

Advanced sequence assignment patterns =

>>> string = 'SPAM' >>> a, b, c, d = string >>> a, d ('S', 'M') >>> a, b, c = string ...error text omitted... ValueError: too many values to unpack

>>> a, b, c = string[0], string[1], string[2:] >>> a, b, c ('S', 'P', 'AM') >>> a, b, c = list(string[:2]) + [string[2:]] >>> a, b, c

282 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

('S', 'P', 'AM') >>> a, b = string[:2] >>> c = string[2:] >>> a, b, c ('S', 'P', 'AM') >>> (a, b), c = string[:2], string[2:] >>> a, b, c ('S', 'P', 'AM')

>>> ((a, b), c) = ('SP', 'AM') >>> a, b, c ('S', 'P', 'AM')

'SP'

(a, b) 'AM'

c

for for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: ... for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: ...

def f(((a, b), c)): f(((1, 2), 3))

>>> red, green, blue = range(3) >>> red, blue (0, 2)

0 1

2

range

Assignment Statements | 283

www.it-ebooks.info

>>> range(3) [0, 1, 2]

range

for

>>> L = [1, 2, 3, 4] >>> while L: ... front, L = L[0], L[1:] ... print(front, L) ... 1 [2, 3, 4] 2 [3, 4] 3 [4] 4 []

... ...

front = L[0] L = L[1:]

append

pop

front =

L.pop(0) while for

Extended Sequence Unpacking in Python 3.0 *X

Extended unpacking in action

C:\misc> c:\python30\python >>> seq = [1, 2, 3, 4] >>> a, b, c, d = seq >>> print(a, b, c, d) 1 2 3 4

284 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

>>> a, b = seq ValueError: too many values to unpack

a b >>> >>> 1 >>> [2,

a, *b = seq a b 3, 4]

b a >>> >>> [1, >>> 4

*a, b = seq a 2, 3] b

a

c

b >>> >>> 1 >>> [2, >>> 4

a, *b, c = seq a

>>> >>> 1 >>> 2 >>> [3,

a, b, *c = seq a

b 3] c

b c 4]

>>> a, *b = 'spam' >>> a, b ('s', ['p', 'a', 'm']) >>> a, *b, c = 'spam'

Assignment Statements | 285

www.it-ebooks.info

>>> a, b, c ('s', ['p', 'a'], 'm')

>>> S = 'spam' >>> S[0], S[1:] ('s', 'pam') >>> S[0], S[1:3], S[3] ('s', 'pa', 'm')

>>> L = [1, 2, 3, 4] >>> while L: ... front, *L = L ... print(front, L) ... 1 [2, 3, 4] 2 [3, 4] 3 [4] 4 []

Boundary cases

>>> seq [1, 2, 3, 4] >>> a, b, c, *d = seq >>> print(a, b, c, d) 1 2 3 [4]

a b c e >>> a, b, c, d, *e = seq >>> print(a, b, c, d, e) 1 2 3 4 [] >>> a, b, *e, c, d = seq >>> print(a, b, c, d, e) 1 2 3 4 []

286 | Chapter 11: Assignments, Expressions, and Prints

d

www.it-ebooks.info

>>> a, *b, c, *d = seq SyntaxError: two starred expressions in assignment >>> a, b = seq ValueError: too many values to unpack >>> *a = seq SyntaxError: starred assignment target must be in a list or tuple >>> *a, = seq >>> a [1, 2, 3, 4]

A useful convenience

>>> seq [1, 2, 3, 4] >>> a, *b = seq >>> a, b (1, [2, 3, 4]) >>> a, b = seq[0], seq[1:] >>> a, b (1, [2, 3, 4])

>>> *a, b = seq >>> a, b ([1, 2, 3], 4) >>> a, b = seq[:-1], seq[-1] >>> a, b ([1, 2, 3], 4)

Assignment Statements | 287

www.it-ebooks.info

Application to for loops for for for for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]: ...

a, *b, c = (1, 2, 3, 4)

a b

# b gets [2, 3]

c

for (a, b, c) in [(1, 2, 3), (4, 5, 6)]:

# a, b, c = (1, 2, 3), ...

for all in [(1, 2, 3, 4), (5, 6, 7, 8)]: a, b, c = all[0], all[1:3], all[3]

for

Multiple-Target Assignments a b 'spam' >>> a = b = c = 'spam' >>> a, b, c ('spam', 'spam', 'spam')

>>> c = 'spam' >>> b = c >>> a = b

Multiple-target assignment and shared references

288 | Chapter 11: Assignments, Expressions, and Prints

c

www.it-ebooks.info

>>> >>> >>> (0,

a = b = 0 b = b + 1 a, b 1)

b

b

>>> a = b = [] >>> b.append(42) >>> a, b ([42], [42])

a

b

b

a

>>> a = [] >>> b = [] >>> b.append(42) >>> a, b ([], [42])

Augmented Assignments

X = X + Y X += Y

X += Y

X &= Y

X -= Y

X |= Y

X *= Y

X ^= Y

X /= Y

X >>= Y

X %= Y

X > >>> >>> 2 >>> >>> 3

x = 1 x = x + 1 x x += 1 x

S = S + "SPAM" >>> S = "spam" >>> S += "SPAM" >>> S 'spamSPAM'

X *= Y

X >>= Y

X //= Y

X += Y X X = X + Y X

append >>> >>> >>> [1, >>> >>> [1,

L = [1, 2] L = L + [3] L 2, 3] L.append(4) L 2, 3, 4]

X += Y X++ −−X

290 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

extend >>> >>> [1, >>> >>> [1,

L = L + [5, 6] L 2, 3, 4, 5, 6] L.extend([7, 8]) L 2, 3, 4, 5, 6, 7, 8]

extend + >>> L += [9, 10] >>> L [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Augmented assignment and shared references += +

>>> L = [1, 2] >>> M = L >>> L = L + [3, 4] >>> L, M ([1, 2, 3, 4], [1, 2]) >>> L = [1, 2] >>> M = L >>> L += [3, 4] >>> L, M ([1, 2, 3, 4], [1, 2, 3, 4])

L[len(L):] = [11,12,13] extend

Assignment Statements | 291

www.it-ebooks.info

Variable Name Rules

_spam spam 1_Spam spam$

Spam_1

@#!

SPAM

spam X

x

class klass

finally

is

Class

False

class

return

None

continue

for

lambda

try

True

def

from

nonlocal

while

and

del

global

not

with

as

elif

if

or

yield

assert

else

import

pass

break

except

in

raise

print exec nonlocal

292 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

with

as

yield yield

and = 1 True False None

import

Python’s Deprecation Protocol

yield yield

Assignment Statements | 293

www.it-ebooks.info

with

as

from__future__

with

as

Naming conventions

__name__

_X

from module

import * __X__ __X

_

self open = 42

Names have no type, but objects do

294 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

>>> x = 0 >>> x = "Hello" >>> x = [1, 2, 3]

Expression Statements

const _X

__X

Expression Statements | 295

www.it-ebooks.info

print

Operation

Interpretation

spam(eggs, ham)

Function calls

spam.ham(eggs)

Method calls

spam

Printing variables in the interactive interpreter

print(a, b, c, sep='')

Printing operations in Python 3.0

yield x ** 2

Yielding expression statements

yield print None >>> x = print('spam') spam >>> print(x) None

= = == while

Expression Statements and In-Place Changes >>> >>> >>> [1,

L = [1, 2] L.append(3) L 2, 3]

296 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

L >>> L = L.append(4) >>> print(L) None

append sort

reverse None

Print Operations print

print

file.write(str) print

stdout

stdout

sys

sys.stdout print

stdout print

Print Operations | 297

www.it-ebooks.info

The Python 3.0 print Function print None

print

Call format print print([object, ...][, sep=' '][, end='\n'][, file=sys.stdout])

= object end sep end

sep

file

file

print

298 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

sep end

\n print

file sys.stdout write(string) object str print

The 3.0 print function in action

C:\misc> c:\python30\python >>> >>> print() >>> x = 'spam' >>> y = 99 >>> z = ['eggs'] >>> >>> print(x, y, z) spam 99 ['eggs']

print sep >>> print(x, y, z, sep='') spam99['eggs'] >>> >>> print(x, y, z, sep=', ') spam, 99, ['eggs']

str str

Print Operations | 299

www.it-ebooks.info

print end \n >>> print(x, y, z, end='') spam 99 ['eggs']>>> >>> >>> print(x, y, z, end=''); print(x, y, z) spam 99 ['eggs']spam 99 ['eggs'] >>> print(x, y, z, end='...\n') spam 99 ['eggs']... >>>

>>> print(x, y, z, sep='...', end='!\n') spam...99...['eggs']! >>> print(x, y, z, end='!\n', sep='...') spam...99...['eggs']!

file print >>> print(x, y, z, sep='...', file=open('data.txt', 'w')) >>> print(x, y, z) spam 99 ['eggs'] >>> print(open('data.txt').read()) spam...99...['eggs']

print >>> text = '%s: %-.4f, %05d' % ('Result', 3.14159, 42) >>> print(text) Result: 3.1416, 00042 >>> print('%s: %-.4f, %05d' % ('Result', 3.14159, 42)) Result: 3.1416, 00042

print print

The Python 2.6 print Statement

300 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

print print

Statement forms print print print >> sys.stdout

Python 2.6 statement

Python 3.0 equivalent

Interpretation

print x, y

print(x, y)

Print objects’ textual forms to sys.stdout; add a space between the items and an end-of-line at the end

print x, y,

print(x, y, end='')

Same, but don’t add end-of-line at end of text

print >> afile, x, y

print(x, y, file=afile)

Send text to myfile.write, not to sys.stdout.write

The 2.6 print statement in action print print C:\misc> c:\python26\python >>> >>> x = 'a' >>> y = 'b' >>> print x, y a b

print

>>> print x, y,; print x, y a b a b

Print Operations | 301

www.it-ebooks.info

>>> print x + y ab >>> print '%s...%s' % (x, y) a...b

print print

Print Stream Redirection

The Python “hello world” program

>>> print('hello world') hello world >>> print 'hello world' hello world

print >>> 'hello world' 'hello world'

print sys.stdout

>>> import sys >>> sys.stdout.write('hello world\n') hello world

302 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

write

sys.stdout print

Manual stream redirection sys.stdout print sys.stdout print(X, Y)

import sys sys.stdout.write(str(X) + ' ' + str(Y) + '\n')

str +

write

print sys.stdout print import sys sys.stdout = open('log.txt', 'a') ... print(x, y, x)

sys.stdout print print sys.stdout

sys.stdout write sys print

sys.stdout

print

stdout

sys.stdout write write

print print

Print Operations | 303

www.it-ebooks.info

sys.stdout print

Automatic stream redirection sys.stdout

sys.stdout C:\misc> c:\python30\python >>> import sys >>> temp = sys.stdout >>> sys.stdout = open('log.txt', 'a') >>> print('spam') >>> print(1, 2, 3) >>> sys.stdout.close() >>> sys.stdout = temp >>> print('back here') back here >>> print(open('log.txt').read()) spam 1 2 3

print file

print

write

sys.stdout print

print >>

log = open('log.txt', 'a') print(x, y, z, file=log) print(a, b, c) log = open('log.txt', 'a') print >> log, x, y, z print a, b, c

print

__stdout__

sys

sys.stdout sys.__stdout__

304 | Chapter 11: Assignments, Expressions, and Prints

sys.stdout sys

www.it-ebooks.info

write C:\misc> c:\python30\python >>> log = open('log.txt', 'w') >>> print(1, 2, 3, file=log) >>> print(4, 5, 6, file=log) >>> log.close() >>> print(7, 8, 9) 7 8 9 >>> print(open('log.txt').read()) 1 2 3 4 5 6

print sys.stderr

write

>>> import sys >>> sys.stderr.write(('Bad!' * 8) + '\n') Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad! >>> print('Bad!' * 8, file=sys.stderr) Bad!Bad!Bad!Bad!Bad!Bad!Bad!Bad!

write >>> >>> 1 2 >>> >>> 1 2 4 >>>

X = 1; Y = 2 print(X, Y)

>>> 4 >>> b'1 >>> b'1

open('temp2', 'w').write(str(X) + ' ' + str(Y) + '\n')

import sys sys.stdout.write(str(X) + ' ' + str(Y) + '\n') print(X, Y, file=open('temp1', 'w'))

print(open('temp1', 'rb').read()) 2\r\n' print(open('temp2', 'rb').read()) 2\r\n'

print print

Print Operations | 305

www.it-ebooks.info

Version-Neutral Printing print 2to3

print from __future__ import print_function

print

print

C:\misc> c:\python30\python >>> print('spam') spam >>> print('spam', 'ham', 'eggs') spam ham eggs

C:\misc> c:\python26\python >>> print('spam') spam >>> print('spam', 'ham', 'eggs') ('spam', 'ham', 'eggs')

>>> print('%s %s %s' % ('spam', 'ham', 'eggs')) spam ham eggs >>> print('{0} {1} {2}'.format('spam', 'ham', 'eggs')) spam ham eggs

306 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

print

print

Why You Will Care: print and stdout print sys.stdout write sys.stdout.write sys.stdout

sys.stdout print

write write

class FileFaker: def write(self, string): # Do something with printed text in string import sys sys.stdout = FileFaker() print(someObjects)

print sys.stdout write

file >>

print

sys.stdout

stdout

myobj = FileFaker() print(someObjects, file=myobj) myobj = FileFaker() print >> myobj, someObjects

input

sys.stdin read

input

while stdout

Print Operations | 307

www.it-ebooks.info

python script.py < inputfile > outputfile python script.py | filterProgram

Chapter Summary print

append

if

Test Your Knowledge: Quiz L = L.sort() print

Test Your Knowledge: Answers A = B = C = 0 A, B, C = 0, 0, 0 A = 0 B = 0 C = 0 A = 0; B = 0; C = 0

308 | Chapter 11: Assignments, Expressions, and Prints

www.it-ebooks.info

A = B = C = []

A.append(99)

sort

append None

L

L

None sorted

sys.stdout

print print >> file, X print

print(X, file=F)

Test Your Knowledge: Answers | 309

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 12

if Tests and Syntax Rules

if

if

if Statements if if if

General Format if

if if else

elif else if

else

if

if : elif : else:

311

www.it-ebooks.info

Basic Examples if if >>> if 1: ... print('true') ... true

...

1 else >>> if not 1: ... print('true') ... else: ... print('false') ... false

Multiway Branching if >>> >>> ... ... ... ... ... ... Run

x = 'killer rabbit' if x == 'roger': print("how's jessica?") elif x == 'bugs': print("what's up doc?") else: print('Run away! Run away!') away! Run away!

if

else

else else

elif if elif

switch

else

case if elif if

312 | Chapter 12: if Tests and Syntax Rules

www.it-ebooks.info

>>> choice = 'ham' >>> print({'spam': ... 'ham': ... 'eggs': ... 'bacon': 1.99

1.25, 1.99, 0.99, 1.10}[choice])

choice switch

if

>>> if choice == 'spam': ... print(1.25) ... elif choice == 'ham': ... print(1.99) ... elif choice == 'eggs': ... print(0.99) ... elif choice == 'bacon': ... print(1.10) ... else: ... print('Bad choice') ... 1.99

else

if in

get get

>>> branch = {'spam': 1.25, ... 'ham': 1.99, ... 'eggs': 0.99} >>> print(branch.get('spam', 'Bad choice')) 1.25 >>> print(branch.get('bacon', 'Bad choice')) Bad choice

in >>> >>> ... ... ... ... Bad

if choice = 'bacon' if choice in branch: print(branch[choice]) else: print('Bad choice') choice

if

if Statements | 313

www.it-ebooks.info

lambda

if

Python Syntax Rules if

if if

if

elif

else

#

#

314 | Chapter 12: if Tests and Syntax Rules

if

www.it-ebooks.info

Block Delimiters: Indentation Rules

x = 1 if x: y = 2 if y: print('block2') print('block1') print('block0')

Python Syntax Rules | 315

www.it-ebooks.info

if print

x = 'SPAM' if 'rubbery' in 'shrubbery': print(x * 8) x += 'NI' if x.endswith('NI'): x *= 2 print(x)

x = 'SPAM' if 'rubbery' in 'shrubbery': print(x * 8) x += 'NI' if x.endswith('NI'): x *= 2 print(x)

316 | Chapter 12: if Tests and Syntax Rules

if

www.it-ebooks.info

Avoid mixing tabs and spaces: New error checking in 3.0

-t -tt python –t main.py -tt

Statement Delimiters: Lines and Continuations

() {}

) }

[]

]

\

\

Python Syntax Rules | 317

www.it-ebooks.info

#

A Few Special Cases L = ["Good", "Bad", "Ugly"]

if a == b and c == d and d == e and f == g: print('olde')

\

if (a == b and c == d and d == e and e == f): print('new')

x

10 x

+4

318 | Chapter 12: if Tests and Syntax Rules

6

www.it-ebooks.info

x = 1 + 2 + 3 \ +4

x = 1; y = 2; print(x)

+

\ncccc'

S

S '\naaaa\nbbbb 'aaaabbbbcccc'

S = """ aaaa bbbb cccc""" S = ('aaaa' 'bbbb' 'cccc')

if if 1: print('hello')

Python Syntax Rules | 319

www.it-ebooks.info

Truth Tests if

None True and

False

1

0

or

X and Y X

Y

X or Y X

Y

not X X X

True

False

Y && ||

!

and True

False

True

False

or

>>> 2 < 3, 3 < 2 (True, False)

1 0 and

or if True

320 | Chapter 12: if Tests and Syntax Rules

False

www.it-ebooks.info

or

>>> (2, >>> 3 >>> {}

2 or 3, 3 or 2 3) [] or 3 [] or {}

2

3

and >>> (3, >>> [] >>> []

2 and 3, 3 and 2 2) [] and {} 3 and []

[] 3 [] if and

while

or

The if/else Ternary Expression if A

Y

Z

X

The if/else Ternary Expression | 321

www.it-ebooks.info

if X: A = Y else: A = Z

A = Y if X else Z

if Y X

Z

>>> >>> 't' >>> >>> 'f'

X

A = 't' if 'spam' else 'f' A A = 't' if '' else 'f' A

and

or

A = ((X and Y) or Z)

Y and or

Z

X

Y

Y Z

X

and or Y if X else Z if bool X

1

0

A = [Z, Y][bool(X)]

X if Y else Z

322 | Chapter 12: if Tests and Syntax Rules

Y ? X : Z

www.it-ebooks.info

>>> ['f', 't'][bool('')] 'f' >>> ['f', 't'][bool('spam')] 't'

Z

Y

X if else if

and or

Why You Will Care: Booleans or X = A or B or C or None

X

A B

C

None

or or X A

A

default

X = A or default

if f1() or f2(): ...

f1

f2 or

tmp1, tmp2 = f1(), f2() if tmp1 or tmp2: ...

((A and B) or C)

if

else

if X:

if X != '': True

False

1

0

The if/else Ternary Expression | 323

www.it-ebooks.info

X = False

while True:

__bool__ __len__

__bool__

__nonzero__

Chapter Summary if

if else while

for

Test Your Knowledge: Quiz if else True

False

Test Your Knowledge: Answers if

elif

def

lambda Y if X else Z

Z ((X and Y) or Z) Y

324 | Chapter 12: if Tests and Syntax Rules

if

Y and or

X

www.it-ebooks.info

() []

True

False

{}

1

0

Test Your Knowledge: Answers | 325

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 13

while and for Loops

while for

break range zip while

continue map for

for for filter

reduce

while Loops while

while

327

www.it-ebooks.info

General Format while else break

while : else:

Examples while print

while

True

>>> while True: ... print('Type Ctrl-C to stop me!')

while x != '': for >>> x = 'spam' >>> while x: ... print(x, end=' ') ... x = x[1:] ... spam pam am m

end=' ' a for

b range

>>> a=0; b=10 >>> while a < b: ... print(a, end=' ') ... a += 1 ... 0 1 2 3 4 5 6 7 8 9

break

328 | Chapter 13: while and for Loops

www.it-ebooks.info

while True: ...loop body... if exitTest(): break

break

break, continue, pass, and the Loop else break else

break pass

break continue pass Loop else block break

General Loop Format break

continue

while

while : if : break if : continue else:

break

continue

while

for

if

break, continue, pass, and the Loop else | 329

www.it-ebooks.info

pass pass

pass while True: pass

pass None while

if

pass pass try

class pass

def func1(): pass def func2(): pass

pass

... pass def func1(): ... def func2(): ... func1()

def func1(): ... def func2(): ... >>> X = ...

330 | Chapter 13: while and for Loops

www.it-ebooks.info

>>> X Ellipsis

... pass

None

continue continue continue % 8 6 4 2 0 x = 10 while x: x = x−1 if x % 2 != 0: continue print(x, end=' ')

continue if

print print

continue

continue continue print

if

x = 10 while x: x = x−1 if x % 2 == 0: print(x, end=' ')

break break break break input raw_input >>> while True: ... name = input('Enter name:') ... if name == 'stop': break ... age = input('Enter age: ') ... print('Hello', name, '=>', int(age) ** 2) ... Enter name:mel Enter age: 40

break, continue, pass, and the Loop else | 331

www.it-ebooks.info

Hello Enter Enter Hello Enter

mel => 1600 name:bob age: 30 bob => 900 name:stop

age

int input

input input

try

Loop else else

break y

x = y // 2 while x > 1: if y % x == 0: print(y, 'has factor', x) break x -= 1 else: print(y, 'is prime')

break else break else break

while

x

y

// /

332 | Chapter 13: while and for Loops

/

www.it-ebooks.info

More on the loop else else else

found = False while x and not found: if match(x[0]): print('Ni') found = True else: x = x[1:] if not found: print('not found')

else

else

while x: if match(x[0]): print('Ni') break x = x[1:] else: print('Not found')

else

if break

while while

else else x

if not x:

else else for

break, continue, pass, and the Loop else | 333

www.it-ebooks.info

Why You Will Care: Emulating C while Loops

while ((x = next()) != NULL) {...process x...}

=

== while break

while True: x = next() if not x: break ...process x...

x = True while x: x = next() if x: ...process x...

x = next() while x: ...process x... x = next()

for

for Loops for for

General Format for

334 | Chapter 13: while and for Loops

www.it-ebooks.info

for in : else:

for

for for

break for while

else break for

break continue while for

for in : if : break if : continue else:

Examples for

Basic usage for x print print

x

>>> for x in ["spam", "eggs", "ham"]: ... print(x, end=' ') ... spam eggs ham

+ *

for

for Loops | 335

www.it-ebooks.info

>>> >>> ... ... >>> 10 >>> >>> ... >>> 24

sum = 0 for x in [1, 2, 3, 4]: sum = sum + x sum prod = 1 for item in [1, 2, 3, 4]: prod *= item prod

Other data types for

for

>>> S = "lumberjack" >>> T = ("and", "I'm", "okay") >>> for x in S: print(x, end=' ') ... l u m b e r j a c k >>> for x in T: print(x, end=' ') ... and I'm okay

for

Tuple assignment in for loops

for >>> T = [(1, 2), (3, 4), (5, 6)] >>> for (a, b) in T: ... print(a, b) ... 1 2 3 4 5 6

(a,b) = (1,2) (a,b) = (3,4) zip

336 | Chapter 13: while and for Loops

www.it-ebooks.info

for items >>> D = {'a': 1, 'b': 2, 'c': 3} >>> for key in D: ... print(key, '=>', D[key]) ... a => 1 c => 3 b => 2 >>> list(D.items()) [('a', 1), ('c', 3), ('b', 2)] >>> for (key, value) in D.items(): ... print(key, '=>', value) ... a => 1 c => 3 b => 2

for for >>> T [(1, 2), (3, 4), (5, 6)] >>> for both in T: ... a, b = both ... print(a, b) ... 1 2 3 4 5 6

for >>> ((a, b), c) = ((1, 2), 3) >>> (1, 2, 3) >>> for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]: print(a, b, c) ... 1 2 3 4 5 6

for Loops | 337

www.it-ebooks.info

for

>>> for ((a, b), c) in [([1, 2], 3), ['XY', 6]]: print(a, b, c) ... 1 2 3 X Y 6

Python 3.0 extended sequence assignment in for loops for

for

>>> a, b, c = (1, 2, 3) >>> a, b, c (1, 2, 3) >>> for (a, b, c) in [(1, 2, 3), (4, 5, 6)]: ... print(a, b, c) ... 1 2 3 4 5 6

for >>> a, *b, c = (1, 2, 3, 4) >>> a, b, c (1, [2, 3], 4) >>> for (a, *b, c) in [(1, 2, 3, 4), (5, 6, 7, 8)]: ... print(a, b, c) ... 1 [2, 3] 4 5 [6, 7] 8

>>> for all in [(1, 2, 3, 4), (5, 6, 7, 8)]: ... a, b, c = all[0], all[1:3], all[3] ... print(a, b, c)

338 | Chapter 13: while and for Loops

www.it-ebooks.info

... 1 (2, 3) 4 5 (6, 7) 8

Nested for loops for else items

for

tests

>>> items = ["aaa", 111, (4, 5), 2.01] >>> tests = [(4, 5), 3.14] >>> >>> for key in tests: ... for item in items: ... if item == key: ... print(key, "was found") ... break ... else: ... print(key, "not found!") ... (4, 5) was found 3.14 not found!

if

break

else

else for if

for in

in >>> for key in tests: ... if key in items: ... print(key, "was found") ... else: ... print(key, "not found!") ... (4, 5) was found 3.14 not found!

for res

seq1

seq2

for Loops | 339

www.it-ebooks.info

>>> seq1 = "spam" >>> seq2 = "scam" >>> >>> res = [] >>> for x in seq1: ... if x in seq2: ... res.append(x) ... >>> res ['s', 'a', 'm']

seq1 seq2

Why You Will Care: File Scanners

read file = open('test.txt', 'r') print(file.read())

while for file = open('test.txt') while True: char = file.read(1) if not char: break print(char) for char in open('test.txt').read(): print(char)

for while file = open('test.txt') while True: line = file.readline() if not line: break print(line, end='') file = open('test.txt', 'rb') while True: chunk = file.read(10) if not chunk: break print(chunk)

340 | Chapter 13: while and for Loops

www.it-ebooks.info

for for line in open('test.txt').readlines(): print(line, end='') for line in open('test.txt'): print(line, end='')

readlines

open

file

file

xreadlines readlines xreadlines

Loop Coding Techniques for while

for while for range for zip for for

while for

Loop Coding Techniques | 341

www.it-ebooks.info

Counter Loops: while and range range for range list >>> list(range(5)), list(range(2, 5)), list(range(0, 10, 2)) ([0, 1, 2, 3, 4], [2, 3, 4], [0, 2, 4, 6, 8])

range

>>> list(range(−5, 5)) [−5, −4, −3, −2, −1, 0, 1, 2, 3, 4] >>> list(range(5, −5, −1)) [5, 4, 3, 2, 1, 0, −1, −2, −3, −4]

range for for

range range

list >>> for i in range(3): ... print(i, 'Pythons') ... 0 Pythons 1 Pythons 2 Pythons

range for >>> X = 'spam' >>> for item in X: print(item, end=' ') ... s p a m

for while >>> i = 0 >>> while i < len(X): ... print(X[i], end=' ') ... i += 1

342 | Chapter 13: while and for Loops

www.it-ebooks.info

... s p a m

for

range

>>> X 'spam' >>> len(X) 4 >>> list(range(len(X))) [0, 1, 2, 3] >>> >>> for i in range(len(X)): print(X[i], end=' ') ... s p a m

X X

X

Nonexhaustive Traversals: range and Slices for

for

while

range

for

>>> for item in X: print(item) ...

>>> S = 'abcdefghijk' >>> list(range(0, len(S), 2)) [0, 2, 4, 6, 8, 10] >>> for i in range(0, len(S), 2): print(S[i], end=' ') ... a c e g i k

S

range range

3

range for

S

Loop Coding Techniques | 343

www.it-ebooks.info

>>> S = 'abcdefghijk' >>> for c in S[::2]: print(c, end=' ') ... a c e g i k

range

Changing Lists: range range

for

for >>> L = [1, 2, 3, 4, 5] >>> ... ... >>> [1, >>> 6

for x in L: x += 1 L 2, 3, 4, 5] x

x

L

x x x

1

2

1

range len >>> L = [1, 2, 3, 4, 5] >>> for i in range(len(L)): ... L[i] += 1 ... >>> L [2, 3, 4, 5, 6]

for x in L: while >>> i = 0 >>> while i < len(L): ... L[i] += 1

344 | Chapter 13: while and for Loops

www.it-ebooks.info

... i += 1 ... >>> L [3, 4, 5, 6, 7]

range [x+1 for x in L]

L

Parallel Traversals: zip and map range

for zip

for zip

>>> L1 = [1,2,3,4] >>> L2 = [5,6,7,8]

zip range zip

list

>>> zip(L1, L2) >>> list(zip(L1, L2)) [(1, 5), (2, 6), (3, 7), (4, 8)]

for >>> ... ... 1 5 2 6 3 7 4 8

for (x, y) in zip(L1, L2): print(x, y, '--', x+y) -----

6 8 10 12

zip for zip (x, y) = (1, 5)

Loop Coding Techniques | 345

www.it-ebooks.info

L1

L2

while for zip zip

>>> T1, T2, T3 = (1,2,3), (4,5,6), (7,8,9) >>> T3 (7, 8, 9) >>> list(zip(T1, T2, T3)) [(1, 4, 7), (2, 5, 8), (3, 6, 9)]

zip

>>> S1 = 'abc' >>> S2 = 'xyz123' >>> >>> list(zip(S1, S2)) [('a', 'x'), ('b', 'y'), ('c', 'z')]

map equivalence in Python 2.6 map None >>> S1 = 'abc' >>> S2 = 'xyz123' >>> map(None, S1, S2) [('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None,'3')]

map map map ord map

zip list

>>> list(map(ord, 'spam')) [115, 112, 97, 109]

346 | Chapter 13: while and for Loops

www.it-ebooks.info

>>> res = [] >>> for c in 'spam': res.append(ord(c)) >>> res [115, 112, 97, 109]

map None zip

map zip

Dictionary construction with zip zip zip

>>> D1 = {'spam':1, 'eggs':3, 'toast':5} >>> D1 {'toast': 5, 'eggs': 3, 'spam': 1} >>> >>> >>> >>>

D1 = {} D1['spam'] = 1 D1['eggs'] = 3 D1['toast'] = 5

>>> keys = ['spam', 'eggs', 'toast'] >>> vals = [1, 3, 5]

zip for >>> list(zip(keys, vals)) [('spam', 1), ('eggs', 3), ('toast', 5)] >>> D2 = {} >>> for (k, v) in zip(keys, vals): D2[k] = v ... >>> D2 {'toast': 5, 'eggs': 3, 'spam': 1}

Loop Coding Techniques | 347

www.it-ebooks.info

for dict >>> keys = ['spam', 'eggs', 'toast'] >>> vals = [1, 3, 5] >>> D3 = dict(zip(keys, vals)) >>> D3 {'toast': 5, 'eggs': 3, 'spam': 1}

dict

dict

Generating Both Offsets and Items: enumerate range for >>> S = 'spam' >>> offset = 0 >>> for item in S: ... print(item, ... offset += 1 ... s appears at offset p appears at offset a appears at offset m appears at offset

'appears at offset', offset) 0 1 2 3

enumerate >>> S = 'spam' >>> for (offset, item) in enumerate(S): ... print(item, 'appears at offset', offset) ... s appears at offset 0 p appears at offset 1 a appears at offset 2 m appears at offset 3

enumerate __next__

next

for

zip

(index, value)

348 | Chapter 13: while and for Loops

www.it-ebooks.info

>>> E = enumerate(S) >>> E >>> next(E) (0, 's') >>> next(E) (1, 'p') >>> next(E) (2, 'a')

>>> [c * i for (i, c) in enumerate(S)] ['', 'p', 'aa', 'mmm']

enumerate zip

Chapter Summary while

for

else

break

continue for

range zip map

enumerate

for range zip

Test Your Knowledge: Quiz while break

for

continue

else range

for

Test Your Knowledge: Quiz | 349

www.it-ebooks.info

Test Your Knowledge: Answers while

for while

for break while for else

continue while while

for

for break

break

else while for

range for

range range

for range

350 | Chapter 13: while and for Loops

www.it-ebooks.info

CHAPTER 14

Iterations and Comprehensions, Part 1

while

for

for for for

Iterators: A First Look for >>> for x in [1, 2, 3, 4]: print(x ** 2, end=' ') ... 1 4 9 16 >>> for x in (1, 2, 3, 4): print(x ** 3, end=' ') ... 1 8 27 64

351

www.it-ebooks.info

>>> for x in 'spam': print(x * 2, end=' ') ... ss pp aa mm

for for map

in

for

The Iteration Protocol: File Iterators readline readline >>> f = open('script1.py') >>> f.readline() 'import sys\n' >>> f.readline() 'print(sys.path)\n' >>> f.readline() 'x = 2\n' >>> f.readline() 'print(2 ** 33)\n' >>> f.readline() ''

__next__ __next__

StopIteration

>>> f = open('script1.py') >>> f.__next__() 'import sys\n' >>> f.__next__() 'print(sys.path)\n'

iter

352 | Chapter 14: Iterations and Comprehensions, Part 1

iter

next(I)

www.it-ebooks.info

>>> f.__next__() 'x = 2\n' >>> f.__next__() 'print(2 ** 33)\n' >>> f.__next__() Traceback (most recent call last): ...more exception text omitted... StopIteration

__next__

StopIteration for __next__

StopIteration for __next__

>>> for line in open('script1.py'): ... print(line.upper(), end='') ... IMPORT SYS PRINT(SYS.PATH) X = 2 PRINT(2 ** 33)

print

end=''

\n

for

readlines

>>> for line in open('script1.py').readlines(): ... print(line.upper(), end='') ... IMPORT SYS PRINT(SYS.PATH) X = 2 PRINT(2 ** 33)

readlines

Iterators: A First Look | 353

www.it-ebooks.info

while >>> f = open('script1.py') >>> while True: ... line = f.readline() ... if not line: break ... print(line.upper(), end='') ... ...same output...

for while

Manual Iteration: iter and next next X

next(X)

__next__ X.__next__()

>>> f = open('script1.py') >>> f.__next__() 'import sys\n' >>> f.__next__() 'print(sys.path)\n' >>> f = open('script1.py') >>> next(f) 'import sys\n' >>> next(f) 'print(sys.path)\n'

for iter iter for >>> >>> >>> 1 >>> 2

L = [1, 2, 3] I = iter(L) I.next() I.next()

354 | Chapter 14: Iterations and Comprehensions, Part 1

next

www.it-ebooks.info

>>> I.next() 3 >>> I.next() Traceback (most recent call last): ...more omitted... StopIteration

__next__ >>> f = open('script1.py') >>> iter(f) is f True >>> f.__next__() 'import sys\n'

iter >>> L = [1, 2, 3] >>> iter(L) is L False >>> L.__next__() AttributeError: 'list' object has no attribute '__next__' >>> I = iter(L) >>> I.__next__() 1 >>> next(I) 2

>>> L = [1, 2, 3] >>> >>> for X in L: ... print(X ** 2, end=' ') ... 1 4 9 >>> I = iter(L)

for

I.__next__

next(I)

os.popen for os.popen __getattr__

subprocess __next__

next os.popen2/3/4

subprocess.Popen

Iterators: A First Look | 355

www.it-ebooks.info

>>> while True: ... try: ... X = next(I) ... except StopIteration: ... break ... print(X ** 2, end=' ') ... 1 4 9

try for

X.next()

X.__next__()

X.next()

X.__next__()

X.next()

X.next() X.__next__() next(X)

Other Built-in Type Iterators

>>> D = {'a':1, 'b':2, 'c':3} >>> for key in D.keys(): ... print(key, D[key]) ... a 1 c 3 b 2

>>> I = iter(D) >>> next(I) 'a' >>> next(I) 'c' >>> next(I) 'b' >>> next(I) Traceback (most recent call last):

356 | Chapter 14: Iterations and Comprehensions, Part 1

next(X)

next(X)

www.it-ebooks.info

...more omitted... StopIteration

keys for >>> for key in D: ... print(key, D[key]) ... a 1 c 3 b 2

for os.popen >>> import os >>> P = os.popen('dir') >>> P.__next__() ' Volume in drive C is SQ004828V03\n' >>> P.__next__() ' Volume Serial Number is 08BE-3CD4\n' >>> next(P) TypeError: _wrap_close object is not an iterator

popen P.__next__()

P.next() next(P)

for

list >>> R = range(5) >>> R range(0, 5) >>> I = iter(R) >>> next(I) 0 >>> next(I) 1 >>> list(range(5)) [0, 1, 2, 3, 4]

Iterators: A First Look | 357

www.it-ebooks.info

enumerate >>> E = enumerate('spam') >>> E >>> I = iter(E) >>> next(I) (0, 's') >>> next(I) (1, 'p') >>> list(enumerate('spam')) [(0, 's'), (1, 'p'), (2, 'a'), (3, 'm')]

for

List Comprehensions: A First Look for range >>> L = [1, 2, 3, 4, 5] >>> for i in range(len(L)): ... L[i] += 10 ... >>> L [11, 12, 13, 14, 15]

>>> L = [x + 10 for x in L] >>> L [21, 22, 23, 24, 25]

for

358 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

List Comprehension Basics

for >>> L = [x + 10 for x in L]

x + 10 for for x in L L x x + 10

x

L

for >>> res = [] >>> for x in L: ... res.append(x + 10) ... >>> res [21, 22, 23, 24, 25]

for

Using List Comprehensions on Files readlines >>> f = open('script1.py') >>> lines = f.readlines()

List Comprehensions: A First Look | 359

www.it-ebooks.info

>>> lines ['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n']

\n

lines rstrip line[:−1] >>> lines = [line.rstrip() for line in lines] >>> lines ['import sys', 'print(sys.path)', 'x = 2', 'print(2 ** 33)']

for

next

rstrip rstrip

>>> lines = [line.rstrip() for line in open('script1.py')] >>> lines ['import sys', 'print(sys.path)', 'x = 2', 'print(2 ** 33)']

for

>>> [line.upper() for line in open('script1.py')] ['IMPORT SYS\n', 'PRINT(SYS.PATH)\n', 'X = 2\n', 'PRINT(2 ** 33)\n'] >>> [line.rstrip().upper() for line in open('script1.py')] ['IMPORT SYS', 'PRINT(SYS.PATH)', 'X = 2', 'PRINT(2 ** 33)'] >>> [line.split() for line in open('script1.py')] [['import', 'sys'], ['print(sys.path)'], ['x', '=', '2'], ['print(2', '**','33)']]

360 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

>>> [line.replace(' ', '!') for line in open('script1.py')] ['import!sys\n', 'print(sys.path)\n', 'x!=!2\n', 'print(2!**!33)\n'] >>> [('sys' in line, line[0]) for line in open('script1.py')] [(True, 'i'), (True, 'p'), (False, 'x'), (False, 'p')]

Extended List Comprehension Syntax for

if

if >>> lines = [line.rstrip() for line in open('script1.py') if line[0] == 'p'] >>> lines ['print(sys.path)', 'print(2 ** 33)']

if for for >>> res = [] >>> for line in open('script1.py'): ... if line[0] == 'p': ... res.append(line.rstrip()) ... >>> res ['print(sys.path)', 'print(2 ** 33)']

for

for for if x + y

x

y >>> [x + y for x in 'abc' for y in 'lmn'] ['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']

List Comprehensions: A First Look | 361

www.it-ebooks.info

>>> res = [] >>> for x in 'abc': ... for y in 'lmn': ... res.append(x + y) ... >>> res ['al', 'am', 'an', 'bl', 'bm', 'bn', 'cl', 'cm', 'cn']

for

Other Iteration Contexts

for

for >>> for line in open('script1.py'): ... print(line.upper(), end='') ... IMPORT SYS PRINT(SYS.PATH) X = 2 PRINT(2 ** 33)

in sorted

map zip

>>> uppers = [line.upper() for line in open('script1.py')] >>> uppers ['IMPORT SYS\n', 'PRINT(SYS.PATH)\n', 'X = 2\n', 'PRINT(2 ** 33)\n']

362 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

>>> map(str.upper, open('script1.py')) >>> list( map(str.upper, open('script1.py')) ) ['IMPORT SYS\n', 'PRINT(SYS.PATH)\n', 'X = 2\n', 'PRINT(2 ** 33)\n'] >>> 'y = 2\n' in open('script1.py') False >>> 'x = 2\n' in open('script1.py') True

map map

list map

for sorted

zip

enumerate filter

reduce zip enumerate

filter

map

>>> sorted(open('script1.py')) ['import sys\n', 'print(2 ** 33)\n', 'print(sys.path)\n', 'x = 2\n'] >>> list(zip(open('script1.py'), open('script1.py'))) [('import sys\n', 'import sys\n'), ('print(sys.path)\n', 'print(sys.path)\n'), ('x = 2\n', 'x = 2\n'), ('print(2 ** 33)\n', 'print(2 ** 33)\n')] >>> list(enumerate(open('script1.py'))) [(0, 'import sys\n'), (1, 'print(sys.path)\n'), (2, 'x = 2\n'), (3, 'print(2 ** 33)\n')] >>> list(filter(bool, open('script1.py'))) ['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n'] >>> import functools, operator >>> functools.reduce(operator.add, open('script1.py')) 'import sys\nprint(sys.path)\nx = 2\nprint(2 ** 33)\n'

zip filter

enumerate

reduce

sorted sorted sort

Other Iteration Contexts | 363

www.it-ebooks.info

map

sorted

sum any True

>>> sum([3, 2, 4, 1, 15 >>> any(['spam', '', True >>> all(['spam', '', False >>> max([3, 2, 5, 1, 5 >>> min([3, 2, 5, 1, 1

all max reduce

True min

5, 0]) 'ni']) 'ni']) 4]) 4])

max

min

>>> max(open('script1.py')) 'x = 2\n' >>> min(open('script1.py')) 'import sys\n'

list join

tuple

>>> list(open('script1.py')) ['import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n'] >>> tuple(open('script1.py')) ('import sys\n', 'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n') >>> '&&'.join(open('script1.py')) 'import sys\n&&print(sys.path)\n&&x = 2\n&&print(2 ** 33)\n' >>> a, b, c, d = open('script1.py') >>> a, d

364 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

('import sys\n', 'print(2 ** 33)\n') >>> a, *b = open('script1.py') >>> a, b ('import sys\n', ['print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n'])

dict

zip

set >>> set(open('script1.py')) {'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n', 'import sys\n'} >>> {line for line in open('script1.py')} {'print(sys.path)\n', 'x = 2\n', 'print(2 ** 33)\n', 'import sys\n'} >>> {ix: line for ix, line in enumerate(open('script1.py'))} {0: 'import sys\n', 1: 'print(sys.path)\n', 2: 'x = 2\n', 3: 'print(2 ** 33)\n'}

if >>> {line for line in open('script1.py') if line[0] == 'p'} {'print(sys.path)\n', 'print(2 ** 33)\n'} >>> {ix: line for (ix, line) in enumerate(open('script1.py')) if line[0] == 'p'} {1: 'print(sys.path)\n', 3: 'print(2 ** 33)\n'}

*arg

>>> def f(a, b, c, d): print(a, b, c, d, sep='&') ... >>> f(1, 2, 3, 4) 1&2&3&4 >>> f(*[1, 2, 3, 4]) 1&2&3&4 >>> f(*open('script1.py')) import sys &print(sys.path) &x = 2 &print(2 ** 33)

zip

zip

Other Iteration Contexts | 365

www.it-ebooks.info

zip >>> X = (1, 2) >>> Y = (3, 4) >>> >>> list(zip(X, Y)) [(1, 3), (2, 4)] >>> >>> A, B = zip(*zip(X, Y)) >>> A (1, 2) >>> B (3, 4)

range

New Iterables in Python 3.0 keys values items range map zip filter

list(...) >>> zip('abc', 'xyz') >>> list(zip('abc', 'xyz')) [('a', 'x'), ('b', 'y'), ('c', 'z')]

zip

366 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

The range Iterator range xrange list(range(...)) C:\\misc> c:\python30\python >>> R = range(10) >>> R range(0, 10) >>> >>> 0 >>> 1 >>> 2

I = iter(R) next(I) next(I) next(I)

>>> list(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

range len list(...) >>> len(R) 10 >>> R[0] 0 >>> R[-1] 9 >>> next(I) 3 >>> I.__next__() 4

xrange range range

xrange

range file.xread lines()

New Iterables in Python 3.0 | 367

www.it-ebooks.info

The map, zip, and filter Iterators range

map zip

filter range

map list(...) >>> M = map(abs, (-1, 0, 1)) >>> M >>> next(M) 1 >>> next(M) 0 >>> next(M) 1 >>> next(M) StopIteration >>> for x in M: print(x) ... >>> >>> ... 1 0 1 >>> [1,

M = map(abs, (-1, 0, 1)) for x in M: print(x)

list(map(abs, (-1, 0, 1))) 0, 1]

zip >>> Z = zip((1, 2, 3), (10, 20, 30)) >>> Z >>> list(Z) [(1, 10), (2, 20), (3, 30)] >>> for pair in Z: print(pair) ... >>> Z = zip((1, 2, 3), (10, 20, 30)) >>> for pair in Z: print(pair) ... (1, 10)

368 | Chapter 14: Iterations and Comprehensions, Part 1

www.it-ebooks.info

(2, 20) (3, 30) >>> >>> (1, >>> (2,

Z = zip((1, 2, 3), (10, 20, 30)) next(Z) 10) next(Z) 20)

filter True True >>> filter(bool, ['spam', '', 'ni']) >>> list(filter(bool, ['spam', '', 'ni'])) ['spam', 'ni']

filter

Multiple Versus Single Iterators range len iter >>> R = range(3) >>> next(R) TypeError: range object is not an iterator >>> >>> 0 >>> 1 >>> >>> 0 >>> 2

I1 = iter(R) next(I1) next(I1) I2 = iter(R) next(I2) next(I1)

zip map >>> >>> >>> >>> (1, >>> (2, >>>

filter

Z = zip((1, 2, 3), (10, 11, 12)) I1 = iter(Z) I2 = iter(Z) next(I1) 10) next(I1) 11) next(I2)

New Iterables in Python 3.0 | 369

www.it-ebooks.info

(3, 12) >>> M = map(abs, (-1, 0, 1)) >>> I1 = iter(M); I2 = iter(M) >>> print(next(I1), next(I1), next(I1)) 1 0 1 >>> next(I2) StopIteration >>> R = range(3) >>> I1, I2 = iter(R), iter(R) >>> [next(I1), next(I1), next(I1)] [0 1 2] >>> next(I2) 0

iter map

zip

range

Dictionary View Iterators keys values

>>> D = dict(a=1, b=2, c=3) >>> D {'a': 1, 'c': 3, 'b': 2} >>> K = D.keys() >>> K >>> next(K) TypeError: dict_keys object is not an iterator >>> I = iter(K) >>> next(I) 'a' >>> next(I) 'c' >>> for k in D.keys(): print(k, end=' ') ... a c b

370 | Chapter 14: Iterations and Comprehensions, Part 1

items

www.it-ebooks.info

list >>> K = D.keys() >>> list(K) ['a', 'c', 'b'] >>> V = D.values() >>> V >>> list(V) [1, 3, 2] >>> list(D.items()) [('a', 1), ('c', 3), ('b', 2)] >>> for (k, v) in D.items(): print(k, v, end=' ') ... a 1 c 3 b 2

keys >>> D {'a': 1, 'c': 3, 'b': 2} >>> I = iter(D) >>> next(I) 'a' >>> next(I) 'c' >>> for key in D: print(key, end=' ') ... a c b

keys list

sorted

>>> D {'a': 1, 'c': 3, 'b': 2} >>> for k in sorted(D.keys())): print(k, D[k], end=' ') ... a 1 b 2 c 3 >>> D {'a': 1, 'c': 3, 'b': 2} >>> for k in sorted(D): print(k, D[k], end=' ') ... a 1 b 2 c 3

New Iterables in Python 3.0 | 371

www.it-ebooks.info

Other Iterator Topics

yield

__iter__

Chapter Summary for

Test Your Knowledge: Quiz for for

372 | Chapter 14: Iterations and Comprehensions, Part 1

__getitem__

www.it-ebooks.info

Test Your Knowledge: Answers for __next__ StopIteration

next for

for for for for sorted sum any join __next__

in all

map list

tuple

for next

Test Your Knowledge: Answers | 373

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 15

The Documentation Interlude

Python Documentation Sources

375

www.it-ebooks.info

Form

Role

# comments

In-file documentation

The dir function

Lists of attributes available in objects

Docstrings: __doc__

In-file documentation attached to objects

PyDoc: The help function

Interactive help for objects

PyDoc: HTML reports

Module documentation in a browser

The standard manual set

Official language and library descriptions

Web resources

Online tutorials, examples, and so on

Published books

Commercially available reference texts

# Comments #

#

The dir Function dir sys dir >>> import sys >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__package__', '__stderr__', '__stdin__', '__stdout__', '_clear_type_cache', '_current_frames', '_getframe', 'api_version', 'argv', 'builtin_module_names', 'byteorder', 'call_tracing', 'callstats', 'copyright', 'displayhook', 'dllhandle', 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', 'executable', 'exit', 'flags', 'float_info', 'getcheckinterval', 'getdefaultencoding', ...more names omitted...]

376 | Chapter 15: The Documentation Interlude

www.it-ebooks.info

dir

>>> dir([]) ['__add__', '__class__', '__contains__', ...more... 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> dir('') ['__add__', '__class__', '__contains__', ...more... 'capitalize', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', ' maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', ...more names omitted...]

dir

dir >>> dir(str) == dir('') True >>> dir(list) == dir([]) True

str

list

dir

Docstrings: __doc__ #

# __doc__

Python Documentation Sources | 377

www.it-ebooks.info

User-defined docstrings

def """ Module documentation Words Go Here """ spam = 40 def square(x): """ function documentation can we have your liver then? """ return x ** 2 # square class Employee: "class documentation" pass print(square(4)) print(square.__doc__)

__doc__ __doc__ >>> import docstrings 16 function documentation can we have your liver then? >>> print(docstrings.__doc__) Module documentation Words Go Here >>> print(docstrings.square.__doc__) function documentation can we have your liver then? >>> print(docstrings.Employee.__doc__) class documentation

378 | Chapter 15: The Documentation Interlude

class

www.it-ebooks.info

print

def

class module.class.method.__doc__

Docstring standards

Built-in docstrings dir __doc__ >>> import sys >>> print(sys.__doc__) This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter. Dynamic objects: argv -path -modules ...more

command line arguments; argv[0] is the script pathname if known module search path; path[0] is the script directory, else '' -- dictionary of loaded modules text omitted...

__doc__ >>> print(sys.getrefcount.__doc__) getrefcount(object) -> integer Return the reference count of object. The count returned is generally one higher than you might expect, because it includes the (temporary) ...more text omitted...

Python Documentation Sources | 379

www.it-ebooks.info

>>> print(int.__doc__) int(x[, base]) -> integer Convert a string or number to an integer, if possible. A floating point argument will be truncated towards zero (this does not include a ...more text omitted... >>> print(map.__doc__) map(func, *iterables) --> map object Make an iterator that computes the function using arguments from each of the iterables. Stops when the shortest iterable is exhausted.

help

PyDoc: The help Function

help help >>> import sys >>> help(sys.getrefcount) Help on built-in function getrefcount in module sys: getrefcount(...) getrefcount(object) -> integer Return the reference count of object. The count returned is generally one higher than you might expect, because it includes the (temporary) ...more omitted...

sys sys

sys help

380 | Chapter 15: The Documentation Interlude

help

www.it-ebooks.info

>>> help(sys) Help on built-in module sys: NAME

sys

FILE

(built-in)

MODULE DOCS http://docs.python.org/library/sys DESCRIPTION This module provides access to some objects used or maintained by the interpreter and to functions that interact strongly with the interpreter. ...more omitted... FUNCTIONS __displayhook__ = displayhook(...) displayhook(object) -> None Print an object to sys.stdout and also save it in builtins. ...more omitted... DATA

__stderr__ = __stdin__ = __stdout__ = ...more omitted...

help dict str

list >>> help(dict) Help on class dict in module builtins: class dict(object) | dict() -> new empty dictionary. | dict(mapping) -> new dictionary initialized from a mapping object's ...more omitted... >>> help(str.replace) Help on method_descriptor: replace(...) S.replace (old, new[, count]) -> str Return a copy of S with all occurrences of substring ...more omitted... >>> help(ord)

Python Documentation Sources | 381

www.it-ebooks.info

Help on built-in function ord in module builtins: ord(...) ord(c) -> integer Return the integer ordinal of a one-character string.

help

>>> import docstrings >>> help(docstrings.square) Help on function square in module docstrings: square(x) function documentation can we have your liver then? >>> help(docstrings.Employee) Help on class Employee in module docstrings: class Employee(builtins.object) | class documentation | | Data descriptors defined here: ...more omitted... >>> help(docstrings) Help on module docstrings: NAME

docstrings

FILE

c:\misc\docstrings.py

DESCRIPTION Module documentation Words Go Here CLASSES builtins.object Employee class Employee(builtins.object) | class documentation | | Data descriptors defined here: ...more omitted... FUNCTIONS square(x) function documentation

382 | Chapter 15: The Documentation Interlude

www.it-ebooks.info

can we have your liver then? DATA

spam = 40

PyDoc: HTML Reports help

pydoc.py

-g

sys.path

glob

Python Documentation Sources | 383

www.it-ebooks.info

help

384 | Chapter 15: The Documentation Interlude

www.it-ebooks.info

Python Documentation Sources | 385

www.it-ebooks.info

The Standard Manual Set

386 | Chapter 15: The Documentation Interlude

www.it-ebooks.info

Web Resources

Published Books

Common Coding Gotchas

: if while for

Common Coding Gotchas | 387

www.it-ebooks.info

...

if

while

if (X==1):

{} for for x in seq: while range for

for

while

while

range

while a = b = [] a += [1, 2]

list.append list.sort None append

mylist = mylist.append(X) mylist None

for k in D.keys().sort(): sort

keys sort None

388 | Chapter 15: The Documentation Interlude

None

www.it-ebooks.info

sorted Ks = list(D.keys()) Ks.sort()

for k in Ks: keys

function() function

file.close

import

file.close()

import mod

import mod.py

import

Chapter Summary help

Test Your Knowledge: Quiz

Test Your Knowledge: Quiz | 389

www.it-ebooks.info

Test Your Knowledge: Answers

__doc__ help

dir(X)

Test Your Knowledge: Part III Exercises

for S

390 | Chapter 15: The Documentation Interlude

ord(character)

www.it-ebooks.info

map(ord, S)

for i in range(50): print('hello %d\n\a' % i)

for keys

sort

sorted while

found L = [1, 2, 4, 8, 16, 32, 64] X = 5 found = False i = 0 while not found and i < len(L): if 2 ** X == L[i]: found = True else: i = i+1 if found: print('at index', i) else: print(X, 'not found') C:\book\tests> python power.py at index 5

while

else

found

if for index

L.index(X)

else X

L

Test Your Knowledge: Part III Exercises | 391

www.it-ebooks.info

in 2 in [1,2,3] for

append

L 2 ** X map(function, list) map(lambda x: 2 ** x, range(7)) lambda

392 | Chapter 15: The Documentation Interlude

www.it-ebooks.info

PART IV

Functions

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 16

Function Basics

Statement

Examples

Calls

myfunc('spam', 'eggs', meat=ham)

def, return

def adder(a, b=1, *c): return a + b + c[0]

global

def changer(): global x; x = 'new'

nonlocal

def changer(): nonlocal x; x = 'new'

yield

def squares(x): for i in range(x): yield i ** 2

lambda

funcs = [lambda x: x**2, lambda x: x*3]

395

www.it-ebooks.info

Why Use Functions?

Coding Functions open len

396 | Chapter 16: Function Basics

www.it-ebooks.info

def def

def def def

while

def

if def

def def

lambda lambda def return return yield yield

global

global nonlocal nonlocal def

Coding Functions | 397

www.it-ebooks.info

def Statements def def (arg1, arg2,... argN):

def

def

return def (arg1, arg2,... argN): ... return

return return return

398 | Chapter 16: Function Basics

www.it-ebooks.info

return

None

yield

def Executes at Runtime def def def def

if

if test: def func(): ... else: def func(): ... ... func()

def

def

=

def

othername = func othername()

def func(): ... func() func.attr = value

Coding Functions | 399

www.it-ebooks.info

A First Example: Definitions and Calls def

Definition times >>> def times(x, y): ... return x * y ...

def times

Calls def

>>> times(2, 4) 8

times x 2 y

4 return

2 * 4

8

>>> x = times(3.14, 4) >>> x 12.56

>>> times('Ni', 4) 'NiNiNiNi'

400 | Chapter 16: Function Basics

www.it-ebooks.info

x

y

* times times

Polymorphism in Python x

x * y y

times

*

*

times

*

*

A First Example: Definitions and Calls | 401

www.it-ebooks.info

type

A Second Example: Intersecting Sequences for

Definition for

def intersect(seq1, seq2): res = [] for x in seq1: if x in seq2: res.append(x) return res

def

402 | Chapter 16: Function Basics

www.it-ebooks.info

return

Calls def def >>> s1 = "SPAM" >>> s2 = "SCAM" >>> intersect(s1, s2) ['S', 'A', 'M']

>>> [x for x in s1 if x in s2] ['S', 'A', 'M']

Polymorphism Revisited intersect >>> x = intersect([1, 2, 3], (1, 4)) >>> x [1]

intersect

intersect

for in

A Second Example: Intersecting Sequences | 403

www.it-ebooks.info

Local Variables res

intersect def intersect

res seq1 for

seq2 x

return res

intersect

Chapter Summary def

return def

return

file.readlines() in open

file.seek(0) in __iter__

404 | Chapter 16: Function Basics

__contains__ __getitem__

www.it-ebooks.info

Test Your Knowledge: Quiz return

Test Your Knowledge: Answers

def

def def if None return None

Test Your Knowledge: Answers | 405

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 17

Scopes

Python Scope Basics

def

def

407

www.it-ebooks.info

def

def def

X def X

def

def def def

X = 99 X X

X = 88 def

X = 99 def func(): X = 88

X

Scope Rules def open

408 | Chapter 17: Scopes

www.it-ebooks.info

def lambda

global def nonlocal

def __builtin__

__main__

=

import

def def

L L = X

L L

L.append(X) L

L global

nonlocal

Python Scope Basics | 409

www.it-ebooks.info

Name Resolution: The LEGB Rule def

global

nonlocal def

lambda

def lambda

def

lambda nonlocal spam object.spam

def

nonlocal

410 | Chapter 17: Scopes

www.it-ebooks.info

.

Scope Example # Global scope X = 99 def func(Y): # Local scope Z = X + Y return Z func(1)

X func X func def

func

Python Scope Basics | 411

www.it-ebooks.info

Y Z Y

Z Z

=

Y

Y

Z

The Built-in Scope builtins builtins

builtins

builtins dir >>> import builtins >>> dir(builtins) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', ...many more names omitted... 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

None True

False

builtins >>> zip

412 | Chapter 17: Scopes

www.it-ebooks.info

>>> import builtins >>> builtins.zip

open def hider(): open = 'spam' ... open('data.txt')

open

X = 88 def func(): X = 99 func() print(X)

X X global

nonlocal

def

builtins

__builtin__ __builtins__

builtins

__builtin__

builtins __builtins__ is builtins __builtins__ is __builtin__ True

True

dir(__builtins__) builtins

Python Scope Basics | 413

www.it-ebooks.info

Breaking the Universe in Python 2.6 True

False True = False True __builtin__.True = False

True

False True

False

None

open

The global Statement global

nonlocal global

global

global

def nonlocal def

global

X = 88 def func(): global X X = 99

414 | Chapter 17: Scopes

global

www.it-ebooks.info

func() print(X)

global X

X

def

def global

y, z = 1, 2 def all_global(): global x x = y + z

x y

z

all_global y x global

y

z global x

z x x

Minimize Global Variables global def

X = 99 def func1(): global X X = 88 def func2(): global X X = 77

X X

The global Statement | 415

www.it-ebooks.info

Minimize Cross-File Changes

X = 99 import first print(first.X) first.X = 88

_thread threading

416 | Chapter 17: Scopes

queue thread threading

Queue

www.it-ebooks.info

X

X

X

X = 99 def setX(new): global X X = new import first first.setX(88)

X

The global Statement | 417

www.it-ebooks.info

Other Ways to Access Globals global sys.modules

var = 99 def local(): var = 0 def glob1(): global var var += 1 def glob2(): var = 0 import thismod thismod.var += 1 def glob3(): var = 0 import sys glob = sys.modules['thismod'] glob.var += 1 def test(): print(var) local(); glob1(); glob2(); glob3() print(var)

>>> import thismod >>> thismod.test() 99 102 >>> thismod.var 102

global global nonlocal

418 | Chapter 17: Scopes

www.it-ebooks.info

Scopes and Nested Functions def

Nested Scope Details X

X

builtins global X = value

X

X X X

X global nonlocal

Nested Scope Examples X = 99 def f1(): X = 88 def f2(): print(X) f2() f1()

def def

Scopes and Nested Functions | 419

www.it-ebooks.info

def

f1 f2

f1

f2

f1 f2

X

X

f1 def

X

f2

X

f1

def f1(): X = 88 def f2(): print(X) return f2 action = f1() action()

action f2

f2 X

Factory functions

>>> def maker(N): ... def action(X): ... return X ** N ... return action ...

>>> f = maker(2) >>> f

420 | Chapter 17: Scopes

f1

f1

f1

www.it-ebooks.info

def >>> f(3) 9 >>> f(4) 16

action

maker 2

maker

N

maker

action action

N

>>> g = maker(3) >>> g(3) 27 >>> f(3) 9

g

3

2

f N

maker

lambda def

Retaining enclosing scopes’ state with defaults def

f2 f2

f1

Scopes and Nested Functions | 421

www.it-ebooks.info

def f1(): x = 88 def f2(x=x): print(x) f2() f1()

arg = val arg

def

val f2

arg

x=x

x

x

x def f1

x

f1

x

88

def def def

def

def

>>> def f1(): ... x = 88 ... f2(x) ... >>> def f2(x): ... print(x) ... >>> f1() 88

def

lambda

def

Nested scopes and lambdas def lambda lambda def

422 | Chapter 17: Scopes

www.it-ebooks.info

def def

lambda lambda

def func(): x = 4 action = (lambda n: x ** n) return action x = func() print(x(2))

lambda

def

def func(): x = 4 action = (lambda n, x=x: x ** n) return action

lambda def lambda

Scopes versus defaults with loop variables lambda

def

i >>> def makeActions(): ... acts = [] ... for i in range(5): ... acts.append(lambda x: i ** x) ... return acts ... >>> acts = makeActions() >>> acts[0]

Scopes and Nested Functions | 423

www.it-ebooks.info

i >>> acts[0](2) 16 >>> acts[2](2) 16 >>> acts[4](2) 16

i >>> ... ... ... ... ... >>> >>> 0 >>> 4 >>> 16

def makeActions(): acts = [] for i in range(5): acts.append(lambda x, i=i: i ** x) return acts acts = makeActions() acts[0](2) acts[2](2) acts[4](2)

lambda

Arbitrary scope nesting def >>> def f1(): ... x = 99 ... def f2(): ... def f3(): ... print(x) ... f3()

def f(a=[])

424 | Chapter 17: Scopes

www.it-ebooks.info

... f2() ... >>> f1() 99

def

The nonlocal Statement nonlocal

def

nonlocal nonlocal nonlocal

global

def

global global

global nonlocal def

nonlocal def

nonlocal Basics nonlocal def func(): nonlocal name1, name2, ...

def def nonlocal

The nonlocal Statement | 425

www.it-ebooks.info

nonlocal def

nonlocal nonlocal def

global nonlocal nonlocal def global

nonlocal

global nonlocal

def nonlocal def

def def

nonlocal nonlocal global

nonlocal

global

nonlocal

def

def

nonlocal in Action def state

426 | Chapter 17: Scopes

tester nested

nested tester

www.it-ebooks.info

C:\\misc>c:\python30\python >>> def tester(start): ... state = start ... def nested(label): ... print(label, state) ... return nested ... >>> F = tester(0) >>> F('spam') spam 0 >>> F('ham') ham 0

def >>> def tester(start): ... state = start ... def nested(label): ... print(label, state) ... state += 1 ... return nested ... >>> F = tester(0) >>> F('spam') UnboundLocalError: local variable 'state' referenced before assignment

Using nonlocal for changes state

tester

nonlocal

nested tester

nested F >>> def tester(start): ... state = start ... def nested(label): ... nonlocal state ... print(label, state) ... state += 1 ... return nested ... >>> F = tester(0) >>> F('spam') spam 0 >>> F('ham') ham 1 >>> F('eggs') eggs 2

tester state nested

The nonlocal Statement | 427

www.it-ebooks.info

state >>> G = tester(42) >>> G('spam') spam 42 >>> G('eggs') eggs 43 >>> F('bacon') bacon 3

Boundary cases global

nonlocal def

nonlocal >>> def tester(start): ... def nested(label): ... nonlocal state ... state = 0 ... print(label, state) ... return nested ... SyntaxError: no binding for nonlocal 'state' found >>> ... ... ... ... ... ... >>> >>> abc >>> 0

def tester(start): def nested(label): global state state = 0 print(label, state) return nested F = tester(0) F('abc') 0 state

nonlocal def >>> spam = 99 >>> def tester(): ... def nested(): ... nonlocal spam ... print('Current=', spam) ... spam += 1 ... return nested ... SyntaxError: no binding for nonlocal 'spam' found

428 | Chapter 17: Scopes

def

www.it-ebooks.info

spam

tester

Why nonlocal?

nonlocal

nonlocal

tester

def tester(start): state = start def nested(label): nonlocal state print(label, state) state += 1 return nested F = tester(0) F('spam')

Shared state with globals nonlocal >>> def tester(start): ... global state ... state = start ... def nested(label): ... global state ... print(label, state) ... state += 1 ... return nested ... >>> F = tester(0) >>> F('spam')

The nonlocal Statement | 429

www.it-ebooks.info

spam 0 >>> F('eggs') eggs 1

global

tester state

state

>>> G = tester(42) >>> G('toast') toast 42 >>> G('bacon') bacon 43 >>> F('ham') ham 44

nonlocal state

global

tester

State with classes (preview)

tester nested def class self >>> class tester: ... def __init__(self, start): ... self.state = start ... def nested(self, label): ... print(label, self.state) ... self.state += 1 ... >>> F = tester(0) >>> F.nested('spam') spam 0 >>> F.nested('ham') ham 1 >>> G = tester(42) >>> G.nested('toast') toast 42

430 | Chapter 17: Scopes

def

class

www.it-ebooks.info

>>> G.nested('bacon') bacon 43 >>> F.nested('eggs') eggs 2 >>> F.state 3

__call__ >>> class tester: ... def __init__(self, start): ... self.state = start ... def __call__(self, label): ... print(label, self.state) ... self.state += 1 ... >>> H = tester(99) >>> H('juice') juice 99 >>> H('pancakes') pancakes 100

__call__

def def

State with function attributes

def >>> def tester(start): ... def nested(label): ... print(label, nested.state) ... nested.state += 1

The nonlocal Statement | 431

www.it-ebooks.info

... nested.state = start ... return nested ... >>> F = tester(0) >>> F('spam') spam 0 >>> F('ham') ham 1 >>> F.state 2 >>> >>> G = tester(42) >>> G('eggs') eggs 42 >>> F('ham') ham 2

nested tester

nested nested.state nested nonlocal

Chapter Summary

432 | Chapter 17: Scopes

nested nested

www.it-ebooks.info

Test Your Knowledge: Quiz >>> X = 'Spam' >>> def func(): ... print(X) ... >>> func()

>>> >>> ... ... >>> >>>

X = 'Spam' def func(): X = 'NI!'

>>> >>> ... ... ... >>> >>>

X = 'Spam' def func(): X = 'NI' print(X)

>>> >>> ... ... ... >>> >>>

X = 'Spam' def func(): global X X = 'NI'

>>> >>> ... ... ... ... ... >>> >>>

X = 'Spam' def func(): X = 'NI' def nested(): print(X) nested()

func() print(X)

func() print(X)

func() print(X)

func() X

Test Your Knowledge: Quiz | 433

www.it-ebooks.info

>>> def func(): ... X = 'NI' ... def nested(): ... nonlocal X ... X = 'Spam' ... nested() ... print(X) ... >>> func()

Test Your Knowledge: Answers 'Spam' 'Spam' print 'NI'

'Spam' print 'NI' 'NI'

'Spam'

print print 'Spam' X X 'NI'

434 | Chapter 17: Scopes

nonlocal X

www.it-ebooks.info

CHAPTER 18

Arguments

Argument-Passing Basics

435

www.it-ebooks.info

Arguments and Shared References >>> ... ... >>> >>> >>> 88

def f(a): a = 99 b = 88 f(b) print(b)

a f(b)

88

a

a

a a=99

436 | Chapter 18: Arguments

b

www.it-ebooks.info

>>> ... ... ... >>> >>> >>> >>> (1,

def changer(a, b): a = 2 b[0] = 'spam' X = 1 L = [1, 2] changer(X, L) X, L ['spam', 2])

changer

a b

a a X b L b[0] L changer

b

b L L

>>> >>> >>> >>> 1

X = 1 a = X a = 2 print(X)

>>> L = [1, 2] >>> b = L >>> b[0] = 'spam' >>> print(L) ['spam', 2]

Argument-Passing Basics | 437

www.it-ebooks.info

Avoiding Mutable Argument Changes

L = [1, 2] changer(X, L[:])

def changer(a, b): b = b[:]

438 | Chapter 18: Arguments

www.it-ebooks.info

a = 2 b[0] = 'spam'

L = [1, 2] changer(X, tuple(L))

tuple

Simulating Output Parameters return return

>>> ... ... ... ... >>> >>> >>> >>> (2,

def multiple(x, y): x = 2 y = [3, 4] return x, y X = 1 L = [1, 2] X, L = multiple(X, L) X, L [3, 4])

Argument-Passing Basics | 439

www.it-ebooks.info

X

def f((a, (b, c))):

f((1, (2, 3)))

a b

c

1 2

3 f(T)

def def f(T): (a, (b, c)) = T

def f((a, [b, c])): lambda

for

Special Argument-Matching Modes def

440 | Chapter 18: Arguments

L

www.it-ebooks.info

The Basics

name=value

name=value *

* *

Special Argument-Matching Modes | 441

www.it-ebooks.info

Matching Syntax

Syntax

Location

Interpretation

func(value)

Caller

Normal argument: matched by position

func(name=value)

Caller

Keyword argument: matched by name

func(*sequence)

Caller

Pass all objects in sequence as individual positional arguments

func(**dict)

Caller

Pass all key/value pairs in dict as individual keyword arguments

def func(name)

Function

Normal argument: matches any passed value by position or name

def func(name=value)

Function

Default argument value, if not passed in the call

def func(*name)

Function

Matches and collects remaining positional arguments in a tuple

def func(**name)

Function

Matches and collects remaining keyword arguments in a dictionary

def func(*args, name)

Function

Arguments that must be passed by keyword only in calls (3.0)

def func(*, name=value)

name=value *sequence

name name=value *name **name *name

*

print

442 | Chapter 18: Arguments

**dict

www.it-ebooks.info

* *

The Gritty Details

value *sequence

name=value **dict

name *

name=value name name=value

*name

**name **arg

*name **name

Special Argument-Matching Modes | 443

www.it-ebooks.info

name:value

name:value=default

def f()->value

Keyword and Default Examples

>>> def f(a, b, c): print(a, b, c) ...

a

1 b

2

print >>> f(1, 2, 3) 1 2 3

Keywords

>>> f(c=3, b=2, a=1) 1 2 3

c=3

3 c 3

444 | Chapter 18: Arguments

c c

www.it-ebooks.info

>>> f(1, c=3, b=2) 1 2 3

a b

c

func(name='Bob', age=40, job='dev')

Defaults

>>> def f(a, b=2, c=3): print(a, b, c) ...

a b b

c >>> 1 2 >>> 1 2

2

c

3

f(1) 3 f(a=1) 3

c >>> 1 4 >>> 1 4

f(1, 4) 3 f(1, 4, 5) 5

>>> f(1, c=6) 1 2 6

a

1

c

6

b

2

name=value

Special Argument-Matching Modes | 445

www.it-ebooks.info

Combining keywords and defaults spam toast

eggs ham def func(spam, eggs, toast=0, ham=0): print((spam, eggs, toast, ham)) func(1, 2) func(1, ham=1, eggs=0) func(spam=1, eggs=0) func(toast=1, eggs=2, spam=3) func(1, 2, 3, 4)

spam

eggs name=value

def

Arbitrary Arguments Examples *

**

Collecting arguments

>>> def f(*args): print(args) ...

args for >>> f() () >>> f(1) (1,) >>> f(1, 2, 3, 4) (1, 2, 3, 4)

** ** keys

446 | Chapter 18: Arguments

www.it-ebooks.info

>>> def f(**args): print(args) ... >>> f() {} >>> f(a=1, b=2) {'a': 1, 'b': 2}

* 2

3

pargs

** 1 x

a y

kargs >>> def f(a, *pargs, **kargs): print(a, pargs, kargs) ... >>> f(1, 2, 3, x=1, y=2) 1 (2, 3) {'y': 2, 'x': 1}

*

**

Unpacking arguments *

>>> ... >>> >>> >>> 1 2

def func(a, b, c, d): print(a, b, c, d) args = (1, 2) args += (3, 4) func(*args) 3 4

** >>> >>> >>> 1 2

args = {'a': 1, 'b': 2, 'c': 3} args['d'] = 4 func(**args) 3 4

>>> func(*(1, 2), **{'d': 4, 'c': 4}) 1 2 4 4 >>> func(1, *(2, 3), **{'d': 4}) 1 2 3 4 >>> func(1, c=3, *(2,), **{'d': 4})

Special Argument-Matching Modes | 447

www.it-ebooks.info

1 2 3 4 >>> func(1, *(2, 3), d=4) 1 2 3 4 >>> f(1, *(2,), c=3, **{'d':4}) 1 2 3 4

* **

*pargs

* func(*open('fname') *pargs def * x, *y = z

Applying functions generically

if if : action, args = func1, (1,) else: action, args = func2, (1, 2, 3) ... action(*args)

448 | Chapter 18: Arguments

www.it-ebooks.info

>>> >>> >>> (2, >>>

args = (2,3) args += (4,) args 3, 4) func(*args)

def tracer(func, *pargs, **kargs): print('calling:', func.__name__) return func(*pargs, **kargs) def func(a, b, c, d): return a + b + c + d print(tracer(func, 1, 2, c=3, d=4))

calling: func 10

The defunct apply built-in (Python 2.6) *args apply

**args

func(*pargs, **kargs) apply(func, pargs, kargs)

>>> def echo(*args, **kwargs): print(args, kwargs) ... >>> echo(1, 2, a=3, b=4) (1, 2) {'a': 3, 'b': 4}

Special Argument-Matching Modes | 449

www.it-ebooks.info

apply >>> pargs = (1, 2) >>> kargs = {'a':3, 'b':4} >>> apply(echo, pargs, kargs) (1, 2) {'a': 3, 'b': 4} >>> echo(*pargs, **kargs) (1, 2) {'a': 3, 'b': 4}

apply *pargs

**kargs

def

>>> echo(0, c=5, *pargs, **kargs) (0, 1, 2) {'a': 3, 'c': 5, 'b': 4}

# Normal, keyword, *sequence, **dictionary

apply

Python 3.0 Keyword-Only Arguments

*args a

b

c >>> def kwonly(a, *b, c): ... print(a, b, c) ... >>> kwonly(1, 2, c=3) 1 (2,) 3 >>> kwonly(a=1, c=3) 1 () 3 >>> kwonly(1, 2, 3) TypeError: kwonly() needs keyword-only argument c

* *

a b

c

450 | Chapter 18: Arguments

www.it-ebooks.info

>>> def kwonly(a, *, b, c): ... print(a, b, c) ... >>> kwonly(1, c=3, b=2) 1 2 3 >>> kwonly(c=3, b=2, a=1) 1 2 3 >>> kwonly(1, 2, 3) TypeError: kwonly() takes exactly 1 positional argument (3 given) >>> kwonly(1) TypeError: kwonly() needs keyword-only argument b

* b

a c

>>> def kwonly(a, *, b='spam', c='ham'): ... print(a, b, c) ... >>> kwonly(1) 1 spam ham >>> kwonly(1, c=3) 1 spam 3 >>> kwonly(a=1) 1 spam ham >>> kwonly(c=3, b=2, a=1) 1 2 3 >>> kwonly(1, 2) TypeError: kwonly() takes exactly 1 positional argument (2 given)

>>> def kwonly(a, *, b, c='spam'): ... print(a, b, c) ... >>> kwonly(1, b='eggs') 1 eggs spam >>> kwonly(1, c='eggs') TypeError: kwonly() needs keyword-only argument b >>> kwonly(1, 2) TypeError: kwonly() takes exactly 1 positional argument (2 given) >>> def kwonly(a, *, b=1, c, d=2): ... print(a, b, c, d) ... >>> kwonly(3, c=4) 3 1 4 2 >>> kwonly(3, c=4, b=5) 3 5 4 2 >>> kwonly(3) TypeError: kwonly() needs keyword-only argument c >>> kwonly(1, 2, 3) TypeError: kwonly() takes exactly 1 positional argument (3 given)

Special Argument-Matching Modes | 451

www.it-ebooks.info

Ordering rules **args ** >>> def kwonly(a, **pargs, b, c): SyntaxError: invalid syntax >>> def kwonly(a, **, b, c): SyntaxError: invalid syntax

**args

*args *args

>>> def f(a, *b, **d, c=6): print(a, b, c, d) SyntaxError: invalid syntax >>> def f(a, *b, c=6, **d): print(a, b, c, d) ... >>> f(1, 2, 3, x=4, y=5) 1 (2, 3) 6 {'y': 5, 'x': 4} >>> f(1, 2, 3, x=4, y=5, c=7) 1 (2, 3) 7 {'y': 5, 'x': 4} >>> f(1, 2, 3, c=7, x=4, y=5) 1 (2, 3) 7 {'y': 5, 'x': 4} >>> def f(a, c=6, *b, **d): print(a, b, c, d) ... >>> f(1, 2, 3, x=4) 1 (3,) 2 {'x': 4}

**args *args >>> def f(a, *b, c=6, **d): print(a, b, c, d) ... >>> f(1, *(2, 3), **dict(x=4, y=5)) 1 (2, 3) 6 {'y': 5, 'x': 4} >>> f(1, *(2, 3), **dict(x=4, y=5), c=7) SyntaxError: invalid syntax >>> f(1, *(2, 3), c=7, **dict(x=4, y=5)) 1 (2, 3) 7 {'y': 5, 'x': 4} >>> f(1, c=7, *(2, 3), **dict(x=4, y=5)) 1 (2, 3) 7 {'y': 5, 'x': 4} >>> f(1, *(2, 3), **dict(x=4, y=5, c=7)) 1 (2, 3) 7 {'y': 5, 'x': 4}

452 | Chapter 18: Arguments

**args

www.it-ebooks.info

Why keyword-only arguments?

process(X, Y, Z) process(X, Y, notify=True)

# use flag's default # override flag default

*args

**args

notify def process(*args, notify=False): ...

The min Wakeup Call!

None * for

The min Wakeup Call! | 453

www.it-ebooks.info

Full Credit args

list sort sort

def min1(*args): res = args[0] for arg in args[1:]: if arg < res: res = arg return res def min2(first, *rest): for arg in rest: if arg < first: first = arg return first def min3(*args): tmp = list(args) tmp.sort() return tmp[0] print(min1(3,4,1,2))

sort

time timeit

454 | Chapter 18: Arguments

www.it-ebooks.info

print(min2("bb", "aa")) print(min3([2,2], [1,1], [3,3]))

% python mins.py 1 aa [1, 1]

Bonus Points <

>

tmp[−1]

tmp[0]

eval def minmax(test, *args): res = args[0] for arg in args[1:]: if test(arg, res): res = arg return res def lessthan(x, y): return x < y def grtrthan(x, y): return x > y print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) print(minmax(grtrthan, 4, 2, 1, 5, 6, 3)) % python minmax.py

The min Wakeup Call! | 455

www.it-ebooks.info

1 6

max test

The Punch Line... min max

Generalized Set Functions

*args for

def intersect(*args): res = [] for x in args[0]: for other in args[1:]: if x not in other: break else: res.append(x) return res def union(*args): res = [] for seq in args: for x in seq: if not x in res: res.append(x) return res

456 | Chapter 18: Arguments

union

www.it-ebooks.info

args intersect % python >>> from inter2 import intersect, union >>> s1, s2, s3 = "SPAM", "SCAM", "SLAM" >>> intersect(s1, s2), union(s1, s2) (['S', 'A', 'M'], ['S', 'P', 'A', 'M', 'C']) >>> intersect([1,2,3], (1,4)) [1] >>> intersect(s1, s2, s3) ['S', 'A', 'M'] >>> union(s1, s2, s3) ['S', 'P', 'A', 'M', 'C', 'L']

Emulating the Python 3.0 print Function *args **args print print from __future__ import print_function

Emulating the Python 3.0 print Function | 457

www.it-ebooks.info

""" Emulate most of the 3.0 print function for use in 2.X call signature: print30(*args, sep=' ', end='\n', file=None) """ import sys def print30(*args, **kargs): sep = kargs.get('sep', ' ') end = kargs.get('end', '\n') file = kargs.get('file', sys.stdout) output = '' first = True for arg in args: output += ('' if first else sep) + str(arg) first = False file.write(output + end)

print from print30 import print30 print30(1, 2, 3) print30(1, 2, 3, sep='') print30(1, 2, 3, sep='...') print30(1, [2], (3,), sep='...') print30(4, 5, 6, sep='', end='') print30(7, 8, 9) print30() import sys print30(1, 2, 3, sep='??', end='.\n', file=sys.stderr)

print C:\misc> c:\python26\python testprint30.py 1 2 3 123 1...2...3 1...[2]...(3,) 4567 8 9 1??2??3.

458 | Chapter 18: Arguments

www.it-ebooks.info

Using Keyword-Only Arguments

def print30(*args, sep=' ', end='\n', file=sys.stdout): output = '' first = True for arg in args: output += ('' if first else sep) + str(arg) first = False file.write(output + end)

>>> print30(99, name='bob') TypeError: print30() got an unexpected keyword argument 'name'

name dict.pop()

def print30(*args, **kargs): sep = kargs.pop('sep', ' ') end = kargs.pop('end', '\n') file = kargs.pop('file', sys.stdout) if kargs: raise TypeError('extra keywords: %s' % kargs) output = '' first = True for arg in args: output += ('' if first else sep) + str(arg) first = False file.write(output + end)

>>> print30(99, name='bob') TypeError: extra keywords: {'name': 'bob'}

Emulating the Python 3.0 print Function | 459

www.it-ebooks.info

Why You Will Care: Keyword Arguments

tkinter Tkinter tkinter

from tkinter import * widget = Button(text="Press me", command=someFunction)

text command

sorted sorted(iterable, key=None, reverse=False)

None

False

Chapter Summary

460 | Chapter 18: Arguments

www.it-ebooks.info

lambda map

filter

Test Your Knowledge: Quiz >>> def func(a, b=4, c=5): ... print(a, b, c) ... >>> func(1, 2)

>>> def func(a, b, c=5): ... print(a, b, c) ... >>> func(1, c=3, b=2)

>>> def func(a, *pargs): ... print(a, pargs) ... >>> func(1, 2, 3)

>>> def func(a, **kargs): ... print(a, kargs) ... >>> func(a=1, c=3, b=2)

>>> def func(a, b, c=3, d=4): print(a, b, c, d) ... >>> func(1, *(5,6))

Test Your Knowledge: Quiz | 461

www.it-ebooks.info

Test Your Knowledge: Answers '1 2 5' c

1

2

'1 2 3' 1 2

a

b

5 a

b

c

3 '1 (2, 3)'

1

a

*pargs for arg in

pargs: ... '1, {'c': 3, 'b': 2}'

1

a

**kargs for key in kargs: ... *name

'1 5 6 4' 1 6 c

a

5 d

return

return

462 | Chapter 18: Arguments

6 4

b

c

www.it-ebooks.info

CHAPTER 19

Advanced Function Topics

lambda map

filter

lambda

Function Design Concepts

return return

463

www.it-ebooks.info

return

self self.name='bob'

464 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

Recursive Functions

Summation with Recursion sum >>> def mysum(L): ... if not L: ... return 0 ... else: ... return L[0] + mysum(L[1:]) >>> mysum([1, 2, 3, 4, 5]) 15

Recursive Functions | 465

www.it-ebooks.info

L print >>> ... ... ... ... ... ... >>> [1, [2, [3, [4, [5] [] 15

L def mysum(L): print(L) if not L: return 0 else: return L[0] + mysum(L[1:]) mysum([1, 2, 3, 4, 5]) 2, 3, 4, 5] 3, 4, 5] 4, 5] 5]

Coding Alternatives if else

def mysum(L): return 0 if not L else L[0] + mysum(L[1:]) def mysum(L): return L[0] if len(L) == 1 else L[0] + mysum(L[1:]) def mysum(L): first, *rest = L return first if not rest else first + mysum(rest)

+ >>> mysum([1]) 1 >>> mysum([1, 2, 3, 4, 5]) 15 >>> mysum(('s', 'p', 'a', 'm')) 'spam'

466 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

>>> mysum(['spam', 'ham', 'eggs']) 'spamhameggs'

mysum ('spam')) def mysum(first, * rest)

>>> def mysum(L): ... if not L: return 0 ... return nonempty(L) ... >>> def nonempty(L): ... return L[0] + mysum(L[1:]) ... >>> mysum([1.1, 2.2, 3.3, 4.4]) 11.0

Loop Statements Versus Recursion

while

>>> >>> >>> ... ... ... >>> 15

L = [1, 2, 3, 4, 5] sum = 0 while L: sum += L[0] L = L[1:] sum

for

>>> >>> >>> ... >>> 15

L = [1, 2, 3, 4, 5] sum = 0 for x in L: sum += x sum

Recursive Functions | 467

www.it-ebooks.info

Handling Arbitrary Structures

[1, [2, [3, 4], 5], 6, [7, 8]]

def sumtree(L): tot = 0 for x in L: if not isinstance(x, list): tot += x else: tot += sumtree(x) return tot L = [1, [2, [3, 4], 5], 6, [7, 8]] print(sumtree(L))

print(sumtree([1, [2, [3, [4, [5]]]]])) print(sumtree([[[[[1], 2], 3], 4], 5]))

468 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

__setattr__

__getattribute__

Function Objects: Attributes and Annotations

Indirect Function Calls

def =

def

>>> def echo(message): ... print(message) ... >>> echo('Direct call') Direct call >>> x = echo >>> x('Indirect call!') Indirect call!

Function Objects: Attributes and Annotations | 469

www.it-ebooks.info

>>> def indirect(func, arg): ... func(arg) ... >>> indirect(echo, 'Argument call!') Argument call!

>>> schedule = [ (echo, 'Spam!'), (echo, 'Ham!') ] >>> for (func, arg) in schedule: ... func(arg) ... Spam! Ham!

schedule

echo for

>>> def make(label): ... def echo(message): ... print(label + ':' + message) ... return echo ... >>> F = make('Spam') >>> F('Ham!') Spam:Ham! >>> F('Eggs!') Spam:Eggs!

Function Introspection

>>> def func(a): ... b = 'spam' ... return b * a ... >>> func(8) 'spamspamspamspamspamspamspamspam'

470 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

>>> func.__name__ 'func' >>> dir(func) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', ...more omitted... '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> func.__code__ >>> dir(func.__code__) ['__class__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', ...more omitted... 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames'] >>> func.__code__.co_varnames ('a', 'b') >>> func.__code__.co_argcount 1

Function Attributes

>>> func >>> func.count = 0 >>> func.count += 1 >>> func.count 1 >>> func.handles = 'Button-Press' >>> func.handles 'Button-Press' >>> dir(func) ['__annotations__', '__call__', '__class__', '__closure__', '__code__', ...more omitted... __str__', '__subclasshook__', 'count', 'handles']

Function Objects: Attributes and Annotations | 471

www.it-ebooks.info

Function Annotations in 3.0

__annotations__

>>> def func(a, b, c): ... return a + b + c ... >>> func(1, 2, 3) 6

def

-> >>> def func(a: 'spam', b: (1, 10), c: float) -> int: ... return a + b + c ... >>> func(1, 2, 3) 6

>>> func.__annotations__ {'a': 'spam', 'c': , 'b': (1, 10), 'return': }

472 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

>>> def func(a: 'spam', b, c: 99): ... return a + b + c ... >>> func(1, 2, 3) 6 >>> func.__annotations__ {'a': 'spam', 'c': 99} >>> for arg in func.__annotations__: ... print(arg, '=>', func.__annotations__[arg]) ... a => spam c => 99

: = a

4

a: 'spam' = 4 'spam'

>>> def func(a: 'spam' = 4, b: (1, 10) = 5, c: float = 6) -> int: ... return a + b + c ... >>> func(1, 2, 3) 6 >>> func() 15 >>> func(1, c=10) 16 >>> func.__annotations__ {'a': 'spam', 'c': , 'b': (1, 10), 'return': }

>>> def func(a:'spam'=4, b:(1,10)=5, c:float=6)->int: ... return a + b + c ... >>> func(1, 2) 9 >>> func.__annotations__ {'a': 'spam', 'c': , 'b': (1, 10), 'return': }

Function Objects: Attributes and Annotations | 473

www.it-ebooks.info

def

lambda

lambda

Anonymous Functions: lambda def lambda

def lambda

lambda Basics lambda

lambda def

lambda argument1, argument2,... argumentN :expression using arguments

lambda def

lambda

lambda

lambda def lambda def

lambda

lambda def lambda lambda

def if lambda

def

lambda

lambda

474 | Chapter 19: Advanced Function Topics

return

www.it-ebooks.info

def

lambda def

>>> def func(x, y, z): return x + y + z ... >>> func(2, 3, 4) 9

lambda >>> f = lambda x, y, z: x + y + z >>> f(2, 3, 4) 9

f

lambda lambda

def

def

>>> x = (lambda a="fee", b="fie", c="foe": a + b + c) >>> x("wee") 'weefiefoe'

lambda def lambda

def

>>> def knights(): ... title = 'Sir' ... action = (lambda x: title + ' ' + x) ... return action ... >>> act = knights() >>> act('robin') 'Sir robin'

title

Why Use lambda? lambda def

lambda def

Anonymous Functions: lambda | 475

www.it-ebooks.info

lambda L = [lambda x: x ** 2, lambda x: x ** 3, lambda x: x ** 4] for f in L: print(f(2)) print(L[0](3))

lambda

def

lambda

def def

def f1(x): return x ** 2 def f2(x): return x ** 3 def f3(x): return x ** 4 L = [f1, f2, f3] for f in L: print(f(2)) print(L[0](3))

>>> key = 'got' >>> {'already': (lambda: 2 + 2), ... 'got': (lambda: 2 * 4), ... 'one': (lambda: 2 ** 6)}[key]() 8

lambda

if lambda

>>> def f1(): return 2 + 2 ... >>> def f2(): return 2 * 4 ...

476 | Chapter 19: Advanced Function Topics

def

www.it-ebooks.info

>>> def f3(): return 2 ** 6 ... >>> key = 'one' >>> {'already': f1, 'got': f2, 'one': f3}[key]() 64

def lambda

lambda

def

lambda map

How (Not) to Obfuscate Your Python Code lambda lambda lambda sys.stdout.write(str(x)+'\n') print

print(x) lambda

if else and or

if a: b else: c

b if a else c ((a and b) or c)

lambda lambda >>> lower = (lambda x, y: x if x < y else y) >>> lower('bb', 'aa') 'aa' >>> lower('aa', 'bb') 'aa'

Anonymous Functions: lambda | 477

www.it-ebooks.info

lambda map >>> import sys >>> showall = lambda x: list(map(sys.stdout.write, x)) >>> t = showall(['spam\n', 'toast\n', 'eggs\n']) spam toast eggs >>> showall = lambda x: [sys.stdout.write(line) for line in x] >>> t = showall(('bright\n', 'side\n', 'of\n', 'life\n')) bright side of life

lambda def lambda

Nested lambdas and Scopes lambda lambda x

def >>> def action(x): ... return (lambda y: x + y) ... >>> act = action(99) >>> act >>> act(2) 101

lambda def >>> action = (lambda x: (lambda y: x + y)) >>> act = action(99) >>> act(3) 102

478 | Chapter 19: Advanced Function Topics

lambda lambda

www.it-ebooks.info

>>> ((lambda x: (lambda y: x + y))(99))(4) 103

lambda lambda

x

lambda lambda

Why You Will Care: Callbacks lambda tkinter

Tkinter

tkinter import sys from tkinter import Button, mainloop # Tkinter in 2.6 x = Button( text ='Press me', command=(lambda:sys.stdout.write('Spam\n'))) x.pack() mainloop()

lambda command

lambda

lambda

def write

lambda

self class MyGui: def makewidgets(self): Button(command=(lambda: self.onPress("spam"))) def onPress(self, message): ...use message...

self

lambda

Mapping Functions over Sequences: map for Mapping Functions over Sequences: map | 479

www.it-ebooks.info

>>> counters = [1, 2, 3, 4] >>> >>> updated = [] >>> for x in counters: ... updated.append(x + 10) ... >>> updated [11, 12, 13, 14]

map

>>> def inc(x): return x + 10 ... >>> list(map(inc, counters)) [11, 12, 13, 14]

map map

inc map

list map lambda >>> list(map((lambda x: x + 3), counters)) [4, 5, 6, 7]

counters lambda for >>> def mymap(func, seq): ... res = [] ... for x in seq: res.append(func(x)) ... return res

inc >>> list(map(inc, [1, 2, 3])) [11, 12, 13] >>> mymap(inc, [1, 2, 3]) [11, 12, 13]

map for

480 | Chapter 19: Advanced Function Topics

map

map

www.it-ebooks.info

>>> pow(3, 4) 81 >>> list(map(pow, [1, 2, 3], [2, 3, 4])) [1, 8, 81]

map pow map

map map map

Functional Programming Tools: filter and reduce map filter reduce filter

range

list filter

>>> list(range(−5, 5)) [−5, −4, −3, −2, −1, 0, 1, 2, 3, 4] >>> list(filter((lambda x: x > 0), range(−5, 5))) [1, 2, 3, 4]

map >>> >>> ... ... ... >>> [1,

reduce

for

res = [] for x in range(−5, 5): if x > 0: res.append(x) res 2, 3, 4]

functools

Functional Programming Tools: filter and reduce | 481

www.it-ebooks.info

reduce >>> from functools import reduce >>> reduce((lambda x, y: x + y), [1, 2, 3, 4]) 10 >>> reduce((lambda x, y: x * y), [1, 2, 3, 4]) 24

reduce lambda for >>> >>> >>> ... ... >>> 10

L = [1,2,3,4] res = L[0] for x in L[1:]: res = res + x res

reduce

>>> def myreduce(function, sequence): ... tally = sequence[0] ... for next in sequence[1:]: ... tally = function(tally, next) ... return tally ... >>> myreduce((lambda x, y: x + y), [1, 2, 3, 4, 5]) 15 >>> myreduce((lambda x, y: x * y), [1, 2, 3, 4, 5]) 120

reduce

operator

>>> import operator, functools >>> functools.reduce(operator.add, [2, 4, 6]) 12 >>> functools.reduce((lambda x, y: x + y), [2, 4, 6]) 12

482 | Chapter 19: Advanced Function Topics

www.it-ebooks.info

map filter

reduce

lambda

Chapter Summary lambda filter

map

reduce

Test Your Knowledge: Quiz lambda

def lamba map filter

reduce

Test Your Knowledge: Answers lambda

def

lambda def

lambda

lambda lambda

def lambda

map

filter

Test Your Knowledge: Answers | 483

www.it-ebooks.info

map filter

True

reduce reduce

__annotations__

484 | Chapter 19: Advanced Function Topics

functools

www.it-ebooks.info

CHAPTER 20

Iterations and Comprehensions, Part 2

map

filter

map

filter

for

List Comprehensions Revisited: Functional Tools

map

map

filter

filter

485

www.it-ebooks.info

List Comprehensions Versus map ord chr >>> ord('s') 115

for >>> res = [] >>> for x in 'spam': ... res.append(ord(x)) ... >>> res [115, 112, 97, 109]

map >>> res = list(map(ord, 'spam')) >>> res [115, 112, 97, 109]

map >>> res = [ord(x) for x in 'spam'] >>> res [115, 112, 97, 109]

for for map >>> [x ** 2 for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

map

486 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

lambda def >>> list(map((lambda x: x ** 2), range(10))) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

lambda

Adding Tests and Nested Loops: filter if

for

if

filter if

map

filter lambda

for >>> [x for x in range(5) if x % 2 == 0] [0, 2, 4] >>> list(filter((lambda x: x % 2 == 0), range(5))) [0, 2, 4] >>> >>> ... ... ... >>> [0,

res = [] for x in range(5): if x % 2 == 0: res.append(x) res 2, 4]

% filter if filter

map

>>> [x ** 2 for x in range(10) if x % 2 == 0] [0, 4, 16, 36, 64]

for if map

List Comprehensions Revisited: Functional Tools | 487

www.it-ebooks.info

filter

map

>>> list( map((lambda x: x**2), filter((lambda x: x % 2 == 0), range(10))) ) [0, 4, 16, 36, 64]

for

if [ expression for target1 in iterable1 [if condition1] for target2 in iterable2 [if condition2] ... for targetN in iterableN [if conditionN] ]

for for >>> res = [x + y for x in [0, 1, 2] for y in [100, 200, 300]] >>> res [100, 200, 300, 101, 201, 301, 102, 202, 302]

>>> res = [] >>> for x in [0, 1, 2]: ... for y in [100, 200, 300]: ... res.append(x + y) ... >>> res [100, 200, 300, 101, 201, 301, 102, 202, 302]

>>> [x + y for x in 'spam' for y in 'SPAM'] ['sS', 'sP', 'sA', 'sM', 'pS', 'pP', 'pA', 'pM', 'aS', 'aP', 'aA', 'aM', 'mS', 'mP', 'mA', 'mM']

if

for

>>> [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1] [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

if >>> res = [] >>> for x in range(5): ... if x % 2 == 0: ... for y in range(5): ... if y % 2 == 1: ... res.append((x, y)) ...

488 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

>>> res [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

for

map

if

filter

List Comprehensions and Matrixes

>>> M = [[1, 2, 3], ... [4, 5, 6], ... [7, 8, 9]] >>> N = [[2, 2, 2], ... [3, 3, 3], ... [4, 4, 4]]

>>> M[1] [4, 5, 6] >>> M[1][2] 6

>>> [row[1] for row in M] [2, 5, 8] >>> [M[row][1] for row in (0, 1, 2)] [2, 5, 8]

range M[0][0]

M[1][1]

List Comprehensions Revisited: Functional Tools | 489

www.it-ebooks.info

>>> [M[i][i] for i in range(len(M))] [1, 5, 9]

>>> [M[row][col] * N[row][col] for row in range(3) for col in range(3)] [2, 4, 6, 12, 15, 18, 28, 32, 36] >>> [[M[row][col] * N[row][col] for col in range(3)] for row in range(3)] [[2, 4, 6], [12, 15, 18], [28, 32, 36]]

>>> res >>> for ... ... ... ... ... >>> res [[2, 4,

= [] row in range(3): tmp = [] for col in range(3): tmp.append(M[row][col] * N[row][col]) res.append(tmp) 6], [12, 15, 18], [28, 32, 36]]

Comprehending List Comprehensions for map

map for map

map

for for map

map time

time.clock

time.time

490 | Chapter 20: Iterations and Comprehensions, Part 2

timeit

www.it-ebooks.info

for for map map for lambda map

Why You Will Care: List Comprehensions and map map map

readlines

\n

>>> open('myfile').readlines() ['aaa\n', 'bbb\n', 'ccc\n']

map

map

list >>> [line.rstrip() for line in open('myfile').readlines()] ['aaa', 'bbb', 'ccc'] >>> [line.rstrip() for line in open('myfile')] ['aaa', 'bbb', 'ccc'] >>> list(map((lambda line: line.rstrip()), open('myfile'))) ['aaa', 'bbb', 'ccc']

map

listoftuple = [('bob', 35, 'mgr'), ('mel', 40, 'dev')]

for

map

>>> [age for (name, age, job) in listoftuple] [35, 40] >>> list(map((lambda row: row[1]), listoftuple)) [35, 40]

List Comprehensions Revisited: Functional Tools | 491

www.it-ebooks.info

map >>> list(map((lambda (name, age, job): age), listoftuple)) [35, 40]

map

map

Iterators Revisited: Generators

def

Generator Functions: yield Versus return

def

492 | Chapter 20: Iterations and Comprehensions, Part 2

yield

www.it-ebooks.info

State suspension

yield

yield

Iteration protocol integration

__next__ StopIteration iter for

yield __next__ return def StopIteration __next__ yield

StopIteration def

yield

Iterators Revisited: Generators | 493

www.it-ebooks.info

next

__next__ __next__

next next(I)

I.__next__() I.next()

I.next()

Generator functions in action

>>> def gensquares(N): ... for i in range(N): ... yield i ** 2 ...

yield

for yield

>>> for i in gensquares(5): ... print(i, end=' : ') ... 0 : 1 : 4 : 9 : 16 : >>>

return for >>> x = gensquares(4) >>> x

__next__ StopIteration next(X) X.__next__() >>> 0 >>> 1 >>> 4 >>> 9 >>>

next(x) next(x) next(x) next(x) next(x)

494 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

Traceback (most recent call last): ...more text omitted... StopIteration

for __next__ for

>>> def buildsquares(n): ... res = [] ... for i in range(n): res.append(i ** 2) ... return res ... >>> for x in buildsquares(5): print(x, end=' : ') ... 0 : 1 : 4 : 9 : 16 :

for

map

>>> for x in [n ** 2 for n in range(5)]: ... print(x, end=' : ') ... 0 : 1 : 4 : 9 : 16 : >>> for x in map((lambda n: n ** 2), range(5)): ... print(x, end=' : ') ... 0 : 1 : 4 : 9 : 16 :

yield

yield next

Iterators Revisited: Generators | 495

www.it-ebooks.info

Extended generator function protocol: send versus next send

send __next__

yield yield X X = yield Y

send A = (yield X) X = (yield Y) + 42 G

G.send(value) send next(G)

yield

yield G.__next__() None

>>> def gen(): ... for i in range(10): ... X = yield i ... print(X) ... >>> G = gen() >>> next(G) 0 >>> G.send(77) 77 1 >>> G.send(88) 88 2 >>> next(G) None 3

send

yield

throw(type) close

Generator

Exit

next(X) X.__next__()

send G.send(X)

__next__

496 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

Generator Expressions: Iterators Meet Comprehensions

>>> [x ** 2 for x in range(4)] [0, 1, 4, 9] >>> (x ** 2 for x in range(4))

list >>> list(x ** 2 for x in range(4)) [0, 1, 4, 9]

>>> >>> 0 >>> 1 >>> 4 >>> 9 >>>

G = (x ** 2 for x in range(4)) next(G) next(G) next(G) next(G) next(G)

Traceback (most recent call last): ...more text omitted... StopIteration

next for >>> for num in (x ** 2 for x in range(4)): ... print('%s, %s' % (num, num / 2.0)) ... 0, 0.0 1, 0.5 4, 2.0 9, 4.5

sum map sorted any all

list

Iterators Revisited: Generators | 497

www.it-ebooks.info

sorted >>> sum(x ** 2 for x in range(4)) 14 >>> sorted(x ** 2 for x in range(4)) [0, 1, 4, 9] >>> sorted((x ** 2 for x in range(4)), reverse=True) [9, 4, 1, 0] >>> import math >>> list( map(math.sqrt, (x ** 2 for x in range(4))) ) [0.0, 1.0, 2.0, 3.0]

Generator Functions Versus Generator Expressions

>>> G = (c * 4 for c in 'SPAM') >>> list(G) ['SSSS', 'PPPP', 'AAAA', 'MMMM']

>>> def timesfour(S): ... for c in S: ... yield c * 4 ... >>> G = timesfour('spam') >>> list(G) ['ssss', 'pppp', 'aaaa', 'mmmm']

list >>> G = (c * 4 for c in 'SPAM') >>> I = iter(G) >>> next(I) 'SSSS' >>> next(I) 'PPPP'

498 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

>>> G = timesfour('spam') >>> I = iter(G) >>> next(I) 'ssss' >>> next(I) 'pppp'

Generators Are Single-Iterator Objects

iter >>> G = (c * 4 for c in 'SPAM') >>> iter(G) is G True

>>> G = (c * 4 for c in 'SPAM') >>> I1 = iter(G) >>> next(I1) 'SSSS' >>> next(I1) 'PPPP' >>> I2 = iter(G) >>> next(I2) 'AAAA'

>>> list(I1) ['MMMM'] >>> next(I2) StopIteration >>> I3 = iter(G) >>> next(I3) StopIteration >>> I3 = iter(c * 4 for c in 'SPAM') >>> next(I3) 'SSSS'

Iterators Revisited: Generators | 499

www.it-ebooks.info

def >>> def timesfour(S): ... for c in S: ... yield c * 4 ... >>> G = timesfour('spam') >>> iter(G) is G True >>> I1, I2 = iter(G), iter(G) >>> next(I1) 'ssss' >>> next(I1) 'pppp' >>> next(I2) 'aaaa'

>>> L = [1, 2, 3, 4] >>> I1, I2 = iter(L), iter(L) >>> next(I1) 1 >>> next(I1) 2 >>> next(I2) 1 >>> del L[2:] >>> next(I1) StopIteration

Emulating zip and map with Iteration Tools

zip map zip >>> S1 = 'abc' >>> S2 = 'xyz123' >>> list(zip(S1, S2)) [('a', 'x'), ('b', 'y'), ('c', 'z')]

500 | Chapter 20: Iterations and Comprehensions, Part 2

map

www.it-ebooks.info

>>> list(zip([−2, −1, 0, 1, 2])) [(−2,), (−1,), (0,), (1,), (2,)] >>> list(zip([1, 2, 3], [2, 3, 4, 5])) [(1, 2), (2, 3), (3, 4)]

>>> list(map(abs, [−2, −1, 0, 1, 2])) [2, 1, 0, 1, 2] >>> list(map(pow, [1, 2, 3], [2, 3, 4, 5])) [1, 8, 81]

zip

Coding your own map(func, ...) map

zip map

def mymap(func, *seqs): res = [] for args in zip(*seqs): res.append(func(*args)) return res print(mymap(abs, [−2, −1, 0, 1, 2])) print(mymap(pow, [1, 2, 3], [2, 3, 4, 5]))

*args zip zip

map [2, 1, 0, 1, 2] [1, 8, 81]

for

Iterators Revisited: Generators | 501

www.it-ebooks.info

def mymap(func, *seqs): return [func(*args) for args in zip(*seqs)] print(mymap(abs, [−2, −1, 0, 1, 2])) print(mymap(pow, [1, 2, 3], [2, 3, 4, 5]))

mymap

def mymap(func, *seqs): res = [] for args in zip(*seqs): yield func(*args) def mymap(func, *seqs): return (func(*args) for args in zip(*seqs))

list print(list(mymap(abs, [−2, −1, 0, 1, 2]))) print(list(mymap(pow, [1, 2, 3], [2, 3, 4, 5])))

list zip

Coding your own zip(...) and map(None, ...) zip map map map

None

C:\misc> c:\python26\python >>> map(None, [1, 2, 3], [2, 3, 4, 5]) [(1, 2), (2, 3), (3, 4), (None, 5)] >>> map(None, 'abc', 'xyz123') [('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None, '3')]

502 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

zip map

def myzip(*seqs): seqs = [list(S) for S in seqs] res = [] while all(seqs): res.append(tuple(S.pop(0) for S in seqs)) return res def mymapPad(*seqs, pad=None): seqs = [list(S) for S in seqs] res = [] while any(seqs): res.append(tuple((S.pop(0) if S else pad) for S in seqs)) return res S1, S2 = 'abc', 'xyz123' print(myzip(S1, S2)) print(mymapPad(S1, S2)) print(mymapPad(S1, S2, pad=99))

list any

all True

True

pad

map

**kargs zip

map

[('a', 'x'), ('b', 'y'), ('c', 'z')] [('a', 'x'), ('b', 'y'), ('c', 'z'), (None, '1'), (None, '2'), (None, '3')] [('a', 'x'), ('b', 'y'), ('c', 'z'), (99, '1'), (99, '2'), (99, '3')]

zip

map yield

list

def myzip(*seqs): seqs = [list(S) for S in seqs] while all(seqs): yield tuple(S.pop(0) for S in seqs)

Iterators Revisited: Generators | 503

www.it-ebooks.info

def mymapPad(*seqs, pad=None): seqs = [list(S) for S in seqs] while any(seqs): yield tuple((S.pop(0) if S else pad) for S in seqs) S1, S2 = 'abc', 'xyz123' print(list(myzip(S1, S2))) print(list(mymapPad(S1, S2))) print(list(mymapPad(S1, S2, pad=99)))

zip

map

pop

def myzip(*seqs): minlen = min(len(S) for S in seqs) return [tuple(S[i] for S in seqs) for i in range(minlen)] def mymapPad(*seqs, pad=None): maxlen = max(len(S) for S in seqs) index = range(maxlen) return [tuple((S[i] if len(S) > i else pad) for S in seqs) for i in index] S1, S2 = 'abc', 'xyz123' print(myzip(S1, S2)) print(mymapPad(S1, S2)) print(mymapPad(S1, S2, pad=99))

len tuple

min

max range tuple

zip

def myzip(*seqs): minlen = min(len(S) for S in seqs)

504 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

return (tuple(S[i] for S in seqs) for i in range(minlen)) print(list(myzip(S1, S2)))

list

Why You Will Care: One-Shot Iterations map

zip def myzip(*args): iters = map(iter, args) while iters: res = [next(i) for i in iters] yield tuple(res)

iter next StopIteration

next(it) return

while iters:

>>> list(myzip('abc', 'lmnop')) [('a', 'l'), ('b', 'm'), ('c', 'n')]

map iters

res

[]

list def myzip(*args): iters = list(map(iter, args)) ...rest as is...

map list

Iterators Revisited: Generators | 505

www.it-ebooks.info

Value Generation in Built-in Types and Classes

>>> >>> >>> 'a' >>> 'c'

D = {'a':1, 'b':2, 'c':3} x = iter(D) next(x) next(x)

for

map

>>> for key in D: ... print(key, D[key]) ... a 1 c 3 b 2

>>> for line in open('temp.txt'): ... print(line, end='') ... Tis but a flesh wound.

for

__iter__ __next__

for

506 | Chapter 20: Iterations and Comprehensions, Part 2

iter next

__getitem__

www.it-ebooks.info

3.0 Comprehension Syntax Summary

{1, 3, 2} set([1, 3, 2]) {f(x) for x in S if P(x)} set(f(x) for x in S if P(x)) f(x) val) in zip(keys, vals)} f(x) for x in items} items)

{key: val for (key, dict(zip(keys, vals)) {x: dict((x, f(x)) for x in

>>> [x * x for x in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> (x * x for x in range(10)) >>> {x * x for x in range(10)} {0, 1, 4, 81, 64, 9, 16, 49, 25, 36}

0

>>> {x: x * x for x in range(10)} {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Comprehending Set and Dictionary Comprehensions

>>> {0, >>> {0,

{x * x for x in range(10)} 1, 4, 81, 64, 9, 16, 49, 25, 36} set(x * x for x in range(10)) 1, 4, 81, 64, 9, 16, 49, 25, 36}

>>> {0: >>> {0:

{x: x * x for x in range(10)} 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} dict((x, x * x) for x in range(10)) 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

3.0 Comprehension Syntax Summary | 507

www.it-ebooks.info

>>> >>> ... ... >>> {0,

res = set() for x in range(10): res.add(x * x)

>>> >>> ... ... >>> {0:

res = {} for x in range(10): res[x] = x * x

>>> >>> (0, >>> (1,

G = ((x, x * x) for x in range(10)) next(G) 0) next(G) 1)

res 1, 4, 81, 64, 9, 16, 49, 25, 36}

res 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

Extended Comprehension Syntax for Sets and Dictionaries if

>>> [0, >>> {0, >>> {0:

[x * x for x in 4, 16, 36, 64] {x * x for x in 16, 4, 64, 36} {x: x * x for x 0, 8: 64, 2: 4,

range(10) if x % 2 == 0] range(10) if x % 2 == 0} in range(10) if x % 2 == 0} 4: 16, 6: 36}

for >>> [5, >>> {8, >>> {1:

[x + y for x in [1, 2, 3] for y in [4, 5, 6]] 6, 7, 6, 7, 8, 7, 8, 9] {x + y for x in [1, 2, 3] for y in [4, 5, 6]} 9, 5, 6, 7} {x: y for x in [1, 2, 3] for y in [4, 5, 6]} 6, 2: 6, 3: 6}

>>> {x + y for x in 'ab' for y in 'cd'} {'bd', 'ac', 'ad', 'bc'}

508 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

>>> {x + y: (ord(x), ord(y)) for x in 'ab' for y in 'cd'} {'bd': (98, 100), 'ac': (97, 99), 'ad': (97, 100), 'bc': (98, 99)} >>> {k * 2 for k in ['spam', 'ham', 'sausage'] if k[0] == 's'} {'sausagesausage', 'spamspam'} >>> {k.upper(): k * 2 for k in ['spam', 'ham', 'sausage'] if k[0] == 's'} {'SAUSAGE': 'sausagesausage', 'SPAM': 'spamspam'}

for

Timing Iteration Alternatives

for

map

Timing Module

import time reps = 1000 repslist = range(reps) def timer(func, *pargs, **kargs): start = time.clock() for i in repslist: ret = func(*pargs, **kargs) elapsed = time.clock() - start return (elapsed, ret)

Timing Iteration Alternatives | 509

www.it-ebooks.info

time

range range reps

Timing Script

import sys, mytimer reps = 10000 repslist = range(reps) def forLoop(): res = [] for x in repslist: res.append(abs(x)) return res def listComp(): return [abs(x) for x in repslist] def mapCall(): return list(map(abs, repslist)) def genExpr(): return list(abs(x) for x in repslist) def genFunc(): def gen():

510 | Chapter 20: Iterations and Comprehensions, Part 2

mytimer.reps = N

www.it-ebooks.info

for x in repslist: yield abs(x) return list(gen()) print(sys.version) for test in (forLoop, listComp, mapCall, genExpr, genFunc): elapsed, result = mytimer.timer(test) print ('-' * 33) print ('%-9s: %.5f => [%s...%s]' % (test.__name__, elapsed, result[0], result[-1]))

list map __name__

Timing Results map for C:\misc> c:\python30\python timeseqs.py 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] --------------------------------forLoop : 2.64441 => [0...9999] --------------------------------listComp : 1.60110 => [0...9999] --------------------------------mapCall : 1.41977 => [0...9999] --------------------------------genExpr : 2.21758 => [0...9999] --------------------------------genFunc : 2.18696 => [0...9999]

list list return [abs(x) for x in range(size)] return list(abs(x) for x in range(size))

Timing Iteration Alternatives | 511

www.it-ebooks.info

for

map abs

2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit (Intel)] forStatement => 6.10899996758 listComprehension => 3.51499986649 mapFunction => 2.73399996758 generatorExpression => 4.11600017548

list

map

abs

... ... def forLoop(): res = [] for x in repslist: res.append(x + 10) return res def listComp(): return [x + 10 for x in repslist] def mapCall(): return list(map((lambda x: x + 10), repslist)) def genExpr(): return list(x + 10 for x in repslist) def genFunc(): def gen(): for x in repslist: yield x + 10 return list(gen()) ... ...

map for C:\misc> c:\python30\python timeseqs.py 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)]

512 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

--------------------------------forLoop : 2.60754 => [10...10009] --------------------------------listComp : 1.57585 => [10...10009] --------------------------------mapCall : 3.10276 => [10...10009] --------------------------------genExpr : 1.96482 => [10...10009] --------------------------------genFunc : 1.95340 => [10...10009]

2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit (Intel)] forStatement => 5.25699996948 listComprehension => 2.68400001526 mapFunction => 5.96900010109 generatorExpression => 3.37400007248

map

Timing Module Alternatives time.clock time.time timer

Timing Iteration Alternatives | 513

www.it-ebooks.info

_reps

""" timer(spam, 1, 2, a=3, b=4, _reps=1000) calls and times spam(1, 2, a=3) _reps times, and returns total time for all runs, with final result; best(spam, 1, 2, a=3, b=4, _reps=50) runs best-of-N timer to filter out any system load variation, and returns best time among _reps tests """ import time, sys if sys.platform[:3] == 'win': timefunc = time.clock else: timefunc = time.time def trace(*args): pass def timer(func, *pargs, **kargs): _reps = kargs.pop('_reps', 1000) trace(func, pargs, kargs, _reps) repslist = range(_reps) start = timefunc() for i in repslist: ret = func(*pargs, **kargs) elapsed = timefunc() - start return (elapsed, ret) def best(func, *pargs, **kargs): _reps = kargs.pop('_reps', 50) best = 2 ** 32 for i in range(_reps): (time, ret) = timer(func, *pargs, _reps=1, **kargs) if time < best: best = time return (best, ret)

pop

_reps trace

print x + 1

import sys, mytimer reps = 10000 repslist = range(reps) def forLoop(): ...

514 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

def listComp(): ... def mapCall(): ... def genExpr(): ... def genFunc(): ... print(sys.version) for tester in (mytimer.timer, mytimer.best): print('' % tester.__name__) for test in (forLoop, listComp, mapCall, genExpr, genFunc): elapsed, result = tester(test) print ('-' * 35) print ('%-9s: %.5f => [%s...%s]' % (test.__name__, elapsed, result[0], result[-1]))

C:\misc> c:\python30\python timeseqs.py 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] ----------------------------------forLoop : 2.35371 => [10...10009] ----------------------------------listComp : 1.29640 => [10...10009] ----------------------------------mapCall : 3.16556 => [10...10009] ----------------------------------genExpr : 1.97440 => [10...10009] ----------------------------------genFunc : 1.95072 => [10...10009] ----------------------------------forLoop : 0.00193 => [10...10009] ----------------------------------listComp : 0.00124 => [10...10009] ----------------------------------mapCall : 0.00268 => [10...10009] ----------------------------------genExpr : 0.00164 => [10...10009] ----------------------------------genFunc : 0.00165 => [10...10009]

map

Timing Iteration Alternatives | 515

www.it-ebooks.info

Using keyword-only arguments in 3.0

_reps *

** **

""" Use 3.0 keyword-only default arguments, instead of ** and dict pops. No need to hoist range() out of test in 3.0: a generator, not a list """ import time, sys trace = lambda *args: None # or print timefunc = time.clock if sys.platform == 'win32' else time.time def timer(func, *pargs, _reps=1000, **kargs): trace(func, pargs, kargs, _reps) start = timefunc() for i in range(_reps): ret = func(*pargs, **kargs) elapsed = timefunc() - start return (elapsed, ret) def best(func, *pargs, _reps=50, **kargs): best = 2 ** 32 for i in range(_reps): (time, ret) = timer(func, *pargs, _reps=1, **kargs) if time < best: best = time return (best, ret)

C:\misc> c:\python30\python timeseqs.py ...same results as before...

C:\misc> c:\python30\python >>> from mytimer import timer, best >>> >>> def power(X, Y): return X ** Y ... >>> timer(power, 2, 32) (0.002625403507987747, 4294967296) >>> timer(power, 2, 32, _reps=1000000)

516 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

(1.1822605247314932, 4294967296) >>> timer(power, 2, 100000)[0] 2.2496919999608878 >>> best(power, 2, 32) (5.58730229727189e-06, 4294967296) >>> best(power, 2, 100000)[0] 0.0019937589833460834 >>> best(power, 2, 100000, _reps=500)[0] 0.0019845399345541637

X ** Y

2**100,000 >>> timer(power, 2, 1000000, _reps=1)[0] 0.088112804839710179 >>> timer(power, 2, 1000000, _reps=10)[0] 0.40922470593329763 >>> best(power, 2, 1000000, _reps=1)[0] 0.086550036387279761 >>> best(power, 2, 1000000, _reps=10)[0] 0.029616752967200455 >>> best(power, 2, 1000000, _reps=50)[0] 0.029486918030102061

Other Suggestions timeit

profile

Timing Iteration Alternatives | 517

www.it-ebooks.info

str.format % print print('' % tester.__name__) print(''.format(tester.__name__)) print ('%-9s: %.5f => [%s...%s]' % (test.__name__, elapsed, result[0], result[-1])) print('{0: [{2}...{3}]'.format( test.__name__, elapsed, result[0], result[-1]))

for

for

Function Gotchas

Local Names Are Detected Statically def

518 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

>>> X = 99 >>> def selector(): ... print(X) ... >>> selector() 99

X

X X

>>> def selector(): ... print(X) ... X = 88 ... >>> selector() ...error text omitted... UnboundLocalError: local variable 'X' referenced before assignment

X

X

print X =

def

X

X X X

global >>> def selector(): ... global X ... print(X) ... X = 88 ... >>> selector() 99

X X

>>> X = >>> def ... ... ...

99 selector(): import __main__ print(__main__.X) X = 88

Function Gotchas | 519

www.it-ebooks.info

... print(X) ... >>> selector() 99 88

.X __main__

__main__.X

X

Defaults and Mutable Objects def

def

>>> ... ... ... >>> [2, >>> [1] >>> [1, >>> [1,

def saver(x=[]): x.append(1) print(x) saver([2]) 1] saver() saver() 1] saver() 1, 1]

520 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

def

>>> ... ... ... ... ... >>> [2, >>> [1] >>> [1]

def saver(x=None): if x is None: x = [] x.append(1) print(x) saver([2]) 1] saver() saver()

if x = x or []

or None

x

or or

if [] or []

>>> ... ... ... >>> >>> [1] >>> [1, >>> [1,

def saver(): saver.x.append(1) print(saver.x) saver.x = [] saver() saver() 1] saver() 1, 1]

Function Gotchas | 521

www.it-ebooks.info

Functions Without returns return

yield return None

>>> def proc(x): ... print(x) ... >>> x = proc('testing 123...') testing 123... >>> print(x) None

return None

append None >>> list = [1, 2, 3] >>> list = list.append(4) >>> print(list) None

Enclosing Scope Loop Variables

Chapter Summary

522 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

Test Your Knowledge: Quiz

yield map

Test Your Knowledge: Answers

__next__

def __iter__ yield

Test Your Knowledge: Answers | 523

www.it-ebooks.info

yield yield

next

__next__ return

map map

map

map for

if

filter

Test Your Knowledge: Part IV Exercises

adder adder

adder

S[:0]

524 | Chapter 20: Iterations and Comprehensions, Part 2

S min

type

www.it-ebooks.info

adder def adder(good, bad, ugly)

adder(ugly=1, good=2) adder dict.keys for

while

list

copyDict(dict) keys keys

X[:] addDict(dict1, dict2)

type

def f1(a, b): print(a, b) def f2(a, *b): print(a, b) def f3(a, **b): print(a, b) def f4(a, *b, **c): print(a, b, c) def f5(a, b=2, c=3): print(a, b, c) def f6(a, b=2, *c): print(a, b, c)

>>> f1(1, 2) >>> f1(b=2, a=1) >>> f2(1, 2, 3) >>> f3(1, x=2, y=3) >>> f4(1, 2, 3, x=2, y=3)

Test Your Knowledge: Part IV Exercises | 525

www.it-ebooks.info

>>> f5(1) >>> f5(1, 4) >>> f6(1) >>> f6(1, 3, 4)

x = y // 2 while x > 1: if y % x == 0: print(y, 'has factor', x) break x -= 1 else: print(y, 'is prime')

y //

/

/ 0

1

13 is prime 13.0 is prime 15 has factor 5 15.0 has factor 5.0

[2, 4, 9, 16, 25] map math

math.sqrt(X) X ** .5

for sqrt math

math.sqrt(x)

pow(X, .5)

best

for

526 | Chapter 20: Iterations and Comprehensions, Part 2

www.it-ebooks.info

PART V

Modules

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 21

Modules: The Big Picture

import from imp.reload

__name__

__all__

Why Use Modules?

529

www.it-ebooks.info

Python Program Architecture

530 | Chapter 21: Modules: The Big Picture

www.it-ebooks.info

How to Structure a Program

Imports and Attributes

Python Program Architecture | 531

www.it-ebooks.info

spam def def spam(text): print(text, 'spam')

spam import b b.spam('gumby')

import b import

from import import

import

import spam

b

b.spam spam

b 'gumby' object.attribute

a c

b

b.spam

532 | Chapter 21: Modules: The Big Picture

b

www.it-ebooks.info

Standard Library Modules

How Imports Work

C #include

How Imports Work | 533

www.it-ebooks.info

sys.modules

1. Find It import import import b import c:\dir1\b.py import

2. Compile It (Maybe) import

import import

import

534 | Chapter 21: Modules: The Big Picture

www.it-ebooks.info

__name__

__main__

3. Run It

def

print

def

imp.reload

The Module Search Path

sys.modules sys list(sys.modules.keys())

The Module Search Path | 535

www.it-ebooks.info

PYTHONPATH

sys.path

PYTHONPATH PYTHONPATH PYTHONPATH

PYTHONPATH

PYTHONPATH

536 | Chapter 21: Modules: The Big Picture

www.it-ebooks.info

PYTHONPATH

PYTHONPATH PYTHONPATH

site PYTHONPATH

distutils

Configuring the Search Path PYTHONPATH

The Module Search Path | 537

www.it-ebooks.info

PYTHONPATH c:\pycode\utilities;d:\pycode\package1

c:\pycode\utilities d:\pycode\package1

PYTHONPATH

Search Path Variations

PYTHONPATH

sys.path

The sys.path List sys.path path

from

538 | Chapter 21: Modules: The Big Picture

sys

from . import string

www.it-ebooks.info

sys.path PYTHONPATH

PYTHONPATH C:\users\mark

C:\users

>>> import sys >>> sys.path ['', 'C:\\users', 'C:\\Windows\\system32\\python30.zip', 'c:\\Python30\\DLLs', 'c:\\Python30\\lib', 'c:\\Python30\\lib\\plat-win', 'c:\\Python30', 'C:\\Users\\Mark', 'c:\\Python30\\lib\\site-packages']

sys.path PYTHONPATH

Module File Selection import import

import b

sys.path PYTHONPATH import

sys.path sys.path.append(dirname)

The Module Search Path | 539

www.it-ebooks.info

import b b.attr

b

sys.path

Advanced Module Selection Concepts

sys.path __import__

import

-O

Third-Party Software: distutils distutils

540 | Chapter 21: Modules: The Big Picture

www.it-ebooks.info

distutils distutils

distutils

Chapter Summary import

PYTHONPATH

import

Test Your Knowledge: Quiz PYTHONPATH

Test Your Knowledge: Quiz | 541

www.it-ebooks.info

Test Your Knowledge: Answers

PYTHONPATH

PYTHONPATH PYTHONPATH

def class

542 | Chapter 21: Modules: The Big Picture

www.it-ebooks.info

CHAPTER 22

Module Coding Basics

Module Creation

def printer def printer(x): print(x)

if

import if

543

www.it-ebooks.info

M

import

Module Usage import import from

printer

The import Statement module1

>>> import module1 >>> module1.printer('Hello world!') Hello world!

import module1.printer

544 | Chapter 22: Module Coding Basics

from

www.it-ebooks.info

The from Statement from printer >>> from module1 import printer >>> printer('Hello world!') Hello world!

from from import

The from * Statement from

*

printer >>> from module1 import * >>> printer('Hello world!') Hello world!

import

from

from *

from ...*

Module Usage | 545

www.it-ebooks.info

Imports Happen Only Once

import

from

print('hello') spam = 1

print spam

=

% python >>> import simple hello >>> simple.spam 1

spam >>> simple.spam = 2 >>> import simple >>> simple.spam 2

reload

import and from Are Assignments def import

from if

def import

from import from

546 | Chapter 22: Module Coding Basics

def import

from

www.it-ebooks.info

from

x = 1 y = [1, 2] % python >>> from small import x, y >>> x = 42 >>> y[0] = 42

x

y

y

>>> import small >>> small.x 1 >>> small.y [42, 2]

from

Cross-File Name Changes x x from

x import

% python >>> from small import x, y >>> x = 42 >>> import small >>> small.x = 42

y[0]

Module Usage | 547

www.it-ebooks.info

import and from Equivalence import from

small

from from

from module import name1, name2

import module name1 = module.name1 name2 = module.name2 del module

from from * from module import * from

import

from

Potential Pitfalls of the from Statement from name

module.name import from

from

tkinter from

import module.attr attr from from module import x, y, z from reload 548 | Chapter 22: Module Coding Basics

www.it-ebooks.info

from module import *

from *

import from

from

from * from * from

When import is required import

from

def func(): ...do something...

def func(): ...do something else...

from

from M import func from N import func func()

import

import M, N M.func() N.func()

import

Module Usage | 549

www.it-ebooks.info

Module Namespaces

Files Generate Namespaces

X = 1 X M.X

M X

def

class

=

def __dict__ __dict__ dir __dict__

550 | Chapter 22: Module Coding Basics

dir

dir(M)

www.it-ebooks.info

print('starting to load...') import sys name = 42 def func(): pass class klass: pass print('done loading.')

print >>> import module2 starting to load... done loading.

import >>> module2.sys >>> module2.name 42 >>> module2.func >>> module2.klass

sys name func sys

klass import

__dict__

list

>>> list(module2.__dict__.keys()) ['name', '__builtins__', '__file__', '__package__', 'sys', 'klass', 'func', '__name__', '__doc__']

Module Namespaces | 551

www.it-ebooks.info

__file__ __name__

Attribute Name Qualification

object.attribute module2.sys sys L L.append

append

X X.Y X

module2

X X

X.Y.Z

Imports Versus Scopes

552 | Chapter 22: Module Coding Basics

Y

Y

X

Z

X.Y

www.it-ebooks.info

X X X = 88 def f(): global X X = 99

X X = 11 import moda moda.f() print(X, moda.X)

moda.f

X

moda

X

modb

moda.f % python modb.py 11 99

Namespace Nesting

X = 3

X

mod3

Module Namespaces | 553

www.it-ebooks.info

X = 2 import mod3 print(X, end=' ') print(mod3.X)

X

mod2

X = 1 import mod2 print(X, end=' ') print(mod2.X, end=' ') print(mod2.mod3.X)

mod1 mod2

mod2 mod2.mod3.X mod1

mod3 X

% python mod1.py 2 3 1 2 3

mod3

mod2

mod2

mod1 mod1 mod2 import mod1

mod2.mod3.X

import mod2

mod2.mod3 import

Reloading Modules reload import

554 | Chapter 22: Module Coding Basics

from

mod2.mod3.X

import

www.it-ebooks.info

reload

reload reload

reload

reload imp imp.reload import

from reload

imp

reload Basics import

from

reload reload reload reload

import

reload

import module ...use module.attributes... ... ...

Reloading Modules | 555

www.it-ebooks.info

from imp import reload reload(module) ...use module.attributes...

reload reload

reload

def import import from

from

reload Example reload

message = "First version" def printer(): print(message)

message % python >>> import changer >>> changer.printer() First version

556 | Chapter 22: Module Coding Basics

www.it-ebooks.info

...modify changer.py without stopping Python... % vi changer.py

message

printer

message = "After editing" def printer(): print('reloaded:', message)

reload ...back to the Python interpreter/program... >>> import changer >>> changer.printer() First version >>> from imp import reload >>> reload(changer) >>> changer.printer() reloaded: After editing

reload

Why You Will Care: Module Reloads

Reloading Modules | 557

www.it-ebooks.info

Chapter Summary import reload

from

from reload

from

import

Test Your Knowledge: Quiz from

import

reload import

from from

Test Your Knowledge: Answers

558 | Chapter 22: Module Coding Basics

www.it-ebooks.info

from

import from

name

module.name reload

import

from

from reload from *

Test Your Knowledge: Answers | 559

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 23

Module Packages

from

Package Import Basics import import dir1.dir2.mod

from from dir1.dir2.mod import x

561

www.it-ebooks.info

import dir0\dir1\dir2\mod.py

sys.path import

Packages and Search Path Settings import import

C:\dir1 My Documents.dir2

../dir1

import C:\mycode\dir1\dir2\mod

PYTHONPATH import dir1.dir2.mod

import

import

import import

562 | Chapter 23: Module Packages

import mod.py

www.it-ebooks.info

Package __init__.py Files

import dir0\dir1\dir2\mod.py

import import dir1.dir2.mod

PYTHONPATH

dir0\ dir1\ __init__.py dir2\ __init__.py mod.py

from *

from .. import *

Package Import Basics | 563

www.it-ebooks.info

dir1.dir2

from * __all__ from *

__all__

__all__ from * from *

from submodule import X X __all__

__init__ import

Package Import Example

print('dir1 init') x = 1 print('dir2 init') y = 2

564 | Chapter 23: Module Packages

www.it-ebooks.info

print('in mod.py') z = 3

sys.path import print reload

reload

% python >>> import dir1.dir2.mod dir1 init dir2 init in mod.py >>> >>> import dir1.dir2.mod >>> >>> from imp import reload >>> reload(dir1) dir1 init >>> >>> reload(dir1.dir2) dir2 init

import mod

dir2

dir1 >>> dir1 >>> dir1.dir2 >>> dir1.dir2.mod

dir1.x

x

mod.z

z >>> dir1.x 1 >>> dir1.dir2.y 2 >>> dir1.dir2.mod.z 3

Package Import Example | 565

www.it-ebooks.info

from Versus import with Packages import dir1 z

dir2

mod

>>> dir2.mod NameError: name 'dir2' is not defined >>> mod.z NameError: name 'mod' is not defined

from from import % python >>> from dir1.dir2 import mod dir1 init dir2 init in mod.py >>> mod.z 3 >>> from dir1.dir2.mod import z >>> z 3 >>> import dir1.dir2.mod as mod >>> mod.z 3

Why Use Package Imports?

import utilities

566 | Chapter 23: Module Packages

import as

www.it-ebooks.info

import database.client.utilities

PYTHONPATH

A Tale of Three Systems

import utilities

system1\ utilities.py main.py other.py

import utilities

system2\ utilities.py main.py other.py

Why Use Package Imports? | 567

www.it-ebooks.info

import utilities utilities.func('spam')

sys.path

root\ system1\ __init__.py utilities.py main.py other.py system2\ __init__.py utilities.py main.py other.py system3\ __init__.py myfile.py

import

568 | Chapter 23: Module Packages

www.it-ebooks.info

import system1.utilities import system2.utilities system1.utilities.function('spam') system2.utilities.function('eggs')

import

from

from

import

import utilities

Package Relative Imports import

Package Relative Imports | 569

www.it-ebooks.info

Changes in Python 3.0

from

from

from

Relative Import Basics from

from

sys.path

sys.path

570 | Chapter 23: Module Packages

www.it-ebooks.info

from . import spam

spam from .spam import name

spam name

from __future__ import

absolute_import

sys.path string

sys.path

import string

from __future__

string

from . import string

from import

import modname

from import

Package Relative Imports | 571

www.it-ebooks.info

from .string import name1, name2 from . import string from .. import string

Why Relative Imports?

mypkg\ __init__.py main.py string.py

mypkg main

mypkg.string string string

mypkg.main

mypkg.main

string mypkg.string

string

import spam sys.path string string

The relative imports solution in 3.0 import

572 | Chapter 23: Module Packages

www.it-ebooks.info

string sys.path import string

from from string import name

from from . import string

string import

from .string import name1, name2

string mypkg.main

name1

name2

mypkg.string

from .. import spam

mypkg mypkg

spam A.B.C

from . import D from .. import E from .D import X from ..E import X

Relative imports versus absolute package paths mypkg sys.path from mypkg import string

mypkg Package Relative Imports | 573

www.it-ebooks.info

sys.path from system.section.mypkg import string

from . import string

The Scope of Relative Imports

import

from

import from

sys.path PYTHONPATH

sys.path sys.path

574 | Chapter 23: Module Packages

www.it-ebooks.info

from

from

Module Lookup Rules Summary A sys.path

A.B.C

A.B.C A

sys.path B

A

C

B import

sys.path from

sys.path

from . import A

Relative Imports in Action

Imports outside packages string C:\test> c:\Python30\python >>> import string >>> string

Package Relative Imports | 575

www.it-ebooks.info

print('string' * 8) C:\test> c:\Python30\python >>> import string stringstringstringstringstringstringstringstring >>> string

>>> from . import string Traceback (most recent call last): File "", line 1, in ValueError: Attempted relative import in non-package

sys.path sys.path

import string print(string) C:\test> C:\python30\python main.py stringstringstringstringstringstringstringstring

Imports within packages string

C:\test> del string* C:\test> mkdir pkg import eggs print(eggs.X) X = 99999 import string print(string)

576 | Chapter 23: Module Packages

www.it-ebooks.info

import

C:\test> c:\Python26\python >>> import pkg.spam 99999 C:\test> c:\Python30\python >>> import pkg.spam Traceback (most recent call last): File "", line 1, in File "pkg\spam.py", line 1, in import eggs ImportError: No module named eggs

from . import eggs print(eggs.X) X = 99999 import string print(string) C:\test> c:\Python26\python >>> import pkg.spam 99999 C:\test> c:\Python30\python >>> import pkg.spam 99999

Imports are still relative to the CWD string string

print('string' * 8) from . import eggs

Package Relative Imports | 577

www.it-ebooks.info

print(eggs.X) X = 99999 import string print(string) C:\test> c:\Python30\python >>> import pkg.spam stringstringstringstringstringstringstringstring 99999

Selecting modules with relative and absolute imports string C:\test> del string* import string print(string) print('Ni' * 8)

string

C:\test> c:\Python30\python >>> import pkg.spam C:\test> c:\Python26\python >>> import pkg.spam NiNiNiNiNiNiNiNi

from . import string print(string) print('Ni' * 8) C:\test> c:\Python30\python >>> import pkg.spam NiNiNiNiNiNiNiNi

578 | Chapter 23: Module Packages

www.it-ebooks.info

C:\test> c:\Python26\python >>> import pkg.spam NiNiNiNiNiNiNiNi

from . import string C:\test> C:\python30\python >>> import pkg.spam ...text omitted... ImportError: cannot import name string

Imports are still relative to the CWD (again) sys.path

string

print('string' * 8) from . import string print(string) print('Ni' * 8)

string C:\test> c:\Python30\python >>> import pkg.spam NiNiNiNiNiNiNiNi

print('string' * 8)

Package Relative Imports | 579

www.it-ebooks.info

import string print(string) print('Ni' * 8) C:\test> c:\Python30\python >>> import pkg.spam stringstringstringstringstringstringstringstring C:\test> c:\Python26\python >>> import pkg.spam NiNiNiNiNiNiNiNi

sys.path

580 | Chapter 23: Module Packages

www.it-ebooks.info

Why You Will Care: Module Packages

from win32com.client import constants, Dispatch

client

win32com

from email.message import Message from tkinter.filedialog import askopenfilename from http.server import CGIHTTPRequestHandler

Chapter Summary

from

Chapter Summary | 581

www.it-ebooks.info

__name__

Test Your Knowledge: Quiz

import

from from mypkg import spam

from . import spam

Test Your Knowledge: Answers

from as

import from import

import

import

from

from import

from from mypkg import spam from . import spam sys.path

582 | Chapter 23: Module Packages

mypkg sys.path spam

www.it-ebooks.info

CHAPTER 24

Advanced Module Topics

__future__

__name__

sys.path

__name__

Data Hiding in Modules

583

www.it-ebooks.info

Minimizing from * Damage: _X and __all__ _X from * from *

import _X __all__ __all__ = ["Error", "encode", "decode"]

from * __all__

_X

__all__

_X __all__

from * _X

__all__

from * from *

__all__ from *

Enabling Future Language Features import from __future__ import featurename

featurename

584 | Chapter 24: Advanced Module Topics

generators print

www.it-ebooks.info

Mixed Usage Modes: __name__ and __main__ __name__ __name__ "__main__" __name__ __name__ tester def tester(): print("It's Christmas in Heaven...") if __name__ == '__main__': tester()

% python >>> import runme >>> runme.tester() It's Christmas in Heaven...

% python runme.py It's Christmas in Heaven...

__name__

__name__ __name__

__name__

Mixed Usage Modes: __name__ and __main__ | 585

www.it-ebooks.info

unittest

doctest

__name__

__name__

Unit Tests with __name__ __name__ def minmax(test, *args): res = args[0] for arg in args[1:]: if test(arg, res): res = arg return res def lessthan(x, y): return x < y def grtrthan(x, y): return x > y print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))

__name__

print('I am:', __name__) def minmax(test, *args): res = args[0] for arg in args[1:]: if test(arg, res): res = arg return res def lessthan(x, y): return x < y def grtrthan(x, y): return x > y if __name__ == '__main__': print(minmax(lessthan, 4, 2, 1, 5, 6, 3)) print(minmax(grtrthan, 4, 2, 1, 5, 6, 3))

586 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

__name__ __main__ % python min.py I am: __main__ 1 6

__main__ >>> import min I am: min >>> min.minmax(min.lessthan, 's', 'p', 'a', 'm') 'a'

Using Command-Line Arguments with __name__ __name__

sys.argv

""" Various specialized string display formatting utilities. Test me with canned self-test or command-line arguments. """ def commas(N): """ format positive integer-like N for display with commas between digit groupings: xxx,yyy,zzz """ digits = str(N) assert(digits.isdigit()) result = '' while digits: digits, last3 = digits[:-3], digits[-3:] result = (last3 + ',' + result) if result else last3 return result def money(N, width=0): """

Mixed Usage Modes: __name__ and __main__ | 587

www.it-ebooks.info

format number N for display with commas, 2 decimal digits, leading $ and sign, and optional padding: $ -xxx,yyy.zz """ sign = '-' if N < 0 else '' N = abs(N) whole = commas(int(N)) fract = ('%.2f' % N)[-2:] format = '%s%s.%s' % (sign, whole, fract) return '$%*s' % (width, format) if __name__ == '__main__': def selftest(): tests = 0, 1 # fails: −1, 1.23 tests += 12, 123, 1234, 12345, 123456, 1234567 tests += 2 ** 32, 2 ** 100 for test in tests: print(commas(test)) print('') tests = 0, 1, −1, 1.23, 1., 1.2, 3.14159 tests += 12.34, 12.344, 12.345, 12.346 tests += 2 ** 32, (2 ** 32 + .2345) tests += 1.2345, 1.2, 0.2345 tests += −1.2345, −1.2, −0.2345 tests += −(2 ** 32), −(2**32 + .2345) tests += (2 ** 100), −(2 ** 100) for test in tests: print('%s [%s]' % (money(test, 17), test)) import sys if len(sys.argv) == 1: selftest() else: print(money(float(sys.argv[1]), int(sys.argv[2])))

C:\misc> python formats.py 999999999 0 $999,999,999.00 C:\misc> python formats.py −999999999 0 $-999,999,999.00 C:\misc> python formats.py 123456789012345 0 $123,456,789,012,345.00 C:\misc> python formats.py −123456789012345 25 $ −123,456,789,012,345.00 C:\misc> python formats.py 123.456 0 $123.46

588 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

C:\misc> python formats.py −123.454 0 $-123.45 C:\misc> python formats.py ...canned tests: try this yourself...

>>> from formats import money, commas >>> money(123.456) '$123.46' >>> money(-9999999.99, 15) '$ −9,999,999.99' >>> X = 99999999999999999999 >>> '%s (%s)' % (commas(X), X) '99,999,999,999,999,999,999 (99999999999999999999)'

help >>> import formats >>> help(formats) Help on module formats: NAME

formats

FILE

c:\misc\formats.py

DESCRIPTION Various specialized string display formatting utilities. Test me with canned self-test or command-line arguments. FUNCTIONS commas(N) format positive integer-like N for display with commas between digit groupings: xxx,yyy,zzz money(N, width=0) format number N for display with commas, 2 decimal digits, leading $ and sign, and optional padding: $ -xxx,yyy.zz

getopt

optparse

input

Mixed Usage Modes: __name__ and __main__ | 589

www.it-ebooks.info

{,d}

Changing the Module Search Path PYTHONPATH sys.path sys

path

sys.path

>>> import sys >>> sys.path ['', 'C:\\users', 'C:\\Windows\\system32\\python30.zip', ...more deleted...] >>> sys.path.append('C:\\sourcedir') >>> import string

sys.path >>> sys.path = [r'd:\temp'] >>> sys.path.append('c:\\lp4e\\examples') >>> sys.path ['d:\\temp', 'c:\\lp4e\\examples'] >>> import string Traceback (most recent call last): File "", line 1, in ImportError: No module named string

string sys.path PYTHONPATH

590 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

The as Extension for import and from import

from import

import modulename as name

import modulename name = modulename del modulename

import

as from

from modulename import attrname as name

import import reallylongmodulename as name name.func() from module1 import utility as util1 from module2 import utility as util2 util1(); util2()

import dir1.dir2.mod as mod mod.func()

Modules Are Objects: Metaprograms

name

M __dict__

sys.modules

modules

sys

getattr object.attr attr

Modules Are Objects: Metaprograms | 591

www.it-ebooks.info

M.name M.__dict__['name'] sys.modules['M'].name getattr(M, 'name')

dir listing """ mydir.py: a module that lists the namespaces of other modules """ seplen = 60 sepchr = '-' def listing(module, verbose=True): sepline = sepchr * seplen if verbose: print(sepline) print('name:', module.__name__, 'file:', module.__file__) print(sepline) count = 0 for attr in module.__dict__: print('%02d) %s' % (count, attr), end = ' ') if attr.startswith('__'): print('') else: print(getattr(module, attr)) count += 1 if verbose: print(sepline) print(module.__name__, 'has %d names' % count) print(sepline) if __name__ == '__main__': import mydir listing(mydir)

__doc__

help

sys.modules global X; X=0 import sys; glob=sys.modules[__name__]; glob.X=0

592 | Chapter 24: Advanced Module Topics

global __name__

www.it-ebooks.info

>>> import mydir >>> help(mydir) Help on module mydir: NAME

mydir - mydir.py: a module that lists the namespaces of other modules

FILE

c:\users\veramark\mark\mydir.py

FUNCTIONS listing(module, verbose=True) DATA

sepchr = '-' seplen = 60

print

__future__

end C:\Users\veramark\Mark> c:\Python30\python mydir.py -----------------------------------------------------------name: mydir file: C:\Users\veramark\Mark\mydir.py -----------------------------------------------------------00) seplen 60 01) __builtins__ 02) __file__ 03) __package__ 04) listing 05) __name__ 06) sepchr 07) __doc__ -----------------------------------------------------------mydir has 8 names ------------------------------------------------------------

tkinter Tkinter >>> import mydir >>> import tkinter >>> mydir.listing(tkinter) -----------------------------------------------------------name: tkinter file: c:\PYTHON30\lib\tkinter\__init__.py -----------------------------------------------------------00) getdouble 01) MULTIPLE multiple 02) mainloop 03) Canvas 04) AtSelLast ...many more name omitted... 151) StringVar

Modules Are Objects: Metaprograms | 593

www.it-ebooks.info

152) ARC arc 153) At 154) NSEW nsew 155) SCROLL scroll -----------------------------------------------------------tkinter has 156 names ------------------------------------------------------------

getattr

mydir

Importing Modules by Name String import

from

import >>> import "string" File "", line 1 import "string" ^ SyntaxError: invalid syntax

x = "string" import x

string import

import exec

exec

>>> modname = "string" >>> exec("import " + modname) >>> string

mydir.listing PYTHONSTARTUP __main__

594 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

exec

eval

exec exec

import

__import__ __import__ >>> modname = "string" >>> string = __import__(modname) >>> string

Transitive Module Reloads

A A

B

C

A

B A

C B

B

C

C

import B import C % python >>> . . . >>> from imp import reload >>> reload(A)

reload

reload

A

__dict__

type __dict__

type Transitive Module Reloads | 595

www.it-ebooks.info

reload_all

types

type

visited

visited.add(module) """ reloadall.py: transitively reload nested modules """ import types from imp import reload def status(module): print('reloading ' + module.__name__) def transitive_reload(module, visited): if not module in visited: status(module) reload(module) visited[module] = None for attrobj in module.__dict__.values(): if type(attrobj) == types.ModuleType: transitive_reload(attrobj, visited) def reload_all(*args): visited = {} for arg in args: if type(arg) == types.ModuleType: transitive_reload(arg, visited) if __name__ == '__main__': import reloadall reload_all(reloadall)

reload_all reload

+

print

C:\misc> c:\Python30\python reloadall.py reloading reloadall reloading types

os tkinter

tkinter sys Tkinter tkinter

596 | Chapter 24: Advanced Module Topics

os

www.it-ebooks.info

>>> from reloadall import reload_all >>> import os, tkinter >>> reload_all(os) reloading os reloading copyreg reloading ntpath reloading genericpath reloading stat reloading sys reloading errno >>> reload_all(tkinter) reloading tkinter reloading _tkinter reloading tkinter._fix reloading sys reloading ctypes reloading os reloading copyreg reloading ntpath reloading genericpath reloading stat reloading errno reloading ctypes._endian reloading tkinter.constants

import b X = 1 import c Y = 2 Z = 3 C:\misc> C:\Python30\python >>> import a >>> a.X, a.b.Y, a.b.c.Z (1, 2, 3)

>>> from imp import reload >>> reload(a) >>> a.X, a.b.Y, a.b.c.Z (111, 2, 3) >>> from reloadall import reload_all >>> reload_all(a) reloading a

Transitive Module Reloads | 597

www.it-ebooks.info

reloading b reloading c >>> a.X, a.b.Y, a.b.c.Z (111, 222, 333)

Module Design Concepts

__main__

598 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

Module Gotchas

Statement Order Matters in Top-Level Code

Module Gotchas | 599

www.it-ebooks.info

func1() def func1(): print(func2()) func1() def func2(): return "Hello" func1()

func1 func2

func1

func1 func1

func1 def func2 def func1 func1 func2

def def def

from Copies Names but Doesn’t Link from from

X = 99 def printer(): print(X)

from

from nested1 import X, printer X = 88 printer() % python nested2.py 99

600 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

import

import nested1 nested1.X = 88 nested1.printer() % python nested3.py 88

from * Can Obscure the Meaning of Variables from module import * from * from *

>>> >>> >>> >>>

from module1 import * from module2 import * from module3 import * . . .

>>> func()

from

from * from *

import from

from

reload May Not Impact from Imports from

from from from

Module Gotchas | 601

www.it-ebooks.info

from from from module import X . . . from imp import reload reload(module) X

import

from

import module . . . from imp import reload reload(module) module.X

reload, from, and Interactive Testing from

from from module import function function(1, 2, 3)

from imp import reload reload(module)

from reload

module

function import

from imp import reload import module reload(module) function(1, 2, 3)

reload function module.function reload

from

602 | Chapter 24: Advanced Module Topics

www.it-ebooks.info

from imp import reload import module reload(module) from module import function function(1, 2, 3)

reload

from

from reload reload from reload import exec

Recursive from Imports May Not Work

import from recur1 recur2 recur1

recur2 recur1 Y recur2

X

import from import

X

Y

recur1

X = 1 import recur2 Y = 2 from recur1 import X from recur1 import Y C:\misc> C:\Python30\python >>> import recur1 Traceback (most recent call last): File "", line 1, in

Module Gotchas | 603

www.it-ebooks.info

File "recur1.py", line 2, in import recur2 File "recur2.py", line 2, in from recur1 import Y ImportError: cannot import name Y

recur1 recur2 recur1

recur2 from

import

from

from

Chapter Summary __future__ __name__

Test Your Knowledge: Quiz __name__

604 | Chapter 24: Advanced Module Topics

"__main__"

www.it-ebooks.info

sys.path

PYTHONPATH

__future__

Test Your Knowledge: Answers from * from

import __name__

"__main__"

import

exec

__import__ sys.path PYTHONPATH

Test Your Knowledge: Part V Exercises

countLines(name) file.readlines

len

countChars(name) file.read test(name) input

sys.argv

Test Your Knowledge: Part V Exercises | 605

www.it-ebooks.info

mymod

import PYTHONPATH test("mymod.py") test file.seek(0) from/from *

mymod

from from * mymod

test

__name__

from myclient myclient

"__main__"

mymod myclient

myclient mymod mymod import myclient

import mypkg.mymod

changer fg

606 | Chapter 24: Advanced Module Topics

__dict__

www.it-ebooks.info

recur1 recur2 recur2 sys.modules

recur1

python recur1.py

recur1 recur1 recur2

recur2 recur2

Test Your Knowledge: Part V Exercises | 607

www.it-ebooks.info

www.it-ebooks.info

PART VI

Classes and OOP

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 25

OOP: The Big Picture

class

611

www.it-ebooks.info

Why Use Classes?

612 | Chapter 25: OOP: The Big Picture

www.it-ebooks.info

OOP from 30,000 Feet

Attribute Inheritance Search

object.attribute

class attribute

attribute

object

object.attribute

OOP from 30,000 Feet | 613

www.it-ebooks.info

C1 C2

C3

I1

I2

C2 C1

I2.w

614 | Chapter 25: OOP: The Big Picture

C3

www.it-ebooks.info

object.attribute w

I2 I2, C1, C2, C3

w C3 C3.w w

w I2.w I2

w

C3 w x y

I1.x

I2.x

x

C1

I1.y

I2.y

y

C1

I1.z

I2.z

z

C2

I2.name

name

C1

C1

z

C2 y

C2

C3

I2

x C2 C1

Classes and Instances

Employee Employee

OOP from 30,000 Feet | 615

www.it-ebooks.info

computeSalary hoursWorked

Class Method Calls I2.w C3.w

I2.w I2 C3.w(I2)

C3.w I2.w()

giveRaise Employee

self bob.giveRaise() Employee.giveRaise(bob)

Coding Class Trees class class

class

616 | Chapter 25: OOP: The Big Picture

www.it-ebooks.info

class class C2: ... class C3: ... class C1(C2, C3): ... I1 = C1() I2 = C1()

class C1 C1

class

C1

class def self def

class

def

class C1(C2, C3): def setname(self, who): self.name = who I1 = C1() I2 = C1() I1.setname('bob') I2.setname('mel') print(I1.name)

OOP from 30,000 Feet | 617

www.it-ebooks.info

def def

class self

self self

self self

C1.setname I1.setname C1 setname

C1 name I1.name

I1 I1.setname

name class C1(C2, C3): def __init__(self, who): self.name = who I1 = C1('bob') I2 = C1('mel') print(I1.name)

__init__ self __init__

__init__

self self

618 | Chapter 25: OOP: The Big Picture

this

www.it-ebooks.info

intersect

& __and__

OOP Is About Code Reuse

class Employee: def computeSalary(self): ... def giveRaise(self): ... def promote(self): ... def retire(self): ...

OOP from 30,000 Feet | 619

www.it-ebooks.info

class Engineer(Employee): def computeSalary(self): ...

computeSalary Employee bob = Employee() mel = Engineer()

company = [bob, mel] for emp in company: print(emp.computeSalary())

computeSalary

def processor(reader, converter, writer): while 1: data = reader.read() if not data: break

company shelve

620 | Chapter 25: OOP: The Big Picture

www.it-ebooks.info

data = converter(data) writer.write(data)

read processor class Reader: def read(self): ... def other(self): ... class FileReader(Reader): def read(self): ... class SocketReader(Reader): def read(self): ... ... processor(FileReader(...), Converter, processor(SocketReader(...), Converter, processor(FtpReader(...), Converter,

write

FileWriter(...)) TapeWriter(...)) XmlWriter(...))

read

write

processor converter

Employee Reader

Writer

OOP from 30,000 Feet | 621

www.it-ebooks.info

Chapter Summary

Test Your Knowledge: Quiz

__init__

Test Your Knowledge: Answers

622 | Chapter 25: OOP: The Big Picture

www.it-ebooks.info

self

__init__

__init__

__init__ class

class

Test Your Knowledge: Answers | 623

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 26

Class Coding Basics

class

Classes Generate Multiple Instance Objects

625

www.it-ebooks.info

imp.reload

def

Class Objects Provide Default Behavior class class def

class

class

def

class

class class

def class

class object.name

def

Instance Objects Are Concrete Items

626 | Chapter 26: Class Coding Basics

class

www.it-ebooks.info

self self self

A First Example FirstClass

class

>>> class FirstClass: ... def setdata(self, value): ... self.data = value ... def display(self): ... print(self.data) ...

def class def

setdata

def display class FirstClass.setdata

FirstClass.display

def

>>> x = FirstClass() >>> y = FirstClass()

x FirstClass

y

Classes Generate Multiple Instance Objects | 627

www.it-ebooks.info

>>> x.setdata("King Arthur") >>> y.setdata(3.14159)

x

y

setdata

setdata self.data

FirstClass self x

y data self display

self.data display

x

y

>>> x.display() King Arthur >>> y.display() 3.14159

data

display

setdata data setdata

628 | Chapter 26: Class Coding Basics

www.it-ebooks.info

self >>> x.data = "New value" >>> x.display() New value

>>> x.anothername = "spam"

anothername x self

Classes Are Customized by Inheritance

class class

Classes Are Customized by Inheritance | 629

www.it-ebooks.info

object.attribute class X.attr

self self.attr

attr

self

A Second Example SecondClass

FirstClass

>>> class SecondClass(FirstClass): ... def display(self): ... print('Current value = "%s"' % self.data) ...

SecondClass

display FirstClass SecondClass display

display SecondClass

Class

SecondClass FirstClass

SecondClass display setdata >>> z = SecondClass() >>> z.setdata(42) >>> z.display() Current value = "42"

630 | Chapter 26: Class Coding Basics

First display

FirstClass SecondClass FirstClass

www.it-ebooks.info

SecondClass FirstClass

setdata display

Second

Class

SecondClass FirstClass

FirstClass x

>>> x.display() New value

FirstClass

Classes Are Attributes in Modules class FirstClass class from modulename import FirstClass class SecondClass(FirstClass): def display(self): ...

import modulename class SecondClass(modulename.FirstClass): def display(self): ...

Classes Are Customized by Inheritance | 631

www.it-ebooks.info

class

var = 1 def func(): ... class spam: ... class ham: ... class eggs: ...

class person: ...

import person x = person.person()

person.person person

person from

from person import person x = person()

import person x = person.Person()

632 | Chapter 26: Class Coding Basics

person

www.it-ebooks.info

Classes Can Intercept Python Operators

__X__

__add__ +

__add__

+

* giveRaise

+

promote

Classes Can Intercept Python Operators | 633

www.it-ebooks.info

__init__ __init__

self

A Third Example SecondClass __init__

self

__add__

ThirdClass

ThirdClass

+

__str__ str mul >>> class ThirdClass(SecondClass): ... def __init__(self, value): ... self.data = value ... def __add__(self, other): ... return ThirdClass(self.data + other) ... def __str__(self): ... return '[ThirdClass: %s]' % self.data ... def mul(self, other): ... self.data *= other ... >>> a = ThirdClass('abc') >>> a.display() Current value = "abc" >>> print(a) [ThirdClass: abc] >>> b = a + 'xyz' >>> b.display() Current value = "abcxyz" >>> print(b) [ThirdClass: abcxyz] >>> a.mul(3) >>> print(a) [ThirdClass: abcabcabc]

634 | Chapter 26: Class Coding Basics

www.it-ebooks.info

ThirdClass SecondClass SecondClass

display ThirdClass value

self.data

ThirdClass

__init__ ThirdClass setdata +

print __add__ __add__

self other + self

data

+

print

__str__ __str__

print display

__init__ __add__

__str__ class

__init__ __add__ ThirdClass

mul self

* *

Classes Can Intercept Python Operators | 635

www.it-ebooks.info

Why Use Operator Overloading?

__init__

__init__

The World’s Simplest Python Class class

>>> class rec: pass

pass

class >>> rec.name = 'Bob' >>> rec.age = 40

636 | Chapter 26: Class Coding Basics

www.it-ebooks.info

>>> print(rec.name) Bob

>>> x = rec() >>> y = rec()

>>> x.name, y.name ('Bob', 'Bob')

name

x

name

y

name >>> x.name = 'Sue' >>> rec.name, x.name, y.name ('Bob', 'Sue', 'Bob')

__dict__ __slots__ __X__ >>> rec.__dict__.keys() ['__module__', 'name', 'age', '__dict__', '__weakref__', '__doc__'] >>> list(x.__dict__.keys()) ['name']

The World’s Simplest Python Class | 637

www.it-ebooks.info

>>> list(y.__dict__.keys()) []

name x

name

age

y __class__

>>> x.__class__

__bases__ >>> rec.__bases__ (,)

class

def

class

>>> def upperName(self): ... return self.name.upper()

name >>> upperName(x) 'SUE'

>>> rec.method = upperName >>> x.method() 'SUE' >>> y.method() 'BOB' self

self self

638 | Chapter 26: Class Coding Basics

self

self

www.it-ebooks.info

>>> rec.method(x) 'SUE'

class self

Classes Versus Dictionaries

>>> >>> >>> >>> >>> >>> mel

rec = {} rec['name'] = 'mel' rec['age'] = 45 rec['job'] = 'trainer/writer'

>>> ... >>> >>> >>> >>> >>> 40

class rec: pass

print(rec['name'])

rec.name = 'mel' rec.age = 45 rec.job = 'trainer/writer' print(rec.age)

class class

>>> ... >>> >>> >>>

class rec: pass pers1 = rec() pers1.name = 'mel' pers1.job = 'trainer'

The World’s Simplest Python Class | 639

www.it-ebooks.info

>>> pers1.age = 40 >>> >>> pers2 = rec() >>> pers2.name = 'vls' >>> pers2.job = 'developer' >>> >>> pers1.name, pers2.name ('mel', 'vls')

name age

>>> class Person: ... def __init__(self, name, job): ... self.name = name ... self.job = job ... def info(self): ... return (self.name, self.job) ... >>> rec1 = Person('mel', 'trainer') >>> rec2 = Person('vls', 'developer') >>> >>> rec1.job, rec2.info() ('trainer', ('vls', 'developer'))

name

job

640 | Chapter 26: Class Coding Basics

www.it-ebooks.info

Chapter Summary class

Test Your Knowledge: Quiz

self

Test Your Knowledge: Answers

class

Test Your Knowledge: Answers | 641

www.it-ebooks.info

class class class

class class self class __init__ self self this

__init__

self

642 | Chapter 26: Class Coding Basics

__init__

www.it-ebooks.info

CHAPTER 27

A More Realistic Example

Person Manager

643

www.it-ebooks.info

Step 1: Making Instances Person

self Person

class Person:

Person

Coding Constructors Person

self self

__init__

class Person: def __init__(self, name, job, pay): self.name = name self.job = job self.pay = pay

self self pay

__init__

job self.job

644 | Chapter 27: A More Realistic Example

name job

www.it-ebooks.info

job

self.job

self.job=job

job __init__

job job pay

None None

0

pay

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay

job

pay

None

0

Person self

self

Testing As You Go

Step 1: Making Instances | 645

www.it-ebooks.info

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob.name, bob.pay) print(sue.name, sue.pay)

bob

job

pay

sue sue

__init__

name

pay

C:\misc> person.py Bob Smith 0 Sue Jones 100000

Person

bob

name

sue

sue bob

pay sue

bob

self

Using Code Two Ways print

646 | Chapter 27: A More Realistic Example

www.it-ebooks.info

__name__

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob.name, bob.pay) print(sue.name, sue.pay)

__name__

__main__

C:\misc> person.py Bob Smith 0 Sue Jones 100000 c:\misc> python Python 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) ... >>> import person >>>

Version Portability Note print print c:\misc> c:\python26\python person.py ('Bob Smith', 0) ('Sue Jones', 100000)

Step 1: Making Instances | 647

www.it-ebooks.info

print

print('{0} {1}'.format(bob.name, bob.pay)) print('%s %s' % (bob.name, bob.pay))

Step 2: Adding Behavior Methods

name

>>> name = 'Bob Smith' >>> name.split() ['Bob', 'Smith'] >>> name.split()[-1] 'Smith'

pay

>>> pay = 100000 >>> pay *= 1.10 >>> print(pay) 110000.0

Person bob.name

sue.pay

name

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay if __name__ == '__main__':

648 | Chapter 27: A More Realistic Example

pay

www.it-ebooks.info

bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob.name, bob.pay) print(sue.name, sue.pay) print(bob.name.split()[-1]) sue.pay *= 1.10 print(sue.pay)

bob sue

pay sue append

Bob Smith 0 Sue Jones 100000 Smith 110000.0

Coding Methods

Step 2: Adding Behavior Methods | 649

www.it-ebooks.info

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob.name, bob.pay) print(sue.name, sue.pay) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue.pay)

self lastName bob

self self

lastName giveRaise

self

sue

Bob Smith 0 Sue Jones 100000 Smith Jones 110000

sue int

int

float

Person round(N, 2) decimal %.2f

650 | Chapter 27: A More Realistic Example

{0:.2f}

www.it-ebooks.info

int

money sue

self bob.lastName() bob

self

sue.lastName() sue

self self

giveRaise

bob bob

giveRaise

giveRaise

percent

assert

Step 3: Operator Overloading

bob.name sue.pay

print(sue) sue Bob Smith 0 Sue Jones 100000 Smith Jones

Step 3: Operator Overloading | 651

www.it-ebooks.info

Providing Print Displays

__init__

__str__

__str__

__str__ __init__

__str__

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __str__(self): return '[Person: %s, %s]' % (self.name, self.pay) if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob) print(sue) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue)

%

652 | Chapter 27: A More Realistic Example

__str__

www.it-ebooks.info

__str__ [Person: Bob Smith, 0] [Person: Sue Jones, 100000] Smith Jones [Person: Sue Jones, 110000]

__repr__ __str__

__repr__ __str__

__repr__ __str__

Step 4: Customizing Behavior by Subclassing __str__

Coding Subclasses Person Person

Manager

giveRaise

class Manager(Person):

Manager Person

Manager

Step 4: Customizing Behavior by Subclassing | 653

www.it-ebooks.info

Person

Manager Manager Manager

Person Manager

giveRaise Person

class Manager(Person): def giveRaise(self, percent, bonus=.10):

Augmenting Methods: The Bad Way Manager giveRaise

Person

Manager

class Manager(Person): def giveRaise(self, percent, bonus=.10): self.pay = int(self.pay * (1 + percent + bonus))

giveRaise

Manager

Augmenting Methods: The Good Way giveRaise

class Manager(Person): def giveRaise(self, percent, bonus=.10): Person.giveRaise(self, percent + bonus)

654 | Chapter 27: A More Realistic Example

www.it-ebooks.info

self

instance.method(args...)

class.method(instance, args...)

self

giveRaise self

giveRaise Person Manager self.giveRaise() Manager giveRaise Manager self.giveRaise()

Person self Manager.giveRaise

giveRaise Person giveRaise

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __str__(self): return '[Person: %s, %s]' % (self.name, self.pay) class Manager(Person):

Step 4: Customizing Behavior by Subclassing | 655

www.it-ebooks.info

def giveRaise(self, percent, bonus=.10): Person.giveRaise(self, percent + bonus) if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob) print(sue) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue) tom = Manager('Tom Jones', 'mgr', 50000) tom.giveRaise(.10) print(tom.lastName()) print(tom)

Manager Manager [Person: Bob [Person: Sue Smith Jones [Person: Sue Jones [Person: Tom

Smith, 0] Jones, 100000] Jones, 110000] Jones, 60000]

bob giveRaise

sue

tom

Manager Person

Manager

lastName Person

Polymorphism in Action if __name__ == '__main__': ... print('--All three--') for object in (bob, sue, tom): object.giveRaise(.10) print(object)

[Person: Bob Smith, [Person: Sue Jones, Smith Jones [Person: Sue Jones, Jones [Person: Tom Jones, --All three--

0] 100000] 110000] 60000]

656 | Chapter 27: A More Realistic Example

__init__

Manager tom __str__

www.it-ebooks.info

[Person: Bob Smith, 0] [Person: Sue Jones, 121000] [Person: Tom Jones, 72000]

object

Person

giveRaise Manager giveRaise

Manager Person

bob

sue

tom

giveRaise sue

tom

giveRaise giveRaise __str__ Person Manager Person

Inherit, Customize, and Extend Manager Person

Manager giveRaise someThingElse

class Person: def lastName(self): ... def giveRaise(self): ... def __str__(self): ... class Manager(Person): def giveRaise(self, ...): ... def someThingElse(self, ...): ... tom = Manager() tom.lastName() tom.giveRaise() tom.someThingElse() print(tom)

Manager

someThingElse Person

Step 4: Customizing Behavior by Subclassing | 657

www.it-ebooks.info

Person

OOP: The Big Idea

giveRaise Manager Person Manager Person Manager

giveRaise Person

Manager

Person giveRaise Person

Manager

Step 5: Customizing Constructors, Too mgr Manager Manager Manager 658 | Chapter 27: A More Realistic Example

www.it-ebooks.info

__init__ giveRaise

Manager

mgr __init__

Person

Manager tom

mgr

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __str__(self): return '[Person: %s, %s]' % (self.name, self.pay) class Manager(Person): def __init__(self, name, pay): Person.__init__(self, name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): Person.giveRaise(self, percent + bonus) if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob) print(sue) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue) tom = Manager('Tom Jones', 50000) tom.giveRaise(.10) print(tom.lastName()) print(tom)

__init__ giveRaise self Person

__init__ __init__

Step 5: Customizing Constructors, Too | 659

www.it-ebooks.info

[Person: Bob [Person: Sue Smith Jones [Person: Sue Jones [Person: Tom

Smith, 0] Jones, 100000] Jones, 110000] Jones, 60000]

OOP Is Simpler Than You May Think

self

Other Ways to Combine Classes

660 | Chapter 27: A More Realistic Example

www.it-ebooks.info

Manager Person __getattr__ getattr

giveRaise

Manager

class Person: ...same... class Manager: def __init__(self, name, pay): self.person = Person(name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): self.person.giveRaise(percent + bonus) def __getattr__(self, attr): return getattr(self.person, attr) def __str__(self): return str(self.person) if __name__ == '__main__': ...same...

Manager

Manager

Person __str__ Manager

Manager

Step 5: Customizing Constructors, Too | 661

www.it-ebooks.info

Department

... bob = Person(...) sue = Person(...) tom = Manager(...) class Department: def __init__(self, *args): self.members = list(args) def addMember(self, person): self.members.append(person) def giveRaises(self, percent): for person in self.members: person.giveRaise(percent) def showAll(self): for person in self.members: print(person) development = Department(bob, sue) development.addMember(tom) development.giveRaises(.10) development.showAll()

Department Person Manager

Person

Manager

Catching Built-in Attributes in 3.0 Manager __str__ __str__

__str__

662 | Chapter 27: A More Realistic Example

__getitem__

www.it-ebooks.info

__getattr__

__getattribute__ __str__

Manager Person

__str__ __str__

__getattr__ __getattr__

__getattribute__

Private

Step 6: Using Introspection Tools

tom Manager

Manager

Person Person

__str__ tom __str__

Manager __init__

mgr Person __str__

__str__

Step 6: Using Introspection Tools | 663

www.it-ebooks.info

Special Class Attributes

instance.__class__ __name__ __bases__

object.__dict__

Person >>> from person import Person >>> bob = Person('Bob Smith') >>> print(bob) [Person: Bob Smith, 0] >>> bob.__class__ >>> bob.__class__.__name__ 'Person' >>> list(bob.__dict__.keys()) ['pay', 'job', 'name'] >>> for key in bob.__dict__: print(key, '=>', bob.__dict__[key]) pay => 0 job => None name => Bob Smith >>> for key in bob.__dict__: print(key, '=>', getattr(bob, key)) pay => 0

664 | Chapter 27: A More Realistic Example

from

www.it-ebooks.info

job => None name => Bob Smith

__dict__

__slots__

__dict__

A Generic Display Tool

__str__

__str__

"Assorted class utilities and tools" class AttrDisplay: """ Provides an inheritable print overload method that displays instances with their class names and a name=value pair for each attribute stored on the instance itself (but not attrs inherited from its classes). Can be mixed into any class, and will work on any instance. """ def gatherAttrs(self): attrs = [] for key in sorted(self.__dict__): attrs.append('%s=%s' % (key, getattr(self, key))) return ', '.join(attrs) def __str__(self): return '[%s: %s]' % (self.__class__.__name__, self.gatherAttrs()) if __name__ == '__main__': class TopTest(AttrDisplay): count = 0 def __init__(self): self.attr1 = TopTest.count self.attr2 = TopTest.count+1 TopTest.count += 2

Step 6: Using Introspection Tools | 665

www.it-ebooks.info

class SubTest(TopTest): pass X, Y = TopTest(), SubTest() print(X) print(Y)

help

__str__ C:\misc> classtools.py [TopTest: attr1=0, attr2=1] [SubTest: attr1=2, attr2=3]

Instance Versus Class Attributes classtools self self __dict__ count __class__ __dict__ __bases__ dir __dict__ >>> from person import Person >>> bob = Person('Bob Smith')

>>> bob.__dict__.keys() ['pay', 'job', 'name'] >>> dir(bob) ['__doc__', '__init__', '__module__', '__str__', 'giveRaise', 'job', 'lastName', 'name', 'pay']

666 | Chapter 27: A More Realistic Example

dir

www.it-ebooks.info

>>> list(bob.__dict__.keys()) ['pay', 'job', 'name'] >>> dir(bob) ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', ...more lines omitted... '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'giveRaise', 'job', 'lastName', 'name', 'pay']

dict.keys dir

dir

__X__

dir

dir

Name Considerations in Tool Classes AttrDisplay

classtools

__str__

gatherAttrs gather

Attrs gatherAttrs

TopTest

class TopTest(AttrDisplay): .... def gatherAttrs(self): return 'Spam'

__str__ _gatherAttrs _gatherAttrs

Step 6: Using Introspection Tools | 667

www.it-ebooks.info

__gatherAttrs

Our Classes’ Final Form __str__ Person

print Manager Manager AttrDisplay

__str__

from classtools import AttrDisplay class Person(AttrDisplay): """ Create and process person records """ def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) class Manager(Person): """ A customized Person with special requirements """ def __init__(self, name, pay): Person.__init__(self, name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): Person.giveRaise(self, percent + bonus) if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob) print(sue) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue) tom = Manager('Tom Jones', 50000) tom.giveRaise(.10)

668 | Chapter 27: A More Realistic Example

Person

www.it-ebooks.info

print(tom.lastName()) print(tom)

#

AttrDisplay

__str__ self tom

Manager

Person Manager

C:\misc> person.py [Person: job=None, name=Bob Smith, pay=0] [Person: job=dev, name=Sue Jones, pay=100000] Smith Jones [Person: job=dev, name=Sue Jones, pay=110000] Jones [Manager: job=mgr, name=Tom Jones, pay=60000]

Step 7 (Final): Storing Objects in a Database

Step 7 (Final): Storing Objects in a Database | 669

www.it-ebooks.info

Pickles and Shelves pickle dbm

anydbm

shelve

pickle

pickle

pickle

pickle shelve shelve pickle

dbm

len in

dict.keys

670 | Chapter 27: A More Realistic Example

shelve pickle

www.it-ebooks.info

Storing Objects on a Shelve Database

from

import person bob = person.Person(...) from person import Person bob = Person(...)

from

shelve

from person import Person, Manager bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) tom = Manager('Tom Jones', 50000) import shelve db = shelve.open('persondb') for object in (bob, sue, tom): db[object.name] = object db.close()

os time

Step 7 (Final): Storing Objects in a Database | 671

www.it-ebooks.info

C:\misc> makedb.py

Exploring Shelves Interactively open

shelve.open()

shelve bsddb

bsddb

>>> import glob >>> glob.glob('person*') ['person.py', 'person.pyc', 'persondb.bak', 'persondb.dat', 'persondb.dir']

>>> print(open('persondb.dir').read()) 'Tom Jones', (1024, 91) ...more omitted... >>> print(open('persondb.dat', 'rb').read()) b'\x80\x03cperson\nPerson\nq\x00)\x81q\x01}q\x02(X\x03\x00\x00\x00payq\x03K... ...more omitted...

672 | Chapter 27: A More Realistic Example

www.it-ebooks.info

>>> import shelve >>> db = shelve.open('persondb') >>> len(db) 3 >>> list(db.keys()) ['Tom Jones', 'Sue Jones', 'Bob Smith'] >>> bob = db['Bob Smith'] >>> print(bob) [Person: job=None, name=Bob Smith, pay=0] >>> bob.lastName() 'Smith' >>> for key in db: print(key, '=>', db[key]) Tom Jones => [Manager: job=mgr, name=Tom Jones, pay=50000] Sue Jones => [Person: job=dev, name=Sue Jones, pay=100000] Bob Smith => [Person: job=None, name=Bob Smith, pay=0] >>> for key in sorted(db): print(key, '=>', db[key]) Bob Smith => [Person: job=None, name=Bob Smith, pay=0] Sue Jones => [Person: job=dev, name=Sue Jones, pay=100000] Tom Jones => [Manager: job=mgr, name=Tom Jones, pay=50000]

Person

Manager bob lastName

Person self bob bob

sys.path __main__

Step 7 (Final): Storing Objects in a Database | 673

www.it-ebooks.info

Updating Objects on a Shelve

__str__ giveRaise

import shelve db = shelve.open('persondb') for key in sorted(db): print(key, '\t=>', db[key]) sue = db['Sue Jones'] sue.giveRaise(.10) db['Sue Jones'] = sue db.close()

sue

sue c:\misc> updatedb.py Bob Smith => [Person: job=None, name=Bob Smith, pay=0] Sue Jones => [Person: job=dev, name=Sue Jones, pay=100000] Tom Jones => [Manager: job=mgr, name=Tom Jones, pay=50000] c:\misc> updatedb.py Bob Smith => [Person: job=None, name=Bob Smith, pay=0] Sue Jones => [Person: job=dev, name=Sue Jones, pay=110000] Tom Jones => [Manager: job=mgr, name=Tom Jones, pay=50000] c:\misc> updatedb.py Bob Smith => [Person: job=None, name=Bob Smith, pay=0] Sue Jones => [Person: job=dev, name=Sue Jones, pay=121000] Tom Jones => [Manager: job=mgr, name=Tom Jones, pay=50000] c:\misc> updatedb.py

674 | Chapter 27: A More Realistic Example

www.it-ebooks.info

Bob Smith Sue Jones Tom Jones

=> [Person: job=None, name=Bob Smith, pay=0] => [Person: job=dev, name=Sue Jones, pay=133100] => [Manager: job=mgr, name=Tom Jones, pay=50000]

shelve

pickle

c:\misc> python >>> import shelve >>> db = shelve.open('persondb') >>> rec = db['Sue Jones'] >>> print(rec) [Person: job=dev, name=Sue Jones, pay=146410] >>> rec.lastName() 'Jones' >>> rec.pay 146410

pickle

shelve

Future Directions

tkinter Tkinter

Future Directions | 675

www.it-ebooks.info

tkinter

shelve

shelve

676 | Chapter 27: A More Realistic Example

www.it-ebooks.info

Chapter Summary

Test Your Knowledge: Quiz Manager Person giveRaise

Test Your Knowledge: Quiz | 677

www.it-ebooks.info

__dict__

Test Your Knowledge: Answers AttrDisplay

Manager classtools Person

__str__

__str__ Manager AttrDisplay

class pickle

__class__ lastName giveRaise

__str__

678 | Chapter 27: A More Realistic Example

www.it-ebooks.info

__str__ __init__

Manager

print

Person

name address birthday phone email sendmail smptlib AttrDisplay

Test Your Knowledge: Answers | 679

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 28

Class Coding Details

The class Statement class class class

def

class def

class

class

General Form class

681

www.it-ebooks.info

class (superclass,...): data = value def method(self,...): self.member = value

class __init__

Example class class class

class class

class print = if def class

class class

def

>>> class SharedData: ... spam = 42 ... >>> x = SharedData() >>> y = SharedData() >>> x.spam, y.spam (42, 42)

682 | Chapter 28: Class Coding Details

www.it-ebooks.info

spam

class

>>> SharedData.spam = 99 >>> x.spam, y.spam, SharedData.spam (99, 99, 99)

spam >>> x.spam = 88 >>> x.spam, y.spam, SharedData.spam (88, 99, 99)

y.spam x.spam

x

class MixedNames: data = 'spam' def __init__(self, value): self.data = value def display(self): print(self.data, MixedNames.data)

def = class

data data data data self.data

>>> x = MixedNames(1) >>> y = MixedNames(2)

class

__setattr__

The class Statement | 683

www.it-ebooks.info

>>> x.display(); y.display() 1 spam 2 spam

data __init__

self.data data

class

display

self

x.data

self.data Mixed

Names.data

Methods def

instance.method(args...)

class.method(instance, args...)

self

684 | Chapter 28: Class Coding Details

class

www.it-ebooks.info

self this

self self self

Method Example class NextClass: def printer(self, text): self.message = text print(self.message)

printer

class printer

>>> x = NextClass() >>> x.printer('instance call') instance call >>> x.message 'instance call'

printer self x

text printer

'instance call' self self

printer self >>> NextClass.printer(x, 'class call') class call >>> x.message 'class call'

Methods | 685

www.it-ebooks.info

>>> NextClass.printer('bad call') TypeError: unbound method printer() must be called with NextClass instance...

Calling Superclass Constructors __init__ __init__ __init__ class Super: def __init__(self, x): ...default code... class Sub(Super): def __init__(self, x, y): Super.__init__(self, x) ...custom code... I = Sub(1, 2)

Manager

Other Method Call Possibilities

__init__

686 | Chapter 28: Class Coding Details

www.it-ebooks.info

Inheritance class

object.attr

object object

attr

self

Attribute Tree Construction self class class

class

Specializing Inherited Methods

class class __setattr__ __X

Inheritance | 687

www.it-ebooks.info

>>> class Super: ... def method(self): ... print('in Super.method') ... >>> class Sub(Super): ... def method(self): ... print('starting Sub.method') ... Super.method(self) ... print('ending Sub.method') ...

Sub Super Sub

method Sub.method

688 | Chapter 28: Class Coding Details

Super Super.method

www.it-ebooks.info

>>> x = Super() >>> x.method() in Super.method >>> x = Sub() >>> x.method() starting Sub.method in Super.method ending Sub.method

Class Interface Techniques Super method

delegate

action

Inheritor Super Replacer Super

method

Extender Super

method

Provider action

Super

delegate

class Super: def method(self): print('in Super.method') def delegate(self): self.action() class Inheritor(Super): pass class Replacer(Super): def method(self): print('in Replacer.method') class Extender(Super): def method(self): print('starting Extender.method') Super.method(self) print('ending Extender.method')

Inheritance | 689

www.it-ebooks.info

class Provider(Super): def action(self): print('in Provider.action') if __name__ == '__main__': for klass in (Inheritor, Replacer, Extender): print('\n' + klass.__name__ + '...') klass().method() print('\nProvider...') x = Provider() x.delegate()

for __name__

% python specialize.py Inheritor... in Super.method Replacer... in Replacer.method Extender... starting Extender.method in Super.method ending Extender.method Provider... in Provider.action

Abstract Superclasses Provider delegate

Provider x.delegate Provider

delegate

Super x

self Super.delegate self action delegate

690 | Chapter 28: Class Coding Details

self.action self Provider

Provider

www.it-ebooks.info

NotImplementedError

assert raise

assert class Super: def delegate(self): self.action() def action(self): assert False, 'action must be defined!' >>> X = Super() >>> X.delegate() AssertionError: action must be defined!

assert

NotImplementedError class Super: def delegate(self): self.action() def action(self): raise NotImplementedError('action must be defined!') >>> X = Super() >>> X.delegate() NotImplementedError: action must be defined!

>>> class Sub(Super): pass ... >>> X = Sub() >>> X.delegate() NotImplementedError: action must be defined! >>> class Sub(Super): ... def action(self): print('spam') ... >>> X = Sub() >>> X.delegate() spam

Inheritance | 691

www.it-ebooks.info

Python 2.6 and 3.0 Abstract Superclasses

class from abc import ABCMeta, abstractmethod class Super(metaclass=ABCMeta): @abstractmethod def method(self, ...): pass

class Super: __metaclass__ = ABCMeta @abstractmethod def method(self, ...): pass

>>> from abc import ABCMeta, abstractmethod >>> >>> class Super(metaclass=ABCMeta): ... def delegate(self): ... self.action() ... @abstractmethod ... def action(self): ... pass ... >>> X = Super() TypeError: Can't instantiate abstract class Super with abstract methods action >>> class Sub(Super): pass ... >>> X = Sub() TypeError: Can't instantiate abstract class Sub with abstract methods action >>> class Sub(Super): ... def action(self): print('spam') ... >>> X = Sub() >>> X.delegate() spam

692 | Chapter 28: Class Coding Details

@

www.it-ebooks.info

Namespaces: The Whole Story

X object.X

Simple Names: Global Unless Assigned X = value X X X

Attribute Names: Object Namespaces

Namespaces: The Whole Story | 693

www.it-ebooks.info

object.X = value X

object

object.X X X

object

object

The “Zen” of Python Namespaces: Assignments Classify Names

X = 11 def f(): print(X) def g(): X = 22 print(X) class C: X = 33 def m(self): X = 44 self.X = 55

X X X 22 55

11 33

44 X

X

694 | Chapter 28: Class Coding Details

www.it-ebooks.info

if __name__ == '__main__': print(X) f() g() print(X) obj = C() print(obj.X) obj.m() print(obj.X) print(C.X) #print(C.m.X) #print(g.X)

X C.X def def

import manynames X = 66 print(X) print(manynames.X) manynames.f() manynames.g() print(manynames.C.X) I = manynames.C() print(I.X) I.m() print(I.X)

manynames.f()

X

manynames

X

X

I.m() __init__

Namespaces: The Whole Story | 695

www.it-ebooks.info

global

nonlocal

X = 11 def g1(): print(X) def g2(): global X X = 22 def h1(): X = 33 def nested(): print(X) def h2(): X = 33 def nested(): nonlocal X X = 44

Namespace Dictionaries __dict__

>>> class super: ... def hello(self): ... self.data1 = 'spam' ... >>> class sub(super): ... def hola(self):

696 | Chapter 28: Class Coding Details

www.it-ebooks.info

self.data2 = 'eggs'

... ...

__class__ __bases__

>>> X = sub() >>> X.__dict__ {} >>> X.__class__ >>> sub.__bases__ (,) >>> super.__bases__ (,)

self

self >>> Y = sub() >>> X.hello() >>> X.__dict__ {'data1': 'spam'} >>> X.hola() >>> X.__dict__ {'data1': 'spam', 'data2': 'eggs'} >>> sub.__dict__.keys() ['__module__', '__doc__', 'hola'] >>> super.__dict__.keys() ['__dict__', '__module__', '__weakref__', 'hello', '__doc__'] >>> Y.__dict__ {}

__doc__

Namespaces: The Whole Story | 697

www.it-ebooks.info

Y X

>>> X.data1, X.__dict__['data1'] ('spam', 'spam') >>> X.data3 = 'toast' >>> X.__dict__ {'data1': 'spam', 'data3': 'toast', 'data2': 'eggs'} >>> X.__dict__['data3'] = 'ham' >>> X.data3 'ham'

X.hello

X.__dict__['hello'] dir dir(object) object.__dict__.keys()

dir dir object

>>> X.__dict__, Y.__dict__ ({'data1': 'spam', 'data3': 'ham', 'data2': 'eggs'}, {}) >>> list(X.__dict__.keys()) ['data1', 'data3', 'data2']

>>>> dir(X) ['__doc__', '__module__', 'data1', 'data2', 'data3', 'hello', 'hola'] >>> dir(sub) ['__doc__', '__module__', 'hello', 'hola'] >>> dir(super) ['__doc__', '__module__', 'hello']

dir dir dir

698 | Chapter 28: Class Coding Details

www.it-ebooks.info

>>> dir(X) ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', ...more omitted... 'data1', 'data2', 'data3', 'hello', 'hola'] >>> dir(sub) ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', ...more omitted... 'hello', 'hola'] >>> dir(super) ['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__', ...more omitted... 'hello' ]

Namespace Links __class__

__bases__

""" Climb inheritance trees using namespace links, displaying higher superclasses with indentation """ def classtree(cls, indent): print('.' * indent + cls.__name__) for supercls in cls.__bases__: classtree(supercls, indent+3) def instancetree(inst): print('Tree of %s' % inst) classtree(inst.__class__, 3) def selftest(): class A: class B(A): class C(A): class D(B,C): class E: class F(D,E):

pass pass pass pass pass pass

Namespaces: The Whole Story | 699

www.it-ebooks.info

instancetree(B()) instancetree(F()) if __name__ == '__main__': selftest()

classtree __name__ __bases__ cls

indent

classtree

C:\misc> c:\python26\python classtree.py Tree of ...B ......A Tree of ...F ......D .........B ............A .........C ............A ......E

object

C:\misc> c:\python30\python classtree.py Tree of ...B ......A .........object Tree of ...F ......D .........B ............A ...............object .........C ............A ...............object ......E .........object

700 | Chapter 28: Class Coding Details

www.it-ebooks.info

C:\misc> c:\python30\python >>> class Emp: pass ... >>> class Person(Emp): pass >>> bob = Person() >>> import classtree >>> classtree.instancetree(bob) Tree of ...Person ......Emp .........object

Documentation Strings Revisited __doc__ def

"I am: docstr.__doc__" def func(args): "I am: docstr.func.__doc__" pass class spam: "I am: spam.__doc__ or docstr.spam.__doc__" def method(self, arg): "I am: spam.method.__doc__ or self.method.__doc__" pass

Documentation Strings Revisited | 701

www.it-ebooks.info

__doc__ >>> import docstr >>> docstr.__doc__ 'I am: docstr.__doc__' >>> docstr.func.__doc__ 'I am: docstr.func.__doc__' >>> docstr.spam.__doc__ 'I am: spam.__doc__ or docstr.spam.__doc__' >>> docstr.spam.method.__doc__ 'I am: spam.method.__doc__ or self.method.__doc__'

object

>>> help(docstr) Help on module docstr: NAME

docstr - I am: docstr.__doc__

FILE

c:\misc\docstr.py

CLASSES spam class spam | I am: spam.__doc__ or docstr.spam.__doc__ | | Methods defined here: | | method(self, arg) | I am: spam.method.__doc__ or self.method.__doc__ FUNCTIONS func(args) I am: docstr.func.__doc__

#

702 | Chapter 28: Class Coding Details

www.it-ebooks.info

Classes Versus Modules

class

Chapter Summary

Test Your Knowledge: Quiz class

Test Your Knowledge: Quiz | 703

www.it-ebooks.info

__init__

Test Your Knowledge: Answers

X = Y

class Class.X

__init__ __init__

self

Superclass.__init__(self, ...)

self Superclass.method(self, ...)

704 | Chapter 28: Class Coding Details

www.it-ebooks.info

CHAPTER 29

Operator Overloading

The Basics

705

www.it-ebooks.info

Constructors and Expressions: __init__ and __sub__ Number __init__ __sub__ class Number: def __init__(self, start): self.data = start def __sub__(self, other): return Number(self.data - other) >>> >>> >>> >>> 3

from number import Number X = Number(5) Y = X – 2 Y.data

__init__

Common Operator Overloading Methods

__add__ __radd__

__iadd__

Method

Implements

Called for

__init__

Constructor

Object creation: X = Class(args)

__del__

Destructor

Object reclamation of X

__add__

Operator +

X + Y, X += Y if no __iadd__

__or__

Operator | (bitwise OR)

X | Y, X |= Y if no __ior__

__repr__, __str__

Printing, conversions

print(X), repr(X), str(X)

__call__

Function calls

X(*args, **kargs)

__getattr__

Attribute fetch

X.undefined

__setattr__

Attribute assignment

X.any = value

__delattr__

Attribute deletion

del X.any

__getattribute__

Attribute fetch

X.any

706 | Chapter 29: Operator Overloading

www.it-ebooks.info

Method

Implements

Called for

__getitem__

Indexing, slicing, iteration

X[key], X[i:j], for loops and other iterations if no __iter__

__setitem__

Index and slice assignment

X[key] = value, X[i:j] = sequence

__delitem__

Index and slice deletion

del X[key], del X[i:j]

__len__

Length

len(X), truth tests if no __bool__

__bool__

Boolean tests

bool(X), truth tests (named __nonzero__ in 2.6)

__lt__, __gt__, __le__, __ge__, __eq__, __ne__

Comparisons

X < Y, X > Y, X = Y, X == Y, X != Y (or else __cmp__ in 2.6 only)

__radd__

Right-side operators

Other + X

__iadd__

In-place augmented operators

X += Y (or else __add__)

__iter__, __next__

Iteration contexts

I=iter(X), next(I); for loops, in if no __contains__, all comprehensions, map(F,X), others (__next__ is named next in 2.6)

__contains__

Membership test

item in X (any iterable)

__index__

Integer value

hex(X), bin(X), oct(X), O[X], O[X:] (replaces Python 2 __oct__, __hex__)

__enter__, __exit__

Context manager (Chapter 33)

with obj as var:

__get__, __set__, __delete__

Descriptor attributes (Chapter 37)

X.attr, X.attr = value, del X.attr

__new__

Creation (Chapter 39)

Object creation, before __init__

__add__ __add__

+

object

__init__ __init__

The Basics | 707

www.it-ebooks.info

Indexing and Slicing: __getitem__ and __setitem__ __getitem__ X X[i]

__getitem__

X

>>> class Indexer: ... def __getitem__(self, index): ... return index ** 2 ... >>> X = Indexer() >>> X[2] 4 >>> for i in range(5): ... print(X[i], end=' ') ... 0 1 4 9 16

Intercepting Slices __getitem__

>>> >>> [7, >>> [6, >>> [5, >>> [5,

L = [5, 6, 7, 8, 9] L[2:4] 8] L[1:] 7, 8, 9] L[:-1] 6, 7, 8] L[::2] 7, 9]

>>> [7, >>> [6, >>> [5, >>> [5,

L[slice(2, 4)] 8] L[slice(1, None)] 7, 8, 9] L[slice(None, −1)] 6, 7, 8] L[slice(None, None, 2)] 7, 9]

708 | Chapter 29: Operator Overloading

www.it-ebooks.info

__getitem__

>>> class Indexer: ... data = [5, 6, 7, 8, 9] ... def __getitem__(self, index): ... print('getitem:', index) ... return self.data[index] ... >>> X = Indexer() >>> X[0] getitem: 0 5 >>> X[1] getitem: 1 6 >>> X[-1] getitem: −1 9

>>> X[2:4] getitem: slice(2, 4, None) [7, 8] >>> X[1:] getitem: slice(1, None, None) [6, 7, 8, 9] >>> X[:-1] getitem: slice(None, −1, None) [5, 6, 7, 8] >>> X[::2] getitem: slice(None, None, 2) [5, 7, 9]

__setitem__

def __setitem__(self, index, value): ... self.data[index] = value

__getitem__

Slicing and Indexing in Python 2.6 __getslice__

__setslice__

__getitem__

__setitem__

Indexing and Slicing: __getitem__ and __setitem__ | 709

www.it-ebooks.info

__getitem__

__setitem__

__index__

>>> class C: ... def __index__(self): ... return 255 ... >>> X = C() >>> hex(X) '0xff' >>> bin(X) '0b11111111' >>> oct(X) '0o377'

__getitem__ >>> ('C' * 256)[255] 'C' >>> ('C' * 256)[X] 'C' >>> ('C' * 256)[X:] 'C'

hex

oct

__hex__

__oct__

Index Iteration: __getitem__ for __getitem__ for

__getitem__

>>> class stepper: ... def __getitem__(self, i): ... return self.data[i] ...

710 | Chapter 29: Operator Overloading

www.it-ebooks.info

>>> >>> >>> >>> 'p' >>> ... ... S p

X = stepper() X.data = "Spam" X[1] for item in X: print(item, end=' ') a m

for

in

map __getitem__ >>> 'p' in X True >>> [c for c in X] ['S', 'p', 'a', 'm'] >>> list(map(str.upper, X)) ['S', 'P', 'A', 'M'] >>> (a, b, c, d) = X >>> a, c, d ('S', 'a', 'm') >>> list(X), tuple(X), ''.join(X) (['S', 'p', 'a', 'm'], ('S', 'p', 'a', 'm'), 'Spam') >>> X

Iterator Objects: __iter__ and __next__ __getitem__ __iter__ __getitem__

__iter__

__getitem__ iter

__iter__ __next__

Iterator Objects: __iter__ and __next__ | 711

www.it-ebooks.info

StopIteration __getitem__ IndexError

__iter__ next next(I)

I.__next__()

I.__next__() next(I) I.next() I.__next__()

I.next()

User-Defined Iterators __iter__

class Squares: def __init__(self, start, stop): self.value = start - 1 self.stop = stop def __iter__(self): return self def __next__(self): if self.value == self.stop: raise StopIteration self.value += 1 return self.value ** 2 % python >>> from iters import Squares >>> for i in Squares(1, 5): ... print(i, end=' ') ... 1 4 9 16 25

self

raise >>> >>> >>> 1 >>> 4

X = Squares(1, 5) I = iter(X) next(I) next(I)

712 | Chapter 29: Operator Overloading

__next__

www.it-ebooks.info

...more omitted... >>> next(I) 25 >>> next(I) StopIteration

__getitem__ for 0..N start..stop

__iter__

next

__getitem__ __iter__ __getitem__

>>> X = Squares(1, 5) >>> X[1] AttributeError: Squares instance has no attribute '__getitem__'

__iter__ __getitem__ __getitem__ __iter__ Squares >>> >>> [1, >>> [] >>> [1, >>> [1,

X = Squares(1, 5) [n for n in X] 4, 9, 16, 25] [n for n in X] [n for n in Squares(1, 5)] 4, 9, 16, 25] list(Squares(1, 3)) 4, 9]

>>> def gsquares(start, stop): ... for i in range(start, stop+1): ... yield i ** 2 ... >>> for i in gsquares(1, 5): ... print(i, end=' ') ... 1 4 9 16 25

for

map

Iterator Objects: __iter__ and __next__ | 713

www.it-ebooks.info

>>> [x ** 2 for x in range(1, 6)] [1, 4, 9, 16, 25]

Multiple Iterators on One Object

>>> S = 'ace' >>> for x in S: ... for y in S: ... print(x + y, end=' ') ... aa ac ae ca cc ce ea ec ee

iter

map range

__iter__ self

class SkipIterator: def __init__(self, wrapped): self.wrapped = wrapped self.offset = 0 def __next__(self): if self.offset >= len(self.wrapped): raise StopIteration else: item = self.wrapped[self.offset] self.offset += 2 return item class SkipObject:

714 | Chapter 29: Operator Overloading

zip

www.it-ebooks.info

def __init__(self, wrapped): self.wrapped = wrapped def __iter__(self): return SkipIterator(self.wrapped) if __name__ == '__main__': alpha = 'abcdef' skipper = SkipObject(alpha) I = iter(skipper) print(next(I), next(I), next(I)) for x in skipper: for y in skipper: print(x + y, end=' ')

% python skipper.py a c e aa ac ae ca cc ce ea ec ee

Squares Squares SkipObject

>>> S = 'abcdef' >>> for x in S[::2]: ... for y in S[::2]: ... print(x + y, end=' ') ... aa ac ae ca cc ce ea ec ee

>>> S = 'abcdef' >>> S = S[::2] >>> S 'ace' >>> for x in S: ... for y in S: ... print(x + y, end=' ') ... aa ac ae ca cc ce ea ec ee

Iterator Objects: __iter__ and __next__ | 715

www.it-ebooks.info

Membership: __contains__, __iter__, and __getitem__

__lt__ __cmp__ __cmp__ __bool__

True False __len__

True __nonzero__

__bool__ in __getitem__ __contains__

__iter__ __iter__ __contains__

class Iters: def __init__(self, value): self.data = value def __getitem__(self, i): print('get[%s]:' % i, end='') return self.data[i] def __iter__(self): print('iter=> ', end='') self.ix = 0 return self def __next__(self): print('next:', end='')

716 | Chapter 29: Operator Overloading

__getitem__

www.it-ebooks.info

if self.ix == len(self.data): raise StopIteration item = self.data[self.ix] self.ix += 1 return item def __contains__(self, x): print('contains: ', end='') return x in self.data X = Iters([1, 2, 3, 4, 5]) print(3 in X) for i in X: print(i, end=' | ') print() print([i ** 2 for i in X]) print( list(map(bin, X)) ) I = iter(X) while True: try: print(next(I), end=' @ ') except StopIteration: break

__contains__ __iter__ __getitem__

__next__

contains: True iter=> next:1 | next:2 | next:3 | next:4 | next:5 | next: iter=> next:next:next:next:next:next:[1, 4, 9, 16, 25] iter=> next:next:next:next:next:next:['0b1', '0b10', '0b11', '0b100', '0b101'] iter=> next:1 @ next:2 @ next:3 @ next:4 @ next:5 @ next:

__contains__ __iter__ iter=> iter=> iter=> iter=> iter=>

next:next:next:True next:1 | next:2 | next:3 | next:4 | next:5 | next: next:next:next:next:next:next:[1, 4, 9, 16, 25] next:next:next:next:next:next:['0b1', '0b10', '0b11', '0b100', '0b101'] next:1 @ next:2 @ next:3 @ next:4 @ next:5 @ next:

__contains__

__iter__

__getitem__ get[0]:get[1]:get[2]:True get[0]:1 | get[1]:2 | get[2]:3 | get[3]:4 | get[4]:5 | get[5]: get[0]:get[1]:get[2]:get[3]:get[4]:get[5]:[1, 4, 9, 16, 25] get[0]:get[1]:get[2]:get[3]:get[4]:get[5]:['0b1', '0b10', '0b11', '0b100','0b101'] get[0]:1 @ get[1]:2 @ get[2]:3 @ get[3]:4 @ get[4]:5 @ get[5]:

Membership: __contains__, __iter__, and __getitem__ | 717

www.it-ebooks.info

__getitem__ __getitem__

>>> X = Iters('spam') >>> X[0] get[0]:'s' >>> 'spam'[1:] 'pam' >>> 'spam'[slice(1, None)] 'pam' >>> X[1:] get[slice(1, None, None)]:'pam' >>> X[:-1] get[slice(None, −1, None)]:'spa'

__iter__ __contains__

Attribute Reference: __getattr__ and __setattr__ __getattr__

__getattr__ >>> class empty: ... def __getattr__(self, attrname): ... if attrname == "age": ... return 40 ... else: ... raise AttributeError, attrname ... >>> X = empty() >>> X.age 40 >>> X.name ...error text omitted... AttributeError: name

empty X.age attrname

X __getattr__

self

X "age"

age X.age

40

age

718 | Chapter 29: Operator Overloading

www.it-ebooks.info

__getattr__ AttributeError X.name

__getattr__

__setattr__ self.attr = value

self.__setattr__('attr', value) self __setattr__

__setattr__

self.__dict__['name'] = x

self.name = x

>>> class accesscontrol: ... def __setattr__(self, attr, value): ... if attr == 'age': ... self.__dict__[attr] = value ... else: ... raise AttributeError, attr + ' not allowed' ... >>> X = accesscontrol() >>> X.age = 40 >>> X.age 40 >>> X.name = 'mel' ...text omitted... AttributeError: name not allowed

Other Attribute Management Tools __getattribute__ __getattr__ property __get__

__set__

Attribute Reference: __getattr__ and __setattr__ | 719

www.it-ebooks.info

Emulating Privacy for Instance Attributes: Part 1 class PrivateExc(Exception): pass class Privacy: def __setattr__(self, attrname, value): if attrname in self.privates: raise PrivateExc(attrname, self) else: self.__dict__[attrname] = value class Test1(Privacy): privates = ['age'] class Test2(Privacy): privates = ['name', 'pay'] def __init__(self): self.__dict__['name'] = 'Tom' x = Test1() y = Test2() x.name = 'Bob' y.name = 'Sue' y.age x.age

= 30 = 40

__getattr__

720 | Chapter 29: Operator Overloading

www.it-ebooks.info

String Representation: __repr__ and __str__ __init__

self.data

__add__ __repr__

__repr__

__str__

>>> class adder: ... def __init__(self, value=0): ... self.data = value ... def __add__(self, other): ... self.data += other ... >>> x = adder() >>> print(x) >>> x

>>> class addrepr(adder): ... def __repr__(self): ... return 'addrepr(%s)' % self.data ... >>> x = addrepr(2) >>> x + 1 >>> x addrepr(3) >>> print(x) addrepr(3) >>> str(x), repr(x) ('addrepr(3)', 'addrepr(3)')

__str__

print print

str

__repr__

repr print

__repr__

str

__str__

print

str __repr__

__str__ __str__

String Representation: __repr__ and __str__ | 721

www.it-ebooks.info

__repr__

__str__

>>> class addstr(adder): ... def __str__(self): ... return '[Value: %s]' % self.data ... >>> x = addstr(3) >>> x + 1 >>> x >>> print(x) [Value: 4] >>> str(x), repr(x) ('[Value: 4]', '')

__repr__ __str__ __repr__

__str__

__repr__ >>> class addboth(adder): ... def __str__(self): ... return '[Value: %s]' % self.data ... def __repr__(self): ... return 'addboth(%s)' % self.data ... >>> x = addboth(4) >>> x + 1 >>> x addboth(5) >>> print(x) [Value: 5] >>> str(x), repr(x) ('[Value: 5]', 'addboth(5)')

__str__ __repr__ __str__ __repr__ >>> class Printer: ... def __init__(self, val): ... self.val = val ... def __str__(self): ... return str(self.val) ... >>> objs = [Printer(2), Printer(3)] >>> for x in objs: print(x) ...

722 | Chapter 29: Operator Overloading

www.it-ebooks.info

2 3 >>> print(objs) [, >> objs [, >> ... ... ... ... ... >>> >>> ... 2 3 >>> [2, >>> [2,

__str__

class Printer: def __init__(self, val): self.val = val def __repr__(self): return str(self.val) objs = [Printer(2), Printer(3)] for x in objs: print(x)

print(objs) 3] objs 3]

__str__

__repr__ __init__

Right-Side and In-Place Addition: __radd__ and __iadd__ __add__ + __radd__ +

__radd__ __add__ >>> class Commuter: ... def __init__(self, val): ... self.val = val ... def __add__(self, other): ... print('add', self.val, other) ... return self.val + other ... def __radd__(self, other): ... print('radd', self.val, other) ... return other + self.val ... >>> x = Commuter(88) >>> y = Commuter(99)

Right-Side and In-Place Addition: __radd__ and __iadd__ | 723

www.it-ebooks.info

>>> x + 1 add 88 1 89 >>> 1 + y radd 99 1 100 >>> x + y add 88 radd 99 88 187

other

x

,

__radd__ self y

+

__add__ __radd__

__add__

Commuter val __radd__

isinstance Commuter

>>> class Commuter: ... def __init__(self, val): ... self.val = val ... def __add__(self, other): ... if isinstance(other, Commuter): other = other.val ... return Commuter(self.val + other) ... def __radd__(self, other): ... return Commuter(other + self.val) ... def __str__(self): ... return '' % self.val ... >>> x = Commuter(88) >>> y = Commuter(99) >>> print(x + 10) >>> print(10 + y) >>> z = x + y >>> print(z) >>> print(z + 10) >>> print(z + z)

724 | Chapter 29: Operator Overloading

www.it-ebooks.info

In-Place Addition +=

__iadd__ Commuter

__add__ += >>> ... ... ... ... ... ... >>> >>> >>> >>> 7 >>> ... ... ... ... ... >>> >>> >>> >>> 7

__iadd__

class Number: def __init__(self, val): self.val = val def __iadd__(self, other): self.val += other return self x = Number(5) x += 1 x += 1 x.val class Number: def __init__(self, val): self.val = val def __add__(self, other): return Number(self.val + other) x = Number(5) x += 1 x += 1 x.val

__mul__ __rmul__

__imul__

Vector

Employee

Button

Call Expressions: __call__ __call__ __call__

>>> class Callee: ... def __call__(self, *pargs, **kargs): ... print('Called:', pargs, kargs) ... >>> C = Callee() >>> C(1, 2, 3)

Call Expressions: __call__ | 725

www.it-ebooks.info

Called: (1, 2, 3) {} >>> C(1, 2, 3, x=4, y=5) Called: (1, 2, 3) {'y': 5, 'x': 4}

__call__

class C: def __call__(self, a, b, c=5, d=6): ... class C: def __call__(self, *pargs, **kargs): ... class C: def __call__(self, *pargs, d=6, **kargs): ...

X = C() X(1, 2) X(1, 2, 3, 4) X(a=1, b=2, d=4) X(*[1, 2], **dict(c=3, d=4)) X(1, *(2,), c=3, **dict(d=4))

__call__

>>> ... ... ... ... ... >>> >>> 6 >>> 8

class Prod: def __init__(self, value): self.value = value def __call__(self, other): return self.value * other x = Prod(2) x(3) x(4)

__call__ >>> class Prod: ... def __init__(self, value): ... self.value = value ... def comp(self, other): ... return self.value * other ...

726 | Chapter 29: Operator Overloading

www.it-ebooks.info

>>> x = Prod(3) >>> x.comp(3) 9 >>> x.comp(4) 12

__call__

__init__

__str__

__repr__

Function Interfaces and Callback-Based Code tkinter

Tkinter tkinter

__call__

x.comp

x bound __call__

class Callback: def __init__(self, color): self.color = color def __call__(self): print('turn', self.color)

cb1 = Callback('blue') cb2 = Callback('green') B1 = Button(command=cb1) B2 = Button(command=cb2)

cb1() cb2()

Call Expressions: __call__ | 727

www.it-ebooks.info

lambda cb3 = (lambda color='red': 'turn ' + color) print(cb3())

self class Callback: def __init__(self, color): self.color = color def changeColor(self): print('turn', self.color) cb1 = Callback('blue') cb2 = Callback('yellow') B1 = Button(command=cb1.changeColor) B2 = Button(command=cb2.changeColor)

changeColor object = Callback('blue') cb = object.changeColor cb()

__call__ __call__ __call__

Comparisons: __lt__, __gt__, and Others < > = ==

!=

728 | Chapter 29: Operator Overloading

www.it-ebooks.info

__add__ __radd__ __lt__

==

!=

__gt__

__eq__

__ne__

__cmp__

self cmp(x, y)

__cmp__

cmp

class C: data = 'spam' def __gt__(self, other): return self.data > other def __lt__(self, other): return self.data < other X = C() print(X > 'ham') print(X < 'ham')

The 2.6 __cmp__ Method (Removed in 3.0) __cmp__ __cmp__ class C: data = 'spam' def __cmp__(self, other): return cmp(self.data, other) X = C() print(X > 'ham') print(X < 'ham')

Comparisons: __lt__, __gt__, and Others | 729

www.it-ebooks.info

__cmp__

cmp

cmp class C: data = 'spam' def __cmp__(self, other): return (self.data > other) - (self.data < other)

__cmp__ __cmp__ __getslice__ __cmp__

Boolean Tests: __bool__ and __len__ __bool__ __len__

>>> class Truth: ... def __bool__(self): return True ... >>> X = Truth() >>> if X: print('yes!') ... yes! >>> class Truth: ... def __bool__(self): return False ... >>> X = Truth() >>> bool(X) False

>>> class Truth: ... def __len__(self): return 0 ... >>> X = Truth() >>> if not X: print('no!')

730 | Chapter 29: Operator Overloading

www.it-ebooks.info

... no!

__bool__

__len__

>>> class Truth: ... def __bool__(self): return True ... def __len__(self): return 0 ... >>> X = Truth() >>> if X: print('yes!') ... yes!

>>> class Truth: ... pass ... >>> X = Truth() >>> bool(X) True

Booleans in Python 2.6 __nonzero__ __nonzero__

__bool__

__bool__ __len__ __bool__ False

C:\misc> c:\python30\python >>> class C: ... def __bool__(self): ... print('in bool') ... return False ... >>> X = C() >>> bool(X) in bool False >>> if X: print(99) ... in bool

Boolean Tests: __bool__ and __len__ | 731

www.it-ebooks.info

__bool__ C:\misc> c:\python26\python >>> class C: ... def __bool__(self): ... print('in bool') ... return False ... >>> X = C() >>> bool(X) True >>> if X: print(99) ... 99

__nonzero__

0

__len__

C:\misc> c:\python26\python >>> class C: ... def __nonzero__(self): ... print('in nonzero') ... return False ... >>> X = C() >>> bool(X) in nonzero False >>> if X: print(99) ... in nonzero

__nonzero__ __bool__

Object Destruction: __del__ __init__ __del__ >>> class Life: ... def __init__(self, name='unknown'): ... print('Hello', name) ... self.name = name ... def __del__(self): ... print('Goodbye', self.name) ... >>> brian = Life('Brian') Hello Brian >>> brian = 'loretta' Goodbye Brian

732 | Chapter 29: Operator Overloading

www.it-ebooks.info

brian

Life

try finally

__del__ sys.stderr

__del__ __del__

gc

Chapter Summary

__enter__ __get__

__exit__ with __set__

__new__

Chapter Summary | 733

www.it-ebooks.info

__call__

__str__

Test Your Knowledge: Quiz

Test Your Knowledge: Answers __getitem__ __iter__ __next__

__iter__ __iter__

__getitem__ __str__

__repr__ print str __str__ __repr__ __str__ __str__ __repr__

print repr print

__getitem__ __getslice__ __iadd__

__add__ __radd__

734 | Chapter 29: Operator Overloading

str

str

www.it-ebooks.info

Test Your Knowledge: Answers | 735

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 30

Designing with Classes

Python and OOP

X.name X.method

method

X

737

www.it-ebooks.info

Overloading by Call Signatures (or Not)

class C: def meth(self, x): ... def meth(self, x, y, z): ...

def X = 1

class C: def meth(self, *args): if len(args) == 1: ... elif type(arg[0]) == int: ...

class C: def meth(self, x): x.operation()

738 | Chapter 30: Designing with Classes

X = 2 X

2

www.it-ebooks.info

OOP and Inheritance: “Is-a” Relationships

giveRaise

Employee __repr__ Employee Chef Server

work PizzaRobot

Chef

Employee

class Employee: def __init__(self, name, salary=0): self.name = name self.salary = salary def giveRaise(self, percent): self.salary = self.salary + (self.salary * percent) def work(self): print(self.name, "does stuff") def __repr__(self): return "" % (self.name, self.salary) class Chef(Employee): def __init__(self, name): Employee.__init__(self, name, 50000) def work(self): print(self.name, "makes food") class Server(Employee): def __init__(self, name): Employee.__init__(self, name, 40000) def work(self): print(self.name, "interfaces with customer") class PizzaRobot(Chef):

OOP and Inheritance: “Is-a” Relationships | 739

www.it-ebooks.info

def __init__(self, name): Chef.__init__(self, name) def work(self): print(self.name, "makes pizza") if __name__ == "__main__": bob = PizzaRobot('bob') print(bob) bob.work() bob.giveRaise(0.20) print(bob); print() for klass in Employee, Chef, Server, PizzaRobot: obj = klass(klass.__name__) obj.work()

bob bob Employee.giveRaise

PizzaRobot Chef Employee.__repr__

C:\python\examples> python employees.py bob makes pizza Employee does stuff Chef makes food Server interfaces with customer PizzaRobot makes pizza

for work work

OOP and Composition: “Has-a” Relationships

740 | Chapter 30: Designing with Classes

Employee bob

www.it-ebooks.info

from employees import PizzaRobot, Server class Customer: def __init__(self, name): self.name = name def order(self, server): print(self.name, "orders from", server) def pay(self, server): print(self.name, "pays for item to", server) class Oven: def bake(self): print("oven bakes") class PizzaShop: def __init__(self): self.server = Server('Pat') self.chef = PizzaRobot('Bob') self.oven = Oven() def order(self, name): customer = Customer(name) customer.order(self.server) self.chef.work() self.oven.bake() customer.pay(self.server) if __name__ == "__main__": scene = PizzaShop() scene.order('Homer') print('...') scene.order('Shaggy')

PizzaShop Oven PizzaShop order Customer Customer

Server

OOP and Composition: “Has-a” Relationships | 741

www.it-ebooks.info

C:\python\examples> python pizzashop.py Homer orders from Bob makes pizza oven bakes Homer pays for item to ... Shaggy orders from Bob makes pizza oven bakes Shaggy pays for item to

Stream Processors Revisited def processor(reader, converter, writer): while 1: data = reader.read() if not data: break data = converter(data) writer.write(data)

class Processor: def __init__(self, reader, writer): self.reader = reader self.writer = writer def process(self): while 1: data = self.reader.readline() if not data: break data = self.converter(data) self.writer.write(data) def converter(self, data): assert False, 'converter must be defined'

converter assert reader

742 | Chapter 30: Designing with Classes

writer

www.it-ebooks.info

from streams import Processor class Uppercase(Processor): def converter(self, data): return data.upper() if __name__ == '__main__': import sys obj = Uppercase(open('spam.txt'), sys.stdout) obj.process()

Uppercase

stdout C:\lp4e> type spam.txt spam Spam SPAM! C:\lp4e> python converters.py SPAM SPAM SPAM!

C:\lp4e> python >>> import converters >>> prog = converters.Uppercase(open('spam.txt'), open('spamup.txt', 'w')) >>> prog.process() C:\lp4e> type spamup.txt SPAM SPAM SPAM!

C:\lp4e> python >>> from converters import Uppercase >>> >>> class HTMLize: ... def write(self, line): ... print('%s' % line.rstrip()) ... >>> Uppercase(open('spam.txt'), HTMLize()).process() SPAM SPAM SPAM!

OOP and Composition: “Has-a” Relationships | 743

www.it-ebooks.info

Processor write convert Processor

Why You Will Care: Classes and Persistence pickle

pickle

shelve

import pickle object = someClass() file = open(filename, 'wb') pickle.dump(object, file) import pickle file = open(filename, 'rb') object = pickle.load(file)

744 | Chapter 30: Designing with Classes

shelve

www.it-ebooks.info

import shelve object = someClass() dbase = shelve.open('filename') dbase['key'] = object import shelve dbase = shelve.open('filename') object = dbase['key']

>>> from pizzashop import PizzaShop >>> shop = PizzaShop() >>> shop.server, shop.chef (, ) >>> import pickle >>> pickle.dump(shop, open('shopfile.dat', 'wb'))

shop

>>> import pickle >>> obj = pickle.load(open('shopfile.dat', 'rb')) >>> obj.server, obj.chef (, ) >>> obj.order('Sue') Sue orders from Bob makes pizza oven bakes Sue pays for item to

OOP and Delegation: “Wrapper” Objects

__getattr__ __getattr__

OOP and Delegation: “Wrapper” Objects | 745

www.it-ebooks.info

class wrapper: def __init__(self, object): self.wrapped = object def __getattr__(self, attrname): print('Trace:', attrname) return getattr(self.wrapped, attrname)

__getattr__ getattr getattr(X,N) X.N

N getattr(X,N) X.N

X.__dict__[N] __dict__ wrapper

wrapped >>> from trace import wrapper >>> x = wrapper([1,2,3]) >>> x.append(4) Trace: append >>> x.wrapped [1, 2, 3, 4] >>> x = wrapper({"a": 1, "b": 2}) >>> x.keys() Trace: keys ['a', 'b']

wrapped wrapper

746 | Chapter 30: Designing with Classes

www.it-ebooks.info

__getattr__ __repr__

__str__

__getattr__

Pseudoprivate Class Attributes

_X

__getattr__

__setattr__

Pseudoprivate Class Attributes | 747

www.it-ebooks.info

Name Mangling Overview class __X Spam

_Spam__X

class

self __meth

Spam

_Spam__meth self._Spam__X

self.__X

Why Use Pseudoprivate Attributes?

self self.attr = value

X class C1: def meth1(self): self.X = 88 def meth2(self): print(self.X)

class C2: def metha(self): self.X = 99 def methb(self): print(self.X)

748 | Chapter 30: Designing with Classes

www.it-ebooks.info

class C3(C1, C2): ... I = C3()

self.X self.X X

I.X

class C1: def meth1(self): def meth2(self): class C2: def metha(self): def methb(self):

self.__X = 88 print(self.__X) self.__X = 99 print(self.__X)

class C3(C1, C2): pass I = C3() I.meth1(); I.metha() print(I.__dict__) I.meth2(); I.methb()

X dir _C1__X

_C2__X

I

X

% python private.py {'_C2__X': 99, '_C1__X': 88} 88 99

I._C1__X = 77

class Super: def method(self): ... class Tool:

Pseudoprivate Class Attributes | 749

www.it-ebooks.info

def __method(self): ... def other(self): self.__method() class Sub1(Tool, Super): ... def actions(self): self.method() class Sub2(Tool): def __init__(self): self.method = 99

class

Sub1

Tool

Super Sub1 Sub2

__X Private

Private

Methods Are Objects: Bound or Unbound __call__

750 | Chapter 30: Designing with Classes

www.it-ebooks.info

self

self

self

class Spam: def doit(self, message): print(message)

object1 = Spam() object1.doit('hello world')

object.name object1 Spam.doit object1 = Spam() x = object1.doit x('hello world')

Methods Are Objects: Bound or Unbound | 751

www.it-ebooks.info

doit

object1 = Spam() t = Spam.doit t(object1, 'howdy')

self self.method self class Eggs: def m1(self, n): print(n) def m2(self): x = self.m1 x(42) Eggs().m2()

Unbound Methods are Functions in 3.0

C:\misc> c:\python30\python >>> class Selfless:

752 | Chapter 30: Designing with Classes

www.it-ebooks.info

... ... ... ... ... ... ... >>> >>> 9 >>> 9 >>> 7

def __init__(self, data): self.data = data def selfless(arg1, arg2): return arg1 + arg2 def normal(self, arg1, arg2): return self.data + arg1 + arg2 X = Selfless(2) X.normal(3, 4) Selfless.normal(X, 3, 4) Selfless.selfless(3, 4)

>>> X.selfless(3, 4) TypeError: selfless() takes exactly 2 positional arguments (3 given) >>> Selfless.normal(3, 4) TypeError: normal() takes exactly 3 positional arguments (2 given)

staticmethod self

Methods Are Objects: Bound or Unbound | 753

www.it-ebooks.info

Bound Methods and Other Callable Objects

>>> ... ... ... ... ... ... ... >>> >>> >>> >>> 4

class Number: def __init__(self, base): self.base = base def double(self): return self.base * 2 def triple(self): return self.base * 3 x = Number(2) y = Number(3) z = Number(4) x.double()

>>> acts = [x.double, y.double, y.triple, z.double] >>> for act in acts: ... print(act()) ... 4 6 9 8

>>> bound = x.double >>> bound.__self__, bound.__func__ (, ) >>> bound.__self__.base 2 >>> bound() 4

def __call__ >>> def square(arg): ... return arg ** 2 ... >>> class Sum: ... def __init__(self, val):

754 | Chapter 30: Designing with Classes

lambda

www.it-ebooks.info

... ... ... ... >>> ... ... ... ... ... >>> >>> >>>

self.val = val def __call__(self, arg): return self.val + arg class Product: def __init__(self, val): self.val = val def method(self, arg): return self.val * arg sobject = Sum(2) pobject = Product(3) actions = [square, sobject, pobject.method]

>>> for act in actions: ... print(act(5)) ... 25 7 15 >>> actions[-1](5) 15 >>> [act(5) for act in actions] [25, 7, 15] >>> list(map(lambda act: act(5), actions)) [25, 7, 15]

>>> class Negate: ... def __init__(self, val): ... self.val = -val ... def __repr__(self): ... return str(self.val) ... >>> actions = [square, sobject, pobject.method, Negate] >>> for act in actions: ... print(act(5)) ... 25 7 15 -5 >>> [act(5) for act in actions] [25, 7, 15, −5] >>> table = {act(5): act for act in actions} >>> for (key, value) in table.items(): ... print('{0:2} => {1}'.format(key, value)) ... -5 => 25 => 15 => 7 =>

Methods Are Objects: Bound or Unbound | 755

www.it-ebooks.info

__call__

Why You Will Care: Bound Methods and Callbacks

tkinter

Tkinter

def handler(): ...use globals for state... ... widget = Button(text='spam', command=handler)

command

lambda

class MyWidget: def handler(self): ...use self.attr for state... def makewidgets(self): b = Button(text='spam', command=self.handler)

self

MyGui.handler

self.handler self

handler

__call__

Multiple Inheritance: “Mix-in” Classes class

756 | Chapter 30: Designing with Classes

www.it-ebooks.info

class

self

Coding Mix-in Display Classes >>> class Spam: ... def __init__(self): ... self.data1 = "food" ... >>> X = Spam() >>> print(X)

__str__

__repr__

Multiple Inheritance: “Mix-in” Classes | 757

www.it-ebooks.info

AttrDisplay

__str__

Listing instance attributes with __dict__ ListInstance __str__ ListInstance

class ListInstance: """ Mix-in class that provides a formatted print() or str() of instances via inheritance of __str__, coded here; displays instance attrs only; self is the instance of lowest class; uses __X names to avoid clashing with client's attrs """ def __str__(self): return '' % ( self.__class__.__name__, id(self), self.__attrnames()) def __attrnames(self): result = '' for attr in sorted(self.__dict__): result += '\tname %s=%s\n' % (attr, self.__dict__ [attr]) retubrn result

ListInstance __class__ __name__ self.__class__.__name__

758 | Chapter 30: Designing with Classes

www.it-ebooks.info

__dict__

ListInstance

id

__attrnames

_ListInstance__attrnames self

ListInstance

__str__

>>> from lister import ListInstance >>> class Spam(ListInstance): ... def __init__(self): ... self.data1 = 'food' ... >>> x = Spam() >>> print(x)

str >>> str(x) '' >>> x

ListInstance ListInstance __str__

Multiple Inheritance: “Mix-in” Classes | 759

www.it-ebooks.info

from lister import * class Super: def __init__(self): self.data1 = 'spam' def ham(self): pass class Sub(Super, ListInstance): def __init__(self): Super.__init__(self) self.data2 = 'eggs' self.data3 = 42 def spam(self): pass if __name__ == '__main__': X = Sub() print(X)

Sub

Super

ListInstance Sub ListInstance

C:\misc> C:\python30\python testmixin.py

ListInstance

self

Lister import >>> import lister >>> class C(lister.ListInstance): pass ... >>> x = C() >>> x.a = 1; x.b = 2; x.c = 3 >>> print(x)

760 | Chapter 30: Designing with Classes

www.it-ebooks.info

ListInstance

__str__

__str__

Listing inherited attributes with dir Lister

dir

__dict__

class ListInherited: """ Use dir() to collect both instance attrs and names inherited from its classes; Python 3.0 shows more names than 2.6 because of the implied object superclass in the new-style class model; getattr() fetches inherited names not in self.__dict__; use __str__, not __repr__, or else this loops when printing bound methods! """ def __str__(self): return '' % ( self.__class__.__name__, id(self), self.__attrnames()) def __attrnames(self): result = '' for attr in dir(self): if attr[:2] == '__' and attr[-2:] == '__': result += '\tname %s=\n' % attr else: result += '\tname %s=%s\n' % (attr, getattr(self, attr)) return result

__X__ getattr getattr

Multiple Inheritance: “Mix-in” Classes | 761

www.it-ebooks.info

class Sub(Super, ListInherited):

C:\misc> c:\python26\python testmixin.py

object

C:\misc> c:\python30\python testmixin.py

__str__

__repr__

__repr__ __repr__ __repr__ __repr__

762 | Chapter 30: Designing with Classes

www.it-ebooks.info

__str__

__repr__

__repr__ isinstance

types.MethodType

Listing attributes per object in class trees

__class__

__bases__ __dicts__

class ListTree: """ Mix-in that returns an __str__ trace of the entire class tree and all its objects' attrs at and above self; run by print(), str() returns constructed string; uses __X attr names to avoid impacting clients; uses generator expr to recurse to superclasses; uses str.format() to make substitutions clearer """ def __str__(self): self.__visited = {} return ''.format( self.__class__.__name__, id(self), self.__attrnames(self, 0), self.__listclass(self.__class__, 4)) def __listclass(self, aClass, indent): dots = '.' * indent if aClass in self.__visited: return '\n{0}\n'.format( dots, aClass.__name__, id(aClass)) else: self.__visited[aClass] = True genabove = (self.__listclass(c, indent+4) for c in aClass.__bases__) return '\n{0}\n'.format( dots, aClass.__name__, id(aClass), self.__attrnames(aClass, indent), ''.join(genabove), dots) def __attrnames(self, obj, indent):

Multiple Inheritance: “Mix-in” Classes | 763

www.it-ebooks.info

spaces = ' ' * (indent + 4) result = '' for attr in sorted(obj.__dict__): if attr.startswith('__') and attr.endswith('__'): result += spaces + '{0}=\n'.format(attr) else: result += spaces + '{0}={1}\n'.format(attr, getattr(obj, attr)) return result

join format

%

return '' % (...) return ''.format(...)

class Sub(Super, ListTree):

C:\misc> c:\python26\python testmixin.py

764 | Chapter 30: Designing with Classes

www.it-ebooks.info

__visited

object C:\misc> c:\python30\python testmixin.py

id

__X__

C:\misc> c:\python26\python testmixin.py ...more omitted... ........', getattr(X, attr)) d => 4 a => 1 b => 2 __dict__ => {'d': 4}

getattr >>> for attr in list(getattr(X, '__dict__', [])) + getattr(X, '__slots__', []): ... print(attr, '=>', getattr(X, attr)) d => 4 a => 1 b => 2 __dict__ => {'d': 4}

Multiple __slot__ lists in superclasses __slots__ __slots__

__slots__ __dict__ __slots__

790 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

__slots__ __dict__

__slots__

dir >>> ... ... >>> ... ... >>> >>> >>> (1,

class E: __slots__ = ['c', 'd'] class D(E): __slots__ = ['a', '__dict__'] X = D() X.a = 1; X.b = 2; X.c = 3 X.a, X.c 3)

>>> E.__slots__ ['c', 'd'] >>> D.__slots__ ['a', '__dict__'] >>> X.__slots__ ['a', '__dict__'] >>> X.__dict__ {'b': 2} >>> for attr in list(getattr(X, '__dict__', [])) + getattr(X, '__slots__', []): ... print(attr, '=>', getattr(X, attr)) ... b => 2 a => 1 __dict__ => {'b': 2} >>> dir(X) [...many names omitted... 'a', 'b', 'c', 'd']

__slots__

__dict__

Private

New-Style Class Extensions | 791

www.it-ebooks.info

Class Properties

__getattr__

__setattr__

property None class

name = property(...) obj.name __getattr__

>>> class classic: ... def __getattr__(self, name): ... if name == 'age': ... return 40 ... else: ... raise AttributeError ... >>> x = classic() >>> x.age 40 >>> x.name AttributeError

object >>> class newprops(object): ... def getage(self): ... return 40 ... age = property(getage, None, None, None) ... >>> x = newprops() >>> x.age 40 >>> x.name AttributeError: newprops instance has no attribute 'name'

792 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

>>> class newprops(object): ... def getage(self): ... return 40 ... def setage(self, value): ... print('set age:', value) ... self._age = value ... age = property(getage, setage, None, None) ... >>> x = newprops() >>> x.age 40 >>> x.age = 42 set age: 42 >>> x._age 42 >>> x.job = 'trainer' >>> x.job 'trainer'

object

__setattr__

>>> class classic: ... def __getattr__(self, name): ... if name == 'age': ... return 40 ... else: ... raise AttributeError ... def __setattr__(self, name, value): ... print('set:', name, value) ... if name == 'age': ... self.__dict__['_age'] = value ... else: ... self.__dict__[name] = value ... >>> x = classic() >>> x.age 40 >>> x.age = 41 set: age 41 >>> x._age 41 >>> x.job = 'trainer' >>> x.job

New-Style Class Extensions | 793

www.it-ebooks.info

__getattr__

__setattr__

__getattr__

__setattr__

__getattribute__ and Descriptors __getattribute__ __getattr__ __getattr__ __setattr__ __get__

__getattribute__

Metaclasses

type

794 | Chapter 31: Advanced Class Topics

__set__

www.it-ebooks.info

Static and Class Methods

staticmethod classmethod staticmethod

Why the Special Methods?

self

self self

Static and Class Methods | 795

www.it-ebooks.info

self

Static Methods in 2.6 and 3.0

self

class Spam: numInstances = 0 def __init__(self): Spam.numInstances = Spam.numInstances + 1

796 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

def printNumInstances(): print("Number of instances created: ", Spam.numInstances)

printNumInstances

C:\misc> c:\python26\python >>> from spam import Spam >>> a = Spam() >>> b = Spam() >>> c = Spam() >>> Spam.printNumInstances() TypeError: unbound method printNumInstances() must be called with Spam instance as first argument (got nothing instead) >>> a.printNumInstances() TypeError: printNumInstances() takes no arguments (1 given)

def

C:\misc> c:\python30\python >>> from spam import Spam >>> a = Spam() >>> b = Spam() >>> c = Spam() >>> Spam.printNumInstances() Number of instances created: 3 >>> a.printNumInstances() TypeError: printNumInstances() takes no arguments (1 given)

printNumInstances

Spam.printNumInstances() instance.printNumInstances()

Static and Class Methods | 797

www.it-ebooks.info

Static Method Alternatives

print def printNumInstances(): print("Number of instances created: ", Spam.numInstances) class Spam: numInstances = 0 def __init__(self): Spam.numInstances = Spam.numInstances + 1 >>> import spam >>> a = spam.Spam() >>> b = spam.Spam() >>> c = spam.Spam() >>> spam.printNumInstances() Number of instances created: >>> spam.Spam.numInstances 3

798 | Chapter 31: Advanced Class Topics

3

www.it-ebooks.info

class Spam: numInstances = 0 def __init__(self): Spam.numInstances = Spam.numInstances + 1 def printNumInstances(self): print("Number of instances created: ", Spam.numInstances) >>> from spam import Spam >>> a, b, c = Spam(), Spam(), Spam() >>> a.printNumInstances() Number of instances created: 3 >>> Spam.printNumInstances(a) Number of instances created: 3 >>> Spam().printNumInstances() Number of instances created: 4

Using Static and Class Methods

staticmethod

classmethod

class Methods: def imeth(self, x): print(self, x) def smeth(x): print(x) def cmeth(cls, x): print(cls, x) smeth = staticmethod(smeth) cmeth = classmethod(cmeth)

smeth

cmeth

class

def

Static and Class Methods | 799

www.it-ebooks.info

>>> obj = Methods() >>> obj.imeth(1) 1 >>> Methods.imeth(obj, 2) 2

staticmethod staticmethod >>> Methods.smeth(3) 3 >>> obj.smeth(4) 4

>>> Methods.cmeth(5) 5 >>> obj.cmeth(6) 6

Counting Instances with Static Methods

800 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

class Spam: numInstances = 0 def __init__(self): Spam.numInstances += 1 def printNumInstances(): print("Number of instances:", Spam.numInstances) printNumInstances = staticmethod(printNumInstances)

>>> a = Spam() >>> b = Spam() >>> c = Spam() >>> Spam.printNumInstances() Number of instances: 3 >>> a.printNumInstances() Number of instances: 3

printNumInstances staticmethod class

class Sub(Spam): def printNumInstances(): print("Extra stuff...") Spam.printNumInstances() printNumInstances = staticmethod(printNumInstances) >>> a = Sub() >>> b = Sub() >>> a.printNumInstances() Extra stuff... Number of instances: 2 >>> Sub.printNumInstances() Extra stuff... Number of instances: 2 >>> Spam.printNumInstances() Number of instances: 2

>>> class Other(Spam): pass >>> c = Other() >>> c.printNumInstances() Number of instances: 3

Static and Class Methods | 801

www.it-ebooks.info

Counting Instances with Class Methods

class Spam: numInstances = 0 def __init__(self): Spam.numInstances += 1 def printNumInstances(cls): print("Number of instances:", cls.numInstances) printNumInstances = classmethod(printNumInstances)

printNumInstances

>>> a, b = Spam(), Spam() >>> a.printNumInstances() Number of instances: 2 >>> Spam.printNumInstances() Number of instances: 2

Spam.printNumInstances cls class Spam: numInstances = 0 def __init__(self): Spam.numInstances += 1 def printNumInstances(cls): print("Number of instances:", cls.numInstances, cls) printNumInstances = classmethod(printNumInstances) class Sub(Spam): def printNumInstances(cls): print("Extra stuff...", cls) Spam.printNumInstances() printNumInstances = classmethod(printNumInstances) class Other(Spam): pass

>>> x, y = Sub(), Spam() >>> x.printNumInstances() Extra stuff... Number of instances: 2

802 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

>>> Sub.printNumInstances() Extra stuff... Number of instances: 2 >>> y.printNumInstances() Number of instances: 2

Sub Sub Sub

Spam Spam

>>> z = Other() >>> z.printNumInstances() Number of instances: 3

Other

Spam Spam Object

Spam

Spam

Counting instances per class with class methods

class Spam: numInstances = 0 def count(cls): cls.numInstances += 1 def __init__(self): self.count() count = classmethod(count) class Sub(Spam): numInstances = 0 def __init__(self): Spam.__init__(self) class Other(Spam): numInstances = 0

Static and Class Methods | 803

www.it-ebooks.info

>>> >>> >>> >>> (1, >>> (1,

x = Spam() y1, y2 = Sub(), Sub() z1, z2, z3 = Other(), Other(), Other() x.numInstances, y1.numInstances, z1.numInstances 2, 3) Spam.numInstances, Sub.numInstances, Other.numInstances 2, 3)

numInstances

Decorators and Metaclasses: Part 1 staticmethod

Function Decorator Basics def @

804 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

class C: @staticmethod def meth(): ...

class C: def meth(): ... meth = staticmethod(meth)

staticmethod

classmethod class Spam: numInstances = 0 def __init__(self): Spam.numInstances = Spam.numInstances + 1 @staticmethod def printNumInstances(): print("Number of instances created: ", Spam.numInstances) a = Spam() b = Spam() c = Spam() Spam.printNumInstances() a.printNumInstances()

staticmethod

A First Function Decorator Example

Decorators and Metaclasses: Part 1 | 805

www.it-ebooks.info

__call__

class tracer: def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) self.func(*args) @tracer def spam(a, b, c): print(a, b, c) spam(1, 2, 3) spam('a', 'b', 'c') spam(4, 5, 6)

spam spam

tracer __call__

*name

spam tracer spam call 1 to spam 1 2 3 call 2 to spam a b c call 3 to spam 4 5 6

__call__ def

806 | Chapter 31: Advanced Class Topics

tracer

www.it-ebooks.info

Class Decorators and Metaclasses class

def decorator(aClass): ... @decorator class C: ...

def decorator(aClass): ... class C: ... C = decorator(C)

def count(aClass): aClass.numInstances = 0 return aClass @count class Spam: ... @count class Sub(Spam): ... @count class Other(Spam): ...

type

class

class Meta(type): def __new__(meta, classname, supers, classdict): ... class C(metaclass=Meta): ...

Decorators and Metaclasses: Part 1 | 807

www.it-ebooks.info

class class C: __metaclass__ = Meta ...

__new__

__init__

For More Details

Class Gotchas

Changing Class Attributes Can Have Side Effects

808 | Chapter 31: Advanced Class Topics

type

www.it-ebooks.info

a X.a

X >>> ... ... >>> >>> 1 >>> 1

class X: a = 1 I = X() I.a X.a

class

>>> >>> 2 >>> >>> 2

X.a = 2 I.a J = X() J.a

class X: pass class Y: pass X.a X.b X.c Y.a

= = = =

1 2 3 X.a + X.b + X.c

for X.i in range(Y.a): print(X.i)

X

Y

Class Gotchas | 809

www.it-ebooks.info

class Record: pass X = Record() X.name = 'bob' X.job = 'Pizza maker'

Changing Mutable Class Attributes Can Have Side Effects, Too

>>> class C: ... shared = [] ... def __init__(self): ... self.perobj = [] ... >>> x = C() >>> y = C() >>> y.shared, y.perobj ([], []) >>> x.shared.append('spam') >>> x.perobj.append('spam') >>> x.shared, x.perobj (['spam'], ['spam']) >>> y.shared, y.perobj (['spam'], []) >>> C.shared ['spam']

x.shared.append('spam') x.shared = 'spam'

810 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

Multiple Inheritance: Order Matters class

Super

__str__

class ListTree: def __str__(self): ... class Super: def __str__(self): ... class Sub(ListTree, Super): x = Sub()

ListTree Sub

Super

class

ListTree

__str__ tkinter.Button Super

__str__

ListTree Super

ListTree

class Sub class ListTree: def __str__(self): ... def other(self): ... class Super: def __str__(self): ... def other(self): ... class Sub(ListTree, Super): other = Super.other def __init__(self): ... x = Sub()

other Super.other ListTree.other Super ListTree

Sub

class

Sub.other Sub.other other

Class Gotchas | 811

www.it-ebooks.info

class Sub(Super, ListTree): __str__ = Lister.__str__

__X

__str__

other

ListTree __other

Methods, Classes, and Nested Scopes

class

generate Spam generate method

Spam Spam generate

def generate(): class Spam: count = 1 def method(self): print(Spam.count) return Spam() generate().method() C:\python\examples> python nester.py ...error text omitted...

812 | Chapter 31: Advanced Class Topics

method

www.it-ebooks.info

Print(Spam.count) NameError: Spam

def

def

def

def def self self.count

class Spam.count

count Spam

global

method Spam

def generate(): global Spam class Spam: count = 1 def method(self): print(Spam.count) return Spam() generate().method()

Spam global method

generate

Spam def generate(): return Spam() class Spam: count = 1 def method(self): print(Spam.count) generate().method()

Spam method

__class__

def generate(): class Spam:

Class Gotchas | 813

www.it-ebooks.info

count = 1 def method(self): print(self.__class__.count) return Spam() generate().method()

Delegation-Based Classes in 3.0: __getattr__ and built-ins __getattr__

__str__

“Overwrapping-itis”

814 | Chapter 31: Advanced Class Topics

__getattr__

www.it-ebooks.info

Chapter Summary

Test Your Knowledge: Quiz

Test Your Knowledge: Answers

Test Your Knowledge: Answers | 815

www.it-ebooks.info

object

type __getattr__ __slots__ self

Test Your Knowledge: Part VI Exercises

Adder add ListAdder add

816 | Chapter 31: Advanced Class Topics

add(self, x, y) Adder

www.it-ebooks.info

DictAdder add

add Adder self.data Y

+

__add__ X.add(X.data,Y)

add

X +

add add(self,y) self.data + y add Mylist + append

sort

Mylist

start[:] Mylist Mylist

Mylist +

Mylist stdout MylistSub MylistSub

Mylist Mylist

MylistSub

+ stdout

Test Your Knowledge: Part VI Exercises | 817

www.it-ebooks.info

self Meta stdout

Meta

__getattr__

&

|

for

*args &

__add__

__getattr__

__bases__ __bases__

>> from zoo import Cat, Hacker >>> spot = Cat() >>> spot.reply() meow >>> data = Hacker() >>> data.reply() Hello world!

Scene Clerk Parrot

action

Customer line

line % python >>> import parrot >>> parrot.Scene().action() customer: "that's one ex-bird!"

820 | Chapter 31: Advanced Class Topics

line

www.it-ebooks.info

clerk: "no it isn't..." parrot: None

Why You Will Care: OOP by the Masters

Test Your Knowledge: Part VI Exercises | 821

www.it-ebooks.info

822 | Chapter 31: Advanced Class Topics

www.it-ebooks.info

PART VII

Exceptions and Tools

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 32

Exception Basics

try except try finally raise assert with as

Why Use Exceptions?

825

www.it-ebooks.info

try

Exception Roles

try try try

826 | Chapter 32: Exception Basics

www.it-ebooks.info

try finally

Exceptions: The Short Story

Default Exception Handler >>> def fetcher(obj, index): ... return obj[index] ...

>>> x = 'spam' >>> fetcher(x, 3) 'm'

obj[index] IndexError

next(G) try

Exceptions: The Short Story | 827

www.it-ebooks.info

>>> fetcher(x, 4) Traceback (most recent call last): File "", line 1, in File "", line 2, in fetcher IndexError: string index out of range

>>> fetcher(x, 4) Traceback (most recent call last): File "", line 1, in fetcher(x, 4) File "", line 2, in fetcher return obj[index] IndexError: string index out of range

Catching Exceptions try >>> try: ... fetcher(x, 4) ... except IndexError: ... print('got exception') ... got exception >>>

except try except try

828 | Chapter 32: Exception Basics

www.it-ebooks.info

>>> def catcher(): ... try: ... fetcher(x, 4) ... except IndexError: ... print('got exception') ... print('continuing') ... >>> catcher() got exception continuing >>>

try

Raising Exceptions

raise

>>> try: ... raise IndexError ... except IndexError: ... print('got exception') ... got exception

>>> raise IndexError Traceback (most recent call last): File "", line 1, in IndexError

assert raise >>> assert False, 'Nobody expects the Spanish Inquisition!' Traceback (most recent call last): File "", line 1, in AssertionError: Nobody expects the Spanish Inquisition!

Exceptions: The Short Story | 829

www.it-ebooks.info

User-Defined Exceptions raise

Exception >>> ... ... >>> ... ... >>> ... ... ... ... got >>>

class Bad(Exception): pass def doomed(): raise Bad() try: doomed() except Bad: print('got Bad') Bad

Termination Actions try

finally except

try finally try

>>> try: ... fetcher(x, 3) ... finally: ... print('after fetch') ... 'm' after fetch >>>

try

finally try print try

fetcher(x, 3) print('after fetch')

print try

830 | Chapter 32: Exception Basics

try finally finally

www.it-ebooks.info

>>> def after(): ... try: ... fetcher(x, 4) ... finally: ... print('after fetch') ... print('after try?') ... >>> after() after fetch Traceback (most recent call last): File "", line 1, in File "", line 3, in after File "", line 2, in fetcher IndexError: string index out of range >>>

try finally finally finally

try

>>> def after(): ... try: ... fetcher(x, 3) ... finally: ... print('after fetch') ... print('after try?') ... >>> after() after fetch after try? >>>

try except try finally try try except try finally

except

finally

try

finally

except try finally

with as

Exceptions: The Short Story | 831

www.it-ebooks.info

>>> with open('lumberjack.txt', 'w') as file: ... file.write('The larch!\n')

try finally with as

Why You Will Care: Error Checks

doStuff() { if (doFirstThing() == ERROR) return ERROR; if (doNextThing() == ERROR) return ERROR; ... return doLastThing(); } main() { if (doStuff() == ERROR) badEnding(); else goodEnding(); }

def doStuff(): doFirstThing() doNextThing() ... doLastThing() if __name__ == '__main__': try: doStuff() except: badEnding() else: goodEnding()

832 | Chapter 32: Exception Basics

www.it-ebooks.info

Chapter Summary

try try raise

assert

with as

try

Test Your Knowledge: Quiz

Test Your Knowledge: Answers

Test Your Knowledge: Answers | 833

www.it-ebooks.info

try except

raise

assert

try finally with as

834 | Chapter 32: Exception Basics

www.it-ebooks.info

CHAPTER 33

Exception Coding Details

try raise assert

with

try

except

finally else with

raise

except

The try/except/else Statement try except else

try finally except

finally

try try try

835

www.it-ebooks.info

except

else try except

else

try:

except : except (name2, name3): except as : except: else:

name1

name4

try except else

try raise try

try try try try try

except try

except

except try

except try

try else try except else

try try

except try try try 836 | Chapter 33: Exception Coding Details

try

www.it-ebooks.info

try Statement Clauses try

try except

finally

else except

else

except finally

else

finally except try

finally else except finally

try

else

Clause form

Interpretation

except:

Catch all (or all other) exception types.

except name:

Catch a specific exception only.

except name as value:

Catch the listed exception and its instance.

except (name1, name2):

Catch any of the listed exceptions.

except (name1, name2) as value:

Catch any listed exception and its instance.

else:

Run if no exceptions are raised.

finally:

Always perform this block.

as value

except

raise

except: try

except

except (e1, e2, e3): try

except

except except try:

action() except NameError: ... except IndexError: ...

The try/except/else Statement | 837

www.it-ebooks.info

except KeyError: ... except (AttributeError, TypeError, SyntaxError): ... else: ...

try except try

action except

else

action except except

try:

action() except NameError: ... except IndexError: ... except: ... else: ...

except try try:

action() except: ...

except

Exception try:

action() except Exception: ...

838 | Chapter 33: Exception Coding Details

except

www.it-ebooks.info

empty

except E as V: except E, V:

except (E1, E2):

as

as

V

except

The try else Clause else try try: ...run code... except IndexError: ...handle exception...

else

else try

try: ...run code... except IndexError: ...handle exception... else: ...no exception occurred...

else

try

try: ...run code... ...no exception occurred... except IndexError: ...handle exception...

IndexError

try

The try/except/else Statement | 839

www.it-ebooks.info

try else except

try else

Example: Default Behavior try

def gobad(x, y): return x / y def gosouth(x): print(gobad(x, 0)) gosouth(1)

% python bad.py Traceback (most recent call last): File "bad.py", line 7, in gosouth(1) File "bad.py", line 5, in gosouth print(gobad(x, 0)) File "bad.py", line 2, in gobad return x / y ZeroDivisionError: int division or modulo by zero

return

840 | Chapter 33: Exception Coding Details

www.it-ebooks.info

SyntaxError

try pdb

Example: Catching Built-in Exceptions

try TypeError + def kaboom(x, y): print(x + y) try:

kaboom([0,1,2], "spam") except TypeError: print('Hello world!') print('resuming here')

kaboom

try

except try

% python kaboom.py Hello world! resuming here

try kaboom

The try/except/else Statement | 841

www.it-ebooks.info

The try/finally Statement try finally

try try

try try:

finally:

try try try finally

try try

finally try

try

finally except finally finally

try finally

finally

try

else

try finally

finally try

except

else

finally

with finally

842 | Chapter 33: Exception Coding Details

except

www.it-ebooks.info

Example: Coding Termination Actions with try/finally try finally class MyError(Exception): pass def stuff(file): raise MyError() file = open('data', 'w') try: stuff(file) finally: file.close() print('not reached')

try finally

try

try finally finally

try

finally try try finally try

The try/finally Statement | 843

www.it-ebooks.info

Unified try/except/finally try finally except else finally

except

else

finally except

else

Exception1

handler1

try: main-action except Exception1: handler1 except Exception2: handler2 ... else: else-block finally: finally-block

main-action except Exception2 handler2 else-block finally-block finally-block else-block finally finally-block

finallytry

block finally try finally

844 | Chapter 33: Exception Coding Details

www.it-ebooks.info

finally try

Unified try Statement Syntax try

except

finally

try -> except -> else -> finally

else

finally except

except

except else else

try finally

try: statements except [type [as value]]: statements [except [type [as value]]: statements]* [else: statements] [finally: statements] try: statements finally: statements

else except finally finally else

except else except

except

raise

Combining finally and except by Nesting finally try

try except

try

except try finally

Unified try/except/finally | 845

www.it-ebooks.info

try:

try: main-action except Exception1: handler1 except Exception2: handler2 ... else: no-error finally: cleanup

finally try else

except

finally

Unified try Example try print sep = '-' * 32 + '\n' print(sep + 'EXCEPTION RAISED AND CAUGHT') try: x = 'spam'[99] except IndexError: print('except run') finally: print('finally run') print('after run') print(sep + 'NO EXCEPTION RAISED') try: x = 'spam'[3] except IndexError: print('except run') finally: print('finally run') print('after run') print(sep + 'NO EXCEPTION RAISED, WITH ELSE') try:

846 | Chapter 33: Exception Coding Details

www.it-ebooks.info

x = 'spam'[3] except IndexError: print('except run') else: print('else run') finally: print('finally run') print('after run') print(sep + 'EXCEPTION RAISED BUT NOT CAUGHT') try: x = 1 / 0 except IndexError: print('except run') finally: print('finally run') print('after run')

print

c:\misc> C:\Python30\python mergedexc.py -------------------------------EXCEPTION RAISED AND CAUGHT except run finally run after run -------------------------------NO EXCEPTION RAISED finally run after run -------------------------------NO EXCEPTION RAISED, WITH ELSE else run finally run after run -------------------------------EXCEPTION RAISED BUT NOT CAUGHT finally run Traceback (most recent call last): File "mergedexc.py", line 36, in x = 1 / 0 ZeroDivisionError: int division or modulo by zero

Unified try/except/finally | 847

www.it-ebooks.info

The raise Statement raise raise

raise raise raise raise

raise raise

raise

raise IndexError raise IndexError()

raise IndexError exc = IndexError() raise exc excs = [IndexError, TypeError] raise excs[0]

try

except name as X: raise

try:

... except IndexError as X: ...

as

try

848 | Chapter 33: Exception Coding Details

X

www.it-ebooks.info

class MyExc(Exception): pass ... raise MyExc('spam') ... try: ... except MyExc as X: print(X.args)

except try raise

Propagating Exceptions with raise raise

>>> try: ... raise IndexError('spam') ... except IndexError: ... print('propagating') ... raise ... propagating Traceback (most recent call last): File "", line 2, in IndexError: spam

raise

Python 3.0 Exception Chaining: raise from raise

from

raise exception from otherexception

from __cause__ >>> try: ... 1 / 0 ... except Exception as E:

The raise Statement | 849

www.it-ebooks.info

... raise TypeError('Bad!') from E ... Traceback (most recent call last): File "", line 2, in ZeroDivisionError: int division or modulo by zero The above exception was the direct cause of the following exception: Traceback (most recent call last): File "", line 4, in TypeError: Bad!

__context__

raise Exc, Args raise Exc(Args)

raise Exc

raise

Exc()

The assert Statement assert raise assert

raise

assert ,

data

if __debug__: if not : raise AssertionError()

AssertionError

try assert

-O AssertionError

850 | Chapter 33: Exception Coding Details

__debug__

www.it-ebooks.info

True

-O

python –O

main.py

Example: Trapping Constraints (but Not Errors!) assert def f(x): assert x < 0, 'x must be negative' return x ** 2 % python >>> import asserter >>> asserter.f(1) Traceback (most recent call last): File "", line 1, in File "asserter.py", line 2, in f assert x < 0, 'x must be negative' AssertionError: x must be negative

assert assert def reciprocal(x): assert x != 0 return 1 / x

assert assert assert

with/as Context Managers with as import from __future__ import with_statement

with/as Context Managers | 851

www.it-ebooks.info

with as

try

finally try finally

with

Basic Usage with with expression [as variable]: with-block

expression variable

as

variable expression

expression variable

expression

with-block

with with with open(r'C:\misc\data') as myfile: for line in myfile: print(line) ...more code here...

open myfile for with

myfile

with myfile

for with

852 | Chapter 33: Exception Coding Details

www.it-ebooks.info

try/finally myfile = open(r'C:\misc\data') try: for line in myfile: print(line) ...more code here... finally: myfile.close()

with lock = threading.Lock() with lock: ...access shared resources...

decimal

with decimal.localcontext() as ctx: ctx.prec = 2 x = decimal.Decimal('1.00') / decimal.Decimal('3.00')

try finally

The Context Management Protocol with with

with

with/as Context Managers | 853

www.it-ebooks.info

__enter__

__exit__ __enter__ as with

with

__exit__(type, value, traceback)

sys.exc_info

with with type value

__exit__ traceback

None with

with

class TraceBlock: def message(self, arg): print('running', arg) def __enter__(self): print('starting with block') return self def __exit__(self, exc_type, exc_value, exc_tb): if exc_type is None: print('exited normally\n') else: print('raise an exception!', exc_type) return False with TraceBlock() as action: action.message('test 1') print('reached') with TraceBlock() as action: action.message('test 2') raise TypeError print('not reached')

__exit__

False

return

None False

self

__enter__ as with

__enter__

__exit__

854 | Chapter 33: Exception Coding Details

www.it-ebooks.info

% python withas.py starting with block running test 1 reached exited normally starting with block running test 2 raise an exception! Traceback (most recent call last): File "withas.py", line 20, in raise TypeError TypeError

contextlib try finally

with

with open('data') as fin, open('res', 'w') as fout: for line in fin: if 'some key' in line: fout.write(line)

with with A() as a, B() as b: ...statements...

with A() as a: with B() as b: ...statements...

Chapter Summary try assert

raise

with

Chapter Summary | 855

www.it-ebooks.info

Test Your Knowledge: Quiz try try raise assert with as

Test Your Knowledge: Answers try

try

try except else

try finally except finally finally

try

raise raise assert

AssertionError raise

if

with as try finally

856 | Chapter 33: Exception Coding Details

www.it-ebooks.info

CHAPTER 34

Exception Objects

try try

857

www.it-ebooks.info

BaseException Exception

Exceptions: Back to the Future try

String Exceptions Are Right Out!

is C:\misc> C:\Python25\python >>> myexc = "My exception string" >>> try: ... raise myexc ... except myexc: ... print('caught') ... caught

858 | Chapter 34: Exception Objects

==

raise

www.it-ebooks.info

Class-Based Exceptions

except

except

is

except try

try

except except

except

Coding Exceptions Classes General Specific1 General

Specific2

Specific1

Specific2

General class General(Exception): pass class Specific1(General): pass

Exceptions: Back to the Future | 859

www.it-ebooks.info

class Specific2(General): pass def raiser0(): X = General() raise X def raiser1(): X = Specific1() raise X def raiser2(): X = Specific2() raise X for func in (raiser0, raiser1, raiser2): try: func() except General: import sys print('caught:', sys.exc_info()[0]) C:\python30> python classexc.py caught: caught: caught:

pass Exception

Exception

raise raise raise

raise

try try General

860 | Chapter 34: Exception Objects

General

www.it-ebooks.info

sys.exc_info

except sys.exc_info __class__ sys.exc_info

except

except __class__

class General(Exception): pass class Specific1(General): pass class Specific2(General): pass def raiser0(): raise General() def raiser1(): raise Specific1() def raiser2(): raise Specific2() for func in (raiser0, raiser1, raiser2): try: func() except General as X: print('caught:', X.__class__)

__class__ sys.exc_info

except

sys.exc_info __class__

Why Exception Hierarchies? except try:

func() except (General, Specific1, Specific2): ...

Why Exception Hierarchies? | 861

www.it-ebooks.info

except

class Divzero(Exception): pass class Oflow(Exception): pass def func(): ... raise Divzero()

try

import mathlib try:

mathlib.func(...) except (mathlib.Divzero, mathlib.Oflow): ...handle and recover...

class Divzero(Exception): pass class Oflow(Exception): pass class Uflow(Exception): pass

try:

mathlib.func(...) except (mathlib.Divzero, mathlib.Oflow, mathlib.Uflow): ...handle and recover...

862 | Chapter 34: Exception Objects

www.it-ebooks.info

try

except

try:

mathlib.func(...) except: ...handle and recover...

try

class NumErr(Exception): pass class Divzero(NumErr): pass class Oflow(NumErr): pass ... def func(): ... raise DivZero()

except

except

Why Exception Hierarchies? | 863

www.it-ebooks.info

import mathlib ... try: mathlib.func(...) except mathlib.NumErr: ...report and recover...

... class Uflow(NumErr): pass

Built-in Exception Classes

SyntaxError builtins __builtin__ exceptions BaseException Exception str print

864 | Chapter 34: Exception Objects

www.it-ebooks.info

args Exception BaseException SystemExit GeneratorExit BaseException

KeyboardInterrupt Exception

try

Exception except

try ArithmeticError

Exception OverflowError ArithmeticError

exceptions help >>> import exceptions >>> help(exceptions) ...lots of text omitted...

Built-in Exception Categories OverflowError

ArithmeticError ZeroDivisionError

ArithmeticError

try OverflowError Exception except try:

action() except Exception: ...handle all application exceptions... else: ...handle no-exception case...

Built-in Exception Classes | 865

www.it-ebooks.info

Exception except

Default Printing and State

args

>>> raise IndexError Traceback (most recent call last): File "", line 1, in IndexError >>> raise IndexError('spam') Traceback (most recent call last): File "", line 1, in IndexError: spam >>> I = IndexError('spam') >>> I.args ('spam',)

>>> class E(Exception): pass ... >>> try: ... raise E('spam') ... except E as X: ... print(X, X.args) ... spam ('spam',)

866 | Chapter 34: Exception Objects

www.it-ebooks.info

>>> try: ... raise E('spam', 'eggs', 'ham') ... except E as X: ... print(X, X.args) ... ('spam', 'eggs', 'ham') ('spam', 'eggs', 'ham')

__str__ str(X) + "string"

__str__

__init__

Exception

Custom Print Displays >>> class MyBad(Exception): pass ... >>> try: ... raise MyBad('Sorry--my mistake!') ... except MyBad as X: ... print(X) ... Sorry--my mistake!

>>> raise MyBad('Sorry--my mistake!') Traceback (most recent call last): File "", line 1, in __main__.MyBad: Sorry--my mistake!

__repr__ __str__

>>> class MyBad(Exception): ... def __str__(self): ... return 'Always look on the bright side of life...' ... >>> try: ... raise MyBad() ... except MyBad as X: ... print(X)

Custom Print Displays | 867

www.it-ebooks.info

... Always look on the bright side of life... >>> raise MyBad() Traceback (most recent call last): File "", line 1, in __main__.MyBad: Always look on the bright side of life...

__str__ __str__ __repr__

__str__ __repr__

__str__

Custom Data and Behavior args

Providing Exception Details raise try try try

try as except

868 | Chapter 34: Exception Objects

www.it-ebooks.info

>>> class FormatError(Exception): ... def __init__(self, line, file): ... self.line = line ... self.file = file ... >>> def parser(): ... raise FormatError(42, file='spam.txt') ... >>> try: ... parser() ... except FormatError as X: ... print('Error at', X.file, X.line) ... Error at spam.txt 42

except

X

>>> class FormatError(Exception): pass ... >>> def parser(): ... raise FormatError(42, 'spam.txt') ... >>> try: ... parser() ... except FormatError as X: ... print('Error at:', X.args[0], X.args[1]) ... Error at: 42 spam.txt

Providing Exception Methods

class FormatError(Exception): logfile = 'formaterror.txt' def __init__(self, line, file): self.line = line self.file = file

sys.exc_info() except sys.exc_info

Custom Data and Behavior | 869

www.it-ebooks.info

def logerror(self): log = open(self.logfile, 'a') print('Error at', self.file, self.line, file=log) def parser(): raise FormatError(40, 'spam.txt') try:

parser() except FormatError as exc: exc.logerror()

C:\misc> C:\Python30\python parse.py C:\misc> type formaterror.txt Error at spam.txt 40

line

Chapter Summary

try

870 | Chapter 34: Exception Objects

logerror file

www.it-ebooks.info

Test Your Knowledge: Quiz

Test Your Knowledge: Answers BaseException

Exception

args

__str__ print

str

Test Your Knowledge: Answers | 871

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 35

Designing with Exceptions

Nesting Exception Handlers try try try

try

try try

try

except

try try try try

except try

try

except try try raise func1

func2 func1 873

www.it-ebooks.info

finally

try finally try

finally try try finally try except

try except finally

874 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

Example: Control-Flow Nesting action2 action1

action2

try

def action2(): print(1 + []) def action1(): try: action2() except TypeError: print('inner try') try:

action1() except TypeError: print('outer try') % python nestexc.py inner try

action1

try try

action2 action1

TypeError try

try

except

action1

Example: Syntactic Nesting try except finally try try:

try:

action2() except TypeError: print('inner try') except TypeError: print('outer try')

Nesting Exception Handlers | 875

www.it-ebooks.info

try finally >>> try: ... try: ... raise IndexError ... finally: ... print('spam') ... finally: ... print('SPAM') ... spam SPAM Traceback (most recent call last): File "", line 3, in IndexError

def raise1(): raise IndexError def noraise(): return def raise2(): raise SyntaxError for func in (raise1, noraise, raise2): print('\n', func, sep='') try: try: func() except IndexError: print('caught IndexError') finally: print('finally run')

finally except try % python except-finally.py caught IndexError finally run finally run finally run

876 | Chapter 35: Designing with Exceptions

finally

www.it-ebooks.info

Traceback (most recent call last): File "except-finally.py", line 9, in func() File "except-finally.py", line 3, in raise2 def raise2(): raise SyntaxError SyntaxError: None

except

finally

try

Exception Idioms Exceptions Aren’t Always Errors input sys.stdin

EOFError raw_input

input

EOFError input

try

while True: try: line = input() except EOFError: break else: ...process next line here...

sys.exit() SystemExit Key boardInterrupt

warnings

Exception Idioms | 877

www.it-ebooks.info

Functions Can Signal Conditions with raise try except else if else class Found(Exception): pass def searcher(): if ...success...: raise Found() else: return try:

searcher() except Found: ...success... else: ...failure...

class Failure(Exception): pass def searcher(): if ...success...: return ...founditem... else: raise Failure() try:

item = searcher() except Failure: ...report... else: ...use item here...

Closing Files and Server Connections

878 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

try finally myfile = open(r'C:\misc\script', 'w') try: ...process myfile... finally: myfile.close()

with as with open(r'C:\misc\script', 'w') as myfile: ...process myfile...

try finally

try finally try finally try finally

Debugging with Outer try Statements try except sys.exc_info

sys

sys.exc_info

Exception Idioms | 879

www.it-ebooks.info

try:

...run program... except: import sys print('uncaught!', sys.exc_info()[0], sys.exc_info()[1])

Running In-Process Tests import sys log = open('testlog', 'a') from testapi import moreTests, runNextTest, testName def testdriver(): while moreTests(): try: runNextTest() except: print('FAILED', testName(), sys.exc_info()[:2], file=log) else: print('PASSED', testName(), file=log) testdriver()

testdriver

testapi try except sys.exc_info

else

os.system

os.popen

doctest

880 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

More on sys.exc_info sys.exc_info except try:

... except:

None (type, value, traceback) type value traceback traceback

sys.exc_info

__class__

as

sys.exc_info

except try:

... except General as instance:

try:

... except General as instance:

Exception Idioms | 881

www.it-ebooks.info

sys.exc_type sys.exc_value

sys.exc_info()

Exception Design Tips and Gotchas except

try

What Should Be Wrapped try try

try

try try

try finally with as

try

finally

try

try try

882 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

try

Catching Too Much: Avoid Empty except and Exception except try try

def func(): try: ... except: ... try: func() except IndexError: ...

sys.exit(statuscode) SystemExit try finally try

except

import sys def bye(): sys.exit(40) try: bye() except: print('got it')

os._exit try except

try finally

Exception Design Tips and Gotchas | 883

www.it-ebooks.info

print('continuing...') % python exiter.py got it continuing...

Exception

SystemExit

try:

bye() except Exception: ...

except Exception except

Exception

mydictionary = {...} ... try: x = myditctionary['spam'] except: x = None ...continue here with x...

myditctionary mydictionary

NameError

Exception

except

except Exception except KeyError:

884 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

Catching Too Little: Use Class-Based Categories try

MyExcept1

MyExcept2 MyExcept3

try:

... except (MyExcept1, MyExcept2): ... else: ...

except try:

... except SuccessCategoryName: ... else: ...

try

Core Language Summary

Core Language Summary | 885

www.it-ebooks.info

The Python Toolset

Category

Examples

Object types

Lists, dictionaries, files, strings

Functions

len, range, open

Exceptions

IndexError, KeyError

Modules

os, tkinter, pickle, re

Attributes

__dict__, __name__, __class__

Peripheral tools

NumPy, SWIG, Jython, IronPython, Django, etc.

886 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

Development Tools for Larger Projects

help

unittest __name__ == '__main__' unittest

doctest doctest

Core Language Summary | 887

www.it-ebooks.info

doctest doctest doctest

time

timeit

profile

profile

profile pstats profile.run('code')

profile

python -m profile main.py args... cProfile profile

pdb

888 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

pdb pdb.run("main()") python -m pdb main.py args... pdb.pm()

print

distutils

-O

Core Language Summary | 889

www.it-ebooks.info

from *

Chapter Summary

890 | Chapter 35: Designing with Exceptions

__all__ _X __name__ == '__main__'

www.it-ebooks.info

Test Your Knowledge: Quiz

Test Your Knowledge: Answers

Test Your Knowledge: Part VII Exercises

try except

oops

IndexError try except oops KeyError IndexError oops

KeyError

IndexError oops MyError try IndexError

safe(func, *args) *name exc_info oops oops

sys

safe safe

safe print_exc

traceback

Test Your Knowledge: Part VII Exercises | 891

www.it-ebooks.info

892 | Chapter 35: Designing with Exceptions

www.it-ebooks.info

PART VIII

Advanced Topics

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 36

Unicode and Byte Strings

str

895

www.it-ebooks.info

String Changes in 3.0 str unicode bytearray

str bytearray

struct bytes

str

sys.getdefaultencoding()

896 | Chapter 36: Unicode and Byte Strings

bytes

www.it-ebooks.info

String Basics

Character Encoding Schemes

'a' ord

97 chr

>>> ord('a') 97 >>> hex(97) '0x61' >>> chr(97) 'a'

>>> 0xC4 196 >>> chr(196) 'Ä'

String Basics | 897

www.it-ebooks.info

encodings help(encodings)

898 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

Python’s String Types

str unicode unicode str

str bytes bytearray

bytes bytearray

str

str str

unicode

String Basics | 899

www.it-ebooks.info

struct bytes str unicode bytes bytes str

re

bytes bytes

int

bytes

list bytes isalpha bytes bytearray bytearray

bytes str bytes append extend bytearray str

str

bytes

str bytearray

str

str

bytes str unicode

unicode str

bytes

bytearray

Text and Binary Files str bytes

900 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

str str

b open bytes bytes bytearray str

bytes str bytes

bytes bytearray

str open b bytes b rb wb

str rb+

bytes r w+

rt

str

String Basics | 901

www.it-ebooks.info

Python 3.0 Strings in Action str bytes str bytes(X)

str(X) '...' bytes bytes

b'...'

Literals and Basic Properties str bytes

open b'xxx'

B'xxx'

bytes

bytearray

bytearray 'xxx' "xxx" str b'...' C:\misc> c:\python30\python >>> B = b'spam' >>> S = 'eggs' >>> type(B), type(S) (, ) >>> B b'spam' >>> S 'eggs'

902 | Chapter 36: Unicode and Byte Strings

b

B

bytes r'...'

www.it-ebooks.info

bytes >>> B[0], S[0] (115, 'e') >>> B[1:], S[1:] (b'pam', 'ggs') >>> list(B), list(S) ([115, 112, 97, 109], ['e', 'g', 'g', 's'])

bytes

str str bytes

bytearray bytes

bytes

>>> B[0] = 'x' TypeError: 'bytes' object does not support item assignment >>> S[0] = 'x' TypeError: 'str' object does not support item assignment >>> B = B""" ... xxxx ... yyyy ... """ >>> B b'\nxxxx\nyyyy\n'

'xxx'

str

b'xxx' bytes bytes

str u'xxx'

U'xxx'

'xxx'

Conversions str

unicode str

bytes str

str.encode() bytes bytes.decode() str

bytes

bytes(S, encoding) str str(B, encoding) bytes Python 3.0 Strings in Action | 903

www.it-ebooks.info

encode

decode

>>> S = 'eggs' >>> S.encode() b'eggs' >>> bytes(S, encoding='ascii') b'eggs' >>> B = b'spam' >>> B.decode() 'spam' >>> str(B, encoding='ascii') 'spam'

sys str.encode

bytes bytes.decode str

bytes

str

str bytes

str B

S

>>> import sys >>> sys.platform 'win32' >>> sys.getdefaultencoding() 'utf-8' >>> bytes(S) TypeError: string argument without an encoding >>> str(B) "b'spam'" >>> len(str(B)) 7 >>> len(str(B, encoding='ascii')) 4

Coding Unicode Strings "\xNN"

904 | Chapter 36: Unicode and Byte Strings

"\uNNNN"

"\UNNNNNNNN"

www.it-ebooks.info

Coding ASCII Text

C:\misc> c:\python30\python >>> ord('X') 88 >>> chr(88) 'X' >>> S = 'XYZ' >>> S 'XYZ' >>> len(S) 3 >>> [ord(c) for c in S] [88, 89, 90]

>>> S.encode('ascii') b'XYZ' >>> S.encode('latin-1') b'XYZ' >>> S.encode('utf-8') b'XYZ'

bytes >>> S.encode('latin-1')[0] 88 >>> list(S.encode('latin-1')) [88, 89, 90]

Coding Non-ASCII Text 0xCD str

0xE8

str

Coding Unicode Strings | 905

www.it-ebooks.info

>>> chr(0xc4) 'Ä' >>> chr(0xe8) 'è' >>> S = '\xc4\xe8' >>> S 'Äè' >>> S = '\u00c4\u00e8' >>> S 'Äè' >>> len(S) 2

Encoding and Decoding Non-ASCII text

bytes >>> S = '\u00c4\u00e8' >>> S 'Äè' >>> len(S) 2 >>> S.encode('ascii') UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128) >>> S.encode('latin-1') b'\xc4\xe8' >>> S.encode('utf-8') b'\xc3\x84\xc3\xa8' >>> len(S.encode('latin-1')) 2 >>> len(S.encode('utf-8')) 4

open

>>> B = b'\xc4\xe8' >>> B b'\xc4\xe8'

906 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

>>> len(B) 2 >>> B.decode('latin-1') 'Äè' >>> B = b'\xc3\x84\xc3\xa8' >>> len(B) 4 >>> B.decode('utf-8') 'Äè' >>> len(B.decode('utf-8')) 2

Other Unicode Coding Techniques "\u..."

"\U...."

>>> S = 'A\u00c4B\U000000e8C' >>> S 'AÄBèC' >>> len(S) 5 >>> S.encode('latin-1') b'A\xc4B\xe8C' >>> len(S.encode('latin-1')) 5 >>> S.encode('utf-8') b'A\xc3\x84B\xc3\xa8C' >>> len(S.encode('utf-8')) 7

cp500

>>> S 'AÄBèC' >>> S.encode('cp500') b'\xc1c\xc2T\xc3' >>> S.encode('cp850') b'A\x8eB\x8aC' >>> S = 'spam' >>> S.encode('latin-1') b'spam' >>> S.encode('utf-8') b'spam' >>> S.encode('cp500')

Coding Unicode Strings | 907

www.it-ebooks.info

b'\xa2\x97\x81\x94' >>> S.encode('cp850') b'spam'

chr >>> S = 'A' + chr(0xC4) + 'B' + chr(0xE8) + 'C' >>> S 'AÄBèC'

str

bytes bytes

bytes

str

>>> S = 'A\xC4B\xE8C' >>> S 'AÄBèC' >>> S = 'A\u00C4B\U000000E8C' >>> S 'AÄBèC' >>> B = b'A\xC4B\xE8C' >>> B b'A\xc4B\xe8C' >>> B = b'A\u00C4B\U000000E8C' >>> B b'A\\u00C4B\\U000000E8C' >>> B = b'A\xC4B\xE8C' >>> B b'A\xc4B\xe8C' >>> print(B) b'A\xc4B\xe8C' >>> B.decode('latin-1') 'AÄBèC'

bytes str

>>> S = 'AÄBèC' >>> S 'AÄBèC' >>> B = b'AÄBèC' SyntaxError: bytes can only contain ASCII literal characters. >>> B = b'A\xC4B\xE8C' >>> B

908 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

b'A\xc4B\xe8C' >>> B.decode('latin-1') 'AÄBèC' >>> S.encode() b'A\xc3\x84B\xc3\xa8C' >>> S.encode('utf-8') b'A\xc3\x84B\xc3\xa8C' >>> B.decode() UnicodeDecodeError: 'utf8' codec can't decode bytes in position 1-2: ...

Converting Encodings

>>> S = 'AÄBèC' >>> S 'AÄBèC' >>> S.encode() b'A\xc3\x84B\xc3\xa8C' >>> T = S.encode('cp500') >>> T b'\xc1c\xc2T\xc3' >>> U = T.decode('cp500') >>> U 'AÄBèC' >>> U.encode() b'A\xc3\x84B\xc3\xa8C'

open

Coding Unicode Strings | 909

www.it-ebooks.info

Coding Unicode Strings in Python 2.6 unicode str str

bytes

C:\misc> c:\python26\python >>> import sys >>> sys.version '2.6 (r26:66721, Oct 2 2008, 11:35:03) [MSC v.1500 32 bit (Intel)]' >>> S = 'A\xC4B\xE8C' >>> print S AÄBèC >>> S.decode('latin-1') u'A\xc4B\xe8C' >>> S.decode('utf-8') UnicodeDecodeError: 'utf8' codec can't decode bytes in position 1-2: invalid data >>> S.decode('ascii') UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 1: ordinal not in range(128)

unicode

u'xxx'

>>> U = u'A\xC4B\xE8C' >>> U u'A\xc4B\xe8C' >>> print U AÄBèC

str

bytes

>>> U.encode('latin-1') 'A\xc4B\xe8C' >>> U.encode('utf-8') 'A\xc3\x84B\xc3\xa8C'

bytes unicode C:\misc> c:\python26\python >>> U = u'A\xC4B\xE8C' >>> U u'A\xc4B\xe8C'

910 | Chapter 36: Unicode and Byte Strings

"\u..." str

"\U..."

www.it-ebooks.info

>>> print U AÄBèC >>> U = u'A\u00C4B\U000000E8C' >>> U u'A\xc4B\xe8C' >>> print U AÄBèC >>> S = 'A\xC4B\xE8C' >>> S 'A\xc4B\xe8C' >>> print S A-BFC >>> print S.decode('latin-1') AÄBèC >>> S = 'A\u00C4B\U000000E8C' >>> S 'A\\u00C4B\\U000000E8C' >>> print S A\u00C4B\U000000E8C >>> len(S) 19

str

bytes

unicode

str unicode

str unicode str unicode

str str

unicode bytes

>>> u'ab' + 'cd' u'abcd'

re str

unicode

>>> str(u'spam') 'spam' >>> unicode('spam') u'spam'

unicode >>> S = 'A\xC4B\xE8C' >>> U = u'A\xC4B\xE8C' >>> S + U UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 1: ordinal not in range(128)

Coding Unicode Strings | 911

www.it-ebooks.info

>>> S.decode('latin-1') + U u'A\xc4B\xe8CA\xc4B\xe8C' >>> print S.decode('latin-1') + U AÄBèCAÄBèC

open str codecs.open open

unicode

Source File Character Set Encoding Declarations

# -*- coding: latin-1 -*-

# -*- coding: latin-1 -*-

myStr1 = 'aÄBèC' myStr2 = 'A\u00c4B\U000000e8C' myStr3 = 'A' + chr(0xC4) + 'B' + chr(0xE8) + 'C' import sys print('Default encoding:', sys.getdefaultencoding()) for aStr in myStr1, myStr2, myStr3:

912 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

print('{0}, strlen={1}, '.format(aStr, len(aStr)), end='') bytes1 = aStr.encode() bytes2 = aStr.encode('latin-1') #bytes3 = aStr.encode('ascii') print('byteslen1={0}, byteslen2={1}'.format(len(bytes1), len(bytes2)))

C:\misc> c:\python30\python text.py Default encoding: utf-8 aÄBèC, strlen=5, byteslen1=7, byteslen2=5 AÄBèC, strlen=5, byteslen1=7, byteslen2=5 AÄBèC, strlen=5, byteslen1=7, byteslen2=5

Using 3.0 Bytes Objects str

bytes bytes

str mat str

str

bytes

for

%

bytes str bytes

Method Calls str

bytes

dir __mod__

__rmod__

%

C:\misc> c:\python30\python

>>> set(dir('abc')) - set(dir(b'abc')) {'isprintable', 'format', '__mod__', 'encode', 'isidentifier', '_formatter_field_name_split', 'isnumeric', '__rmod__', 'isdecimal',

Using 3.0 Bytes Objects | 913

www.it-ebooks.info

'_formatter_parser', 'maketrans'}

>>> set(dir(b'abc')) - set(dir('abc')) {'decode', 'fromhex'}

str

bytes decode

bytes

str

encode

bytes bytes

bytes bytes str

>>> B = b'spam' >>> B.find(b'pa') 1 >>> B.replace(b'pa', b'XY') b'sXYm' >>> B.split(b'pa') [b's', b'm'] >>> B b'spam' >>> B[0] = 'x' TypeError: 'bytes' object does not support item assignment

str bytes >>> b'%s' % 99 TypeError: unsupported operand type(s) for %: 'bytes' and 'int' >>> '%s' % 99 '99' >>> b'{0}'.format(99) AttributeError: 'bytes' object has no attribute 'format' >>> '{0}'.format(99) '99'

Sequence Operations str

914 | Chapter 36: Unicode and Byte Strings

bytes

www.it-ebooks.info

bytes

bytes chr

>>> B = b'spam' >>> B b'spam' >>> B[0] 115 >>> B[-1] 109 >>> chr(B[0]) 's' >>> list(B) [115, 112, 97, 109] >>> B[1:], B[:-1] (b'pam', b'spa') >>> len(B) 4 >>> B + b'lmn' b'spamlmn' >>> B * 4 b'spamspamspamspam'

Other Ways to Make bytes Objects bytes bytes

b'...' str

bytes str str bytes

>>> B = b'abc' >>> B b'abc' >>> B = bytes('abc', 'ascii') >>> B b'abc' >>> ord('a') 97 >>> B = bytes([97, 98, 99])

Using 3.0 Bytes Objects | 915

www.it-ebooks.info

>>> B b'abc' >>> B = 'spam'.encode() >>> B b'spam' >>> >>> S = B.decode() >>> S 'spam'

str

bytes

Mixing String Types replace bytes

str str

unicode

>>> B = b'spam' >>> B.replace('pa', 'XY') TypeError: expected an object with the buffer interface >>> B.replace(b'pa', b'XY') b'sXYm' >>> B = B'spam' >>> B.replace(bytes('pa'), bytes('xy')) TypeError: string argument without an encoding >>> B.replace(bytes('pa', 'ascii'), bytes('xy', 'utf-8')) b'sxym'

>>> b'ab' + 'cd' TypeError: can't concat bytes to str >>> b'ab'.decode() + 'cd' 'abcd' >>> b'ab' + 'cd'.encode() b'abcd'

916 | Chapter 36: Unicode and Byte Strings

str

www.it-ebooks.info

>>> b'ab' + bytes('cd', 'ascii') b'abcd'

bytes bytes

Using 3.0 (and 2.6) bytearray Objects str

bytes

str

unicode bytearray bytes bytes bytearray

bytearray

bytearray

>>> S = 'spam' >>> C = bytearray(S) >>> C bytearray(b'spam')

>>> S = 'spam' >>> C = bytearray(S) TypeError: string argument without an encoding >>> C = bytearray(S, 'latin1') >>> C bytearray(b'spam') >>> B = b'spam' >>> C = bytearray(B) >>> C bytearray(b'spam')

bytearray

bytes

Using 3.0 (and 2.6) bytearray Objects | 917

www.it-ebooks.info

>>> C[0] 115 >>> C[0] = 'x' TypeError: an integer is required >>> C[0] = b'x' TypeError: an integer is required >>> C[0] = ord('x') >>> C bytearray(b'xpam') >>> C[1] = b'Y'[0] >>> C bytearray(b'xYam')

bytearray __iadd__ bytearray

__setitem__

+=

>>> set(dir(b'abc')) - set(dir(bytearray(b'abc'))) {'__getnewargs__'} >>> set(dir(bytearray(b'abc'))) - set(dir(b'abc')) {'insert', '__alloc__', 'reverse', 'extend', '__delitem__', 'pop', '__setitem__' , '__iadd__', 'remove', 'append', '__imul__'}

bytearray list(str)

>>> C bytearray(b'xYam') >>> C.append(b'LMN') TypeError: an integer is required >>> C.append(ord('L')) >>> C bytearray(b'xYamL') >>> C.extend(b'MNO') >>> C bytearray(b'xYamLMNO')

918 | Chapter 36: Unicode and Byte Strings

''.join(list)

www.it-ebooks.info

bytearray bytes

bytes

str

>>> C + b'!#' bytearray(b'xYamLMNO!#') >>> C[0] 120 >>> C[1:] bytearray(b'YamLMNO') >>> len(C) 8 >>> C bytearray(b'xYamLMNO') >>> C.replace('xY', 'sp') TypeError: Type str doesn't support the buffer API >>> C.replace(b'xY', b'sp') bytearray(b'spamLMNO') >>> C bytearray(b'xYamLMNO') >>> C * 4 bytearray(b'xYamLMNOxYamLMNOxYamLMNOxYamLMNO')

bytes bytearray

int

str

>>> B b'spam' >>> list(B) [115, 112, 97, 109] >>> C bytearray(b'xYamLMNO') >>> list(C) [120, 89, 97, 109, 76, 77, 78, 79] >>> S 'spam' >>> list(S) ['s', 'p', 'a', 'm']

Using 3.0 (and 2.6) bytearray Objects | 919

www.it-ebooks.info

str bytes bytearray

Using Text and Binary Files str

bytes

open '\n'

open "rb"

"rt"

"r"

open str bytes bytearray

Text File Basics

920 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

file C:\misc> c:\python30\python

>>> file = open('temp', 'w') >>> size = file.write('abc\n') >>> file.close() >>> file = open('temp') >>> text = file.read() >>> text 'abc\n' >>> print(text) abc

Text and Binary Modes in 3.0 str \n

\r\n

C:\misc> c:\python26\python >>> open('temp', 'w').write('abd\n') >>> open('temp', 'r').read() 'abd\n' >>> open('temp', 'rb').read() 'abd\r\n' >>> open('temp', 'wb').write('abc\n') >>> open('temp', 'r').read() 'abc\n' >>> open('temp', 'rb').read() 'abc\n'

str bytes str str

bytes

open

C:\misc> c:\python30\python

>>> open('temp', 'w').write('abc\n') 4 >>> open('temp', 'r').read() 'abc\n'

Using Text and Binary Files | 921

www.it-ebooks.info

>>> open('temp', 'rb').read() b'abc\r\n'

\n \r\n

\r\n \n open bytes

str

bytes

>>> open('temp', 'wb').write(b'abc\n') 4 >>> open('temp', 'r').read() 'abc\n' >>> open('temp', 'rb').read() b'abc\n'

\n

\r\n

"\x00"

>>> open('temp', 'wb').write(b'a\x00c') 3 >>> open('temp', 'r').read() 'a\x00c' >>> open('temp', 'rb').read() b'a\x00c'

bytes bytearray

bytes bytearray

bytes bytearray

>>> BA = bytearray(b'\x01\x02\x03') >>> open('temp', 'wb').write(BA) 3 >>> open('temp', 'r').read() '\x01\x02\x03'

922 | Chapter 36: Unicode and Byte Strings

bytes

www.it-ebooks.info

>>> open('temp', 'rb').read() b'\x01\x02\x03'

Type and Content Mismatches str bytes bytes

str

>>> open('temp', 'w').write('abc\n') 4 >>> open('temp', 'w').write(b'abc\n') TypeError: can't write bytes to text stream >>> open('temp', 'wb').write(b'abc\n') 4 >>> open('temp', 'wb').write('abc\n') TypeError: can't write str to binary stream

str

bytes str

bytes

str

str

bytes

bytes

>>> chr(0xFF) 'ÿ' >>> chr(0xFE) UnicodeEncodeError: 'charmap' codec can't encode character '\xfe' in position 1... >>> open('temp', 'w').write(b'\xFF\xFE\xFD') TypeError: can't write bytes to text stream >>> open('temp', 'w').write('\xFF\xFE\xFD') 3 >>> open('temp', 'wb').write(b'\xFF\xFE\xFD') 3 >>> open('temp', 'rb').read()

Using Text and Binary Files | 923

www.it-ebooks.info

b'\xff\xfe\xfd' >>> open('temp', 'r').read() UnicodeEncodeError: 'charmap' codec can't encode characters in position 2-3: ...

Using Unicode Files open

Reading and Writing Unicode in 3.0

C:\misc> c:\python30\python >>> S = 'A\xc4B\xe8C' >>> S 'AÄBèC' >>> len(S) 5

Manual encoding

>>> L = S.encode('latin-1') >>> L b'A\xc4B\xe8C' >>> len(L) 5 >>> U = S.encode('utf-8') >>> U b'A\xc3\x84B\xc3\xa8C' >>> len(U) 7

924 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

File output encoding open

>>> open('latindata', 'w', encoding='latin-1').write(S) 5 >>> open('utf8data', 'w', encoding='utf-8').write(S) 5 >>> open('latindata', 'rb').read() b'A\xc4B\xe8C' >>> open('utf8data', 'rb').read() b'A\xc3\x84B\xc3\xa8C'

File input decoding open

>>> open('latindata', 'r', encoding='latin-1').read() 'AÄBèC' >>> open('utf8data', 'r', encoding='utf-8').read() 'AÄBèC' >>> X = open('latindata', 'rb').read() >>> X.decode('latin-1') 'AÄBèC' >>> X = open('utf8data', 'rb').read() >>> X.decode() 'AÄBèC'

Decoding mismatches

str

>>> file = open('python.exe', 'r') >>> text = file.read() UnicodeDecodeError: 'charmap' codec can't decode byte 0x90 in position 2: ... >>> file = open('python.exe', 'rb')

Using Unicode Files | 925

www.it-ebooks.info

>>> data = file.read() >>> data[:20] b'MZ\x90\x00\x03\x00\x00\x00\x04\x00\x00\x00\xff\xff\x00\x00\xb8\x00\x00\x00'

\r\n \n open

Handling the BOM in 3.0

c:\misc> C:\Python30\python >>> import sys >>> sys.getdefaultencoding() 'utf-8' >>> open('spam.txt', 'rb').read() b'spam\r\nSPAM\r\n' >>> open('spam.txt', 'r').read() 'spam\nSPAM\n' >>> open('spam.txt', 'r', encoding='utf-8').read() 'spam\nSPAM\n'

>>> open('spam.txt', 'rb').read() b'\xef\xbb\xbfspam\r\nSPAM\r\n' >>> open('spam.txt', 'r').read() 'spam\nSPAM\n' >>> open('spam.txt', 'r', encoding='utf-8').read() '\ufeffspam\nSPAM\n' >>> open('spam.txt', 'r', encoding='utf-8-sig').read() 'spam\nSPAM\n'

926 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

>>> open('spam.txt', 'rb').read() b'\xfe\xff\x00s\x00p\x00a\x00m\x00\r\x00\n\x00S\x00P\x00A\x00M\x00\r\x00\n' >>> open('spam.txt', 'r').read() UnicodeEncodeError: 'charmap' codec can't encode character '\xfe' in position 1:... >>> open('spam.txt', 'r', encoding='utf-16').read() 'spam\nSPAM\n' >>> open('spam.txt', 'r', encoding='utf-16-be').read() '\ufeffspam\nSPAM\n'

>>> open('temp.txt', 'w', encoding='utf-8').write('spam\nSPAM\n') 10 >>> open('temp.txt', 'rb').read() b'spam\r\nSPAM\r\n' >>> open('temp.txt', 'w', encoding='utf-8-sig').write('spam\nSPAM\n') 10 >>> open('temp.txt', 'rb').read() b'\xef\xbb\xbfspam\r\nSPAM\r\n' >>> open('temp.txt', 'r').read() 'spam\nSPAM\n' >>> open('temp.txt', 'r', encoding='utf-8').read() '\ufeffspam\nSPAM\n' >>> open('temp.txt', 'r', encoding='utf-8-sig').read() 'spam\nSPAM\n'

>>> open('temp.txt', 10 >>> open('temp.txt', b'spam\r\nSPAM\r\n' >>> open('temp.txt', 'spam\nSPAM\n' >>> open('temp.txt', 'spam\nSPAM\n' >>> open('temp.txt', 'spam\nSPAM\n'

'w').write('spam\nSPAM\n') 'rb').read() 'r').read() 'r', encoding='utf-8').read() 'r', encoding='utf-8-sig').read()

Using Unicode Files | 927

www.it-ebooks.info

>>> sys.byteorder 'little' >>> open('temp.txt', 'w', encoding='utf-16').write('spam\nSPAM\n') 10 >>> open('temp.txt', 'rb').read() b'\xff\xfes\x00p\x00a\x00m\x00\r\x00\n\x00S\x00P\x00A\x00M\x00\r\x00\n\x00' >>> open('temp.txt', 'r', encoding='utf-16').read() 'spam\nSPAM\n' >>> open('temp.txt', 'w', encoding='utf-16-be').write('\ufeffspam\nSPAM\n') 11 >>> open('spam.txt', 'rb').read() b'\xfe\xff\x00s\x00p\x00a\x00m\x00\r\x00\n\x00S\x00P\x00A\x00M\x00\r\x00\n' >>> open('temp.txt', 'r', encoding='utf-16').read() 'spam\nSPAM\n' >>> open('temp.txt', 'r', encoding='utf-16-be').read() '\ufeffspam\nSPAM\n'

>>> open('temp.txt', 'w', encoding='utf-16-le').write('SPAM') 4 >>> open('temp.txt', 'rb').read() b'S\x00P\x00A\x00M\x00' >>> open('temp.txt', 'r', encoding='utf-16-le').read() 'SPAM' >>> open('temp.txt', 'r', encoding='utf-16').read() UnicodeError: UTF-16 stream does not start with BOM

Unicode Files in 2.6 str unicode

open

codecs.open

C:\misc> c:\python26\python >>> S = u'A\xc4B\xe8C' >>> print S AÄBèC >>> len(S) 5 >>> S.encode('latin-1') 'A\xc4B\xe8C' >>> S.encode('utf-8') 'A\xc3\x84B\xc3\xa8C' >>> import codecs

928 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

>>> codecs.open('latindata', 'w', encoding='latin-1').write(S) >>> codecs.open('utfdata', 'w', encoding='utf-8').write(S) >>> open('latindata', 'rb').read() 'A\xc4B\xe8C' >>> open('utfdata', 'rb').read() 'A\xc3\x84B\xc3\xa8C' >>> codecs.open('latindata', 'r', encoding='latin-1').read() u'A\xc4B\xe8C' >>> codecs.open('utfdata', 'r', encoding='utf-8').read() u'A\xc4B\xe8C'

Other String Tool Changes in 3.0 str bytes re struct

pickle

xml

The re Pattern Matching Module re find split

replace

re str bytes

(.*) ()

bytearray

.

* group

groups

C:\misc> c:\python30\python >>> import re >>> S = 'Bugger all down here on earth!' >>> B = b'Bugger all down here on earth!' >>> re.match('(.*) down (.*) on (.*)', S).groups() ('Bugger all', 'here', 'earth!') >>> re.match(b'(.*) down (.*) on (.*)', B).groups() (b'Bugger all', b'here', b'earth!')

unicode str

Other String Tool Changes in 3.0 | 929

www.it-ebooks.info

C:\misc> c:\python26\python >>> import re >>> S = 'Bugger all down here on earth!' >>> U = u'Bugger all down here on earth!' >>> re.match('(.*) down (.*) on (.*)', S).groups() ('Bugger all', 'here', 'earth!') >>> re.match('(.*) down (.*) on (.*)', U).groups() (u'Bugger all', u'here', u'earth!')

bytes

str str

bytes

C:\misc> c:\python30\python >>> import re >>> S = 'Bugger all down here on earth!' >>> B = b'Bugger all down here on earth!' >>> re.match('(.*) down (.*) on (.*)', B).groups() TypeError: can't use a string pattern on a bytes-like object >>> re.match(b'(.*) down (.*) on (.*)', S).groups() TypeError: can't use a bytes pattern on a string-like object >>> re.match(b'(.*) down (.*) on (.*)', bytearray(B)).groups() (bytearray(b'Bugger all'), bytearray(b'here'), bytearray(b'earth!')) >>> re.match('(.*) down (.*) on (.*)', bytearray(B)).groups() TypeError: can't use a string pattern on a bytes-like object

The struct Binary Data Module struct bytes bytearray

str

C:\misc> c:\python30\python >>> from struct import pack >>> pack('>i4sh', 7, 'spam', 8) b'\x00\x00\x00\x07spam\x00\x08' C:\misc> c:\python26\python >>> from struct import pack >>> pack('>i4sh', 7, 'spam', 8) '\x00\x00\x00\x07spam\x00\x08'

930 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

bytes

str bytes

C:\misc> c:\python30\python >>> import struct >>> B = struct.pack('>i4sh', 7, 'spam', 8) >>> B b'\x00\x00\x00\x07spam\x00\x08' >>> vals = struct.unpack('>i4sh', B) >>> vals (7, b'spam', 8) >>> vals = struct.unpack('>i4sh', B.decode()) TypeError: 'str' does not have the buffer interface

bytes C:\misc> c:\python30\python

>>> F = open('data.bin', 'wb') >>> import struct >>> data = struct.pack('>i4sh', 7, 'spam', 8) >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> F.write(data) 10 >>> F.close()

>>> F = open('data.bin', 'rb') >>> data = F.read() >>> data b'\x00\x00\x00\x07spam\x00\x08' >>> values = struct.unpack('>i4sh', data) >>> values (7, b'spam', 8)

>>> values (7, b'spam', 8)

Other String Tool Changes in 3.0 | 931

www.it-ebooks.info

>>> bin(values[0]) '0b111' >>> values[0] & 0x01 1 >>> values[0] | 0b1010 15 >>> bin(values[0] | 0b1010) '0b1111' >>> bin(values[0] ^ 0b1010) '0b1101' >>> bool(values[0] & 0b100) True >>> bool(values[0] & 0b1000) False

bytes

>>> values[1] b'spam' >>> values[1][0] 115 >>> values[1][1:] b'pam' >>> bin(values[1][0]) '0b1110011' >>> bin(values[1][0] | 0b1100) '0b1111111' >>> values[1][0] | 0b1100 127

The pickle Object Serialization Module pickle shelve

pickle pickle dumps

C:\misc> C:\Python30\python >>> import pickle >>> pickle.dumps([1, 2, 3])

932 | Chapter 36: Unicode and Byte Strings

bytes

www.it-ebooks.info

b'\x80\x03]q\x00(K\x01K\x02K\x03e.' >>> pickle.dumps([1, 2, 3], protocol=0) b'(lp0\nL1L\naL2L\naL3L\na.'

str

bytes

dump >>> pickle.dump([1, 2, 3], open('temp', 'w')) TypeError: can't write bytes to text stream >>> pickle.dump([1, 2, 3], open('temp', 'w'), protocol=0) TypeError: can't write bytes to text stream >>> pickle.dump([1, 2, 3], open('temp', 'wb')) >>> open('temp', 'r').read() UnicodeEncodeError: 'charmap' codec can't encode character '\u20ac' in ...

>>> pickle.dump([1, 2, 3], open('temp', 'wb')) >>> pickle.load(open('temp', 'rb')) [1, 2, 3] >>> open('temp', 'rb').read() b'\x80\x03]q\x00(K\x01K\x02K\x03e.'

C:\misc> c:\python26\python >>> import pickle >>> pickle.dumps([1, 2, 3]) '(lp0\nI1\naI2\naI3\na.' >>> pickle.dumps([1, 2, 3], protocol=1) ']q\x00(K\x01K\x02K\x03e.' >>> pickle.dump([1, 2, 3], open('temp', 'w')) >>> pickle.load(open('temp')) [1, 2, 3] >>> open('temp').read() '(lp0\nI1\naI2\naI3\na.'

>>> >>> >>> [1,

import pickle pickle.dump([1, 2, 3], open('temp', 'wb')) pickle.load(open('temp', 'rb')) 2, 3]

Other String Tool Changes in 3.0 | 933

www.it-ebooks.info

XML Parsing Tools re

unicode str

str

2009 Learning Python Programming Python Python Pocket Reference O'Reilly Media

title Learning Python Programming Python Python Pocket Reference

re match search

934 | Chapter 36: Unicode and Byte Strings

findall

www.it-ebooks.info

import re text = open('mybooks.xml').read() found = re.findall('(.*)', text) for title in found: print(title)

from xml.dom.minidom import parse, Node xmltree = parse('mybooks.xml') for node1 in xmltree.getElementsByTagName('title'): for node2 in node1.childNodes: if node2.nodeType == Node.TEXT_NODE: print(node2.data)

import xml.sax.handler class BookHandler(xml.sax.handler.ContentHandler): def __init__(self): self.inTitle = False def startElement(self, name, attributes): if name == 'title': self.inTitle = True def characters(self, data): if self.inTitle: print(data) def endElement(self, name): if name == 'title': self.inTitle = False import xml.sax parser = xml.sax.make_parser() handler = BookHandler() parser.setContentHandler(handler) parser.parse('mybooks.xml')

etree

Other String Tool Changes in 3.0 | 935

www.it-ebooks.info

from xml.etree.ElementTree import parse tree = parse('mybooks.xml') for E in tree.findall('title'): print(E.text)

C:\misc> c:\python26\python domparse.py Learning Python Programming Python Python Pocket Reference C:\misc> c:\python30\python domparse.py Learning Python Programming Python Python Pocket Reference

unicode str C:\misc> c:\python30\python >>> from xml.dom.minidom import parse, Node >>> xmltree = parse('mybooks.xml') >>> for node in xmltree.getElementsByTagName('title'): ... for node2 in node.childNodes: ... if node2.nodeType == Node.TEXT_NODE: ... node2.data ... 'Learning Python' 'Programming Python' 'Python Pocket Reference' C:\misc> c:\python26\python >>> ...same code... ... u'Learning Python' u'Programming Python' u'Python Pocket Reference'

unicode

str

re struct pickle

936 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

Chapter Summary

bytes

Test Your Knowledge: Quiz

Test Your Knowledge: Answers str

bytes bytearray

bytes

str

Test Your Knowledge: Answers | 937

www.it-ebooks.info

str str

unicode unicode

bytearray

str

bytes bytearray

str

str

str unicode

str bytearray str bytes \xNN \uNNNN \UNNNNNNNN

str bytes bytearray \n

open codecs.open()

open

codecs.open()

938 | Chapter 36: Unicode and Byte Strings

www.it-ebooks.info

str re struct pickle str unicode

codecs.open()

bytes str bytes

xml

str open

Test Your Knowledge: Answers | 939

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 37

Managed Attributes

Why Manage Attributes? name person.name person.name = value

name

valid

transform

class Person: def getName(self): if not valid(): raise TypeError('cannot fetch name') else: return self.name.transform()

941

www.it-ebooks.info

def setName(self, value): if not valid(value): raise TypeError('cannot change name') else: self.name = transform(value) person = Person() person.getName() person.setName('value')

Inserting Code to Run on Attribute Access

__getattr__

__setattr__

__getattribute__ property

942 | Chapter 37: Managed Attributes

www.it-ebooks.info

CardHolder

Properties

property

self

The Basics attribute = property(fget, fset, fdel, doc)

None fget fdel

fset doc

fget fset

fdel

None fget None

Properties | 943

www.it-ebooks.info

A First Example name

_name

class Person: def __init__(self, name): self._name = name def getName(self): print('fetch...') return self._name def setName(self, value): print('change...') self._name = value def delName(self): print('remove...') del self._name name = property(getName, setName, delName, "name property docs") bob = Person('Bob Smith') print(bob.name) bob.name = 'Robert Smith' print(bob.name) del bob.name print('-'*20) sue = Person('Sue Jones') print(sue.name) print(Person.name.__doc__)

object object

fetch... Bob Smith change... fetch... Robert Smith remove... -------------------fetch... Sue Jones name property docs

944 | Chapter 37: Managed Attributes

www.it-ebooks.info

class Super: ...the original Person class code... name = property(getName, setName, delName, 'name property docs') class Person(Super): pass bob = Person('Bob Smith') ...rest unchanged...

Person Person

bob

name

Super

self

Computed Attributes

class PropSquare: def __init__(self, start): self.value = start def getX(self): return self.value ** 2 def setX(self, value): self.value = value X = property(getX, setX) P = PropSquare(3) Q = PropSquare(32) print(P.X) P.X = 4 print(P.X) print(Q.X)

X

9 16 1024

self

Properties | 945

www.it-ebooks.info

Coding Properties with Decorators @decorator def func(args): ...

decorator def func(args): ... func = decorator(func)

property class Person: @property def name(self): ...

property class Person: def name(self): ... name = property(name)

getter setter

getter class Person: def __init__(self, name): self._name = name @property def name(self): "name property docs" print('fetch...') return self._name @name.setter def name(self, value): print('change...') self._name = value @name.deleter def name(self): print('remove...') del self._name

946 | Chapter 37: Managed Attributes

deleter

www.it-ebooks.info

bob = Person('Bob Smith') print(bob.name) bob.name = 'Robert Smith' print(bob.name) del bob.name print('-'*20) sue = Person('Sue Jones') print(sue.name) print(Person.name.__doc__)

fetch... Bob Smith change... fetch... Robert Smith remove... -------------------fetch... Sue Jones name property docs

property

Descriptors property

self

Descriptors | 947

www.it-ebooks.info

The Basics

class Descriptor: "docstring goes here" def __get__(self, instance, owner): ... def __set__(self, instance, value): ... def __delete__(self, instance): ...

__set__ __set__

Descriptor method arguments

self instance __get__

self

948 | Chapter 37: Managed Attributes

owner instance instance.attr None class.attr

www.it-ebooks.info

X.attr __get__

Descriptor

Subject.attr object

>>> class Descriptor(object): ... def __get__(self, instance, owner): ... print(self, instance, owner, sep='\n') ... >>> class Subject: ... attr = Descriptor() ... >>> X = Subject() >>> X.attr >>> Subject.attr None

__get__ X.attr Subject.attr X.attr

->

__get__

Descriptor.__get__(Subject.attr, X, Subject)

None

Read-only descriptors __set__ X.a

a >>> ... ... >>> ... ... >>> >>> get >>> get >>> >>> 99 >>>

X

C

class D: def __get__(*args): print('get') class C: a = D() X = C() X.a C.a X.a = 99 X.a list(X.__dict__.keys())

Descriptors | 949

www.it-ebooks.info

['a'] >>> Y = C() >>> Y.a get >>> C.a get

>>> class D: ... def __get__(*args): print('get') ... def __set__(*args): raise AttributeError('cannot set') ... >>> class C: ... a = D() ... >>> X = C() >>> X.a get >>> X.a = 99 AttributeError: cannot set

__delete__ __del__

__delete__ __delattr__

A First Example name

instance

object class Name: "name descriptor docs" def __get__(self, instance, owner): print('fetch...') return instance._name

950 | Chapter 37: Managed Attributes

www.it-ebooks.info

def __set__(self, instance, value): print('change...') instance._name = value def __delete__(self, instance): print('remove...') del instance._name class Person: def __init__(self, name): self._name = name name = Name() bob = Person('Bob Smith') print(bob.name) bob.name = 'Robert Smith' print(bob.name) del bob.name print('-'*20) sue = Person('Sue Jones') print(sue.name) print(Name.__doc__)

self __get__ self instance owner

Name Person Person

fetch... Bob Smith change... fetch... Robert Smith remove... -------------------fetch... Sue Jones name descriptor docs

Person

Descriptors | 951

www.it-ebooks.info

... class Super: def __init__(self, name): self._name = name name = Name() class Person(Super): pass ...

class Person: def __init__(self, name): self._name = name class Name: "name descriptor docs" def __get__(self, instance, owner): print('fetch...') return instance._name def __set__(self, instance, value): print('change...') instance._name = value def __delete__(self, instance): print('remove...') del instance._name name = Name()

Name

... print(Person.Name.__doc__)

Computed Attributes

class DescSquare: def __init__(self, start): self.value = start def __get__(self, instance, owner):

952 | Chapter 37: Managed Attributes

Person

www.it-ebooks.info

return self.value ** 2 def __set__(self, instance, value): self.value = value class Client1: X = DescSquare(3) class Client2: X = DescSquare(32) c1 = Client1() c2 = Client2() print(c1.X) c1.X = 4 print(c1.X) print(c2.X)

9 16 1024

Using State Information in Descriptors name

class DescState: def __init__(self, value): self.value = value def __get__(self, instance, owner): print('DescState get') return self.value * 10 def __set__(self, instance, value): print('DescState set') self.value = value

Descriptors | 953

www.it-ebooks.info

class CalcAttrs: X = DescState(2) Y = 3 def __init__(self): self.Z = 4 obj = CalcAttrs() print(obj.X, obj.Y, obj.Z) obj.X = 5 obj.Y = 6 obj.Z = 7 print(obj.X, obj.Y, obj.Z)

value X Y

Z

DescState get 20 3 4 DescState set DescState get 50 6 7

_Y class InstState: def __get__(self, instance, owner): print('InstState get') return instance._Y * 100 def __set__(self, instance, value): print('InstState set') instance._Y = value

class CalcAttrs: X = DescState(2) Y = InstState() def __init__(self): self._Y = 3 self.Z = 4 obj = CalcAttrs() print(obj.X, obj.Y, obj.Z) obj.X = 5 obj.Y = 6 obj.Z = 7 print(obj.X, obj.Y, obj.Z)

954 | Chapter 37: Managed Attributes

Y

Z X

www.it-ebooks.info

X

Y

X

_Y

DescState InstState 20 300 4 DescState InstState DescState InstState 50 600 7

get get set set get get

How Properties and Descriptors Relate property property class Property: def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, instance, instancetype=None): if instance is None: return self if self.fget is None: raise AttributeError("can't get attribute") return self.fget(instance) def __set__(self, instance, value): if self.fset is None: raise AttributeError("can't set attribute") self.fset(instance, value) def __delete__(self, instance): if self.fdel is None: raise AttributeError("can't delete attribute") self.fdel(instance) class Person:

Descriptors | 955

www.it-ebooks.info

def getName(self): ... def setName(self, value): ... name = Property(getName, setName)

Property

Property

__get__

Person

Person getName

@

Property setter

deleter self

property __slots__

__getattr__ and __getattribute__ __getattr__

__getattribute__

__getattr__ __getattribute__

__setattr__

956 | Chapter 37: Managed Attributes

__delattr__

www.it-ebooks.info

self __getattr__

__getattribute__

__setattr__

__delattr__

The Basics __getattr__ __setattr__ __getattribute__

def def def def

__getattr__(self, name): __getattribute__(self, name): __setattr__(self, name, value): __delattr__(self, name):

self

name value

None class Catcher: def __getattr__(self, name): print('Get:', name) def __setattr__(self, name, value):

__getattr__ and __getattribute__ | 957

www.it-ebooks.info

print('Set:', name, value) X = Catcher() X.job X.pay X.pay = 99

class Wrapper: def __init__(self, object): self.wrapped = object def __getattr__(self, attrname): print('Trace:', attrname) return getattr(self.wrapped, attrname)

Avoiding loops in attribute interception methods __getattr__ __getattribute__

__setattr__

__getattribute__ __getattribute__ def __getattribute__(self, name): x = self.other

object def __getattribute__(self, name): x = object.__getattribute__(self, 'other')

__setattr__ __setattr__ def __setattr__(self, name, value): self.other = value

__dict__

958 | Chapter 37: Managed Attributes

www.it-ebooks.info

def __setattr__(self, name, value): self.__dict__['other'] = value

__setattr__ __getattribute__ def __setattr__(self, name, value): object.__setattr__(self, 'other', value)

__dict__ __getattribute__ def __getattribute__(self, name): x = self.__dict__['other']

__dict__

__getattribute__

__delattr__ __setattr__

A First Example

class Person: def __init__(self, name): self._name = name def __getattr__(self, attr): if attr == 'name': print('fetch...') return self._name else: raise AttributeError(attr) def __setattr__(self, attr, value): if attr == 'name': print('change...') attr = '_name' self.__dict__[attr] = value def __delattr__(self, attr): if attr == 'name': print('remove...') attr = '_name' del self.__dict__[attr]

__getattr__ and __getattribute__ | 959

www.it-ebooks.info

bob = Person('Bob Smith') print(bob.name) bob.name = 'Robert Smith' print(bob.name) del bob.name print('-'*20) sue = Person('Sue Jones') print(sue.name) #print(Person.name.__doc__)

__init__

__setattr__

fetch... Bob Smith change... fetch... Robert Smith remove... -------------------fetch... Sue Jones

__getattribute__

def __getattribute__(self, attr): if attr == 'name': print('fetch...') attr = '_name' return object.__getattribute__(self, attr)

__getattr__

__getattribute__

960 | Chapter 37: Managed Attributes

__getattr__

www.it-ebooks.info

Computed Attributes X class AttrSquare: def __init__(self, start): self.value = start def __getattr__(self, attr): if attr == 'X': return self.value ** 2 else: raise AttributeError(attr) def __setattr__(self, attr, value): if attr == 'X': attr = 'value' self.__dict__[attr] = value A = AttrSquare(3) B = AttrSquare(32) print(A.X) A.X = 4 print(A.X) print(B.X)

9 16 1024

__getattribute__ __getattribute__

__getattr__ __setattr__ __dict__ class AttrSquare: def __init__(self, start): self.value = start def __getattribute__(self, attr): if attr == 'X': return self.value ** 2 else: return object.__getattribute__(self, attr) def __setattr__(self, attr, value):

__getattr__ and __getattribute__ | 961

www.it-ebooks.info

if attr == 'X': attr = 'value' object.__setattr__(self, attr, value)

self.value=start self.value

__setattr__ __getattribute__

__getattribute__ __getattr__

__getattribute__ X value __getattribute__

value def __getattribute__(self, attr): if attr == 'X': return object.__getattribute__(self, 'value') ** 2

print

__getattr__ and __getattribute__ Compared __getattr__ attr2

attr3

class GetAttr: attr1 = 1 def __init__(self): self.attr2 = 2 def __getattr__(self, attr): print('get: ' + attr) return 3 X = GetAttr() print(X.attr1) print(X.attr2) print(X.attr3) print('-'*40) class GetAttribute(object): attr1 = 1 def __init__(self): self.attr2 = 2 def __getattribute__(self, attr): print('get: ' + attr) if attr == 'attr3': return 3

962 | Chapter 37: Managed Attributes

__getattribute__ attr1

www.it-ebooks.info

else: return object.__getattribute__(self, attr) X = GetAttribute() print(X.attr1) print(X.attr2) print(X.attr3)

__getattr__ __getattribute__

attr3

1 2 get: attr3 3 ---------------------------------------get: attr1 1 get: attr2 2 get: attr3 3

__getattribute__

__getattr__

Management Techniques Compared

square

cube

class Powers: def __init__(self, square, cube): self._square = square self._cube = cube def getSquare(self): return self._square ** 2 def setSquare(self, value): self._square = value square = property(getSquare, setSquare) def getCube(self): return self._cube ** 3 cube = property(getCube)

__getattr__ and __getattribute__ | 963

www.it-ebooks.info

X = Powers(3, 4) print(X.square) print(X.cube) X.square = 5 print(X.square)

class DescSquare: def __get__(self, instance, owner): return instance._square ** 2 def __set__(self, instance, value): instance._square = value class DescCube: def __get__(self, instance, owner): return instance._cube ** 3 class Powers: square = DescSquare() cube = DescCube() def __init__(self, square, cube): self._square = square self._cube = cube X = Powers(3, 4) print(X.square) print(X.cube) X.square = 5 print(X.square)

__getattr__ __setattrr__

class Powers: def __init__(self, square, cube): self._square = square self._cube = cube def __getattr__(self, name): if name == 'square': return self._square ** 2 elif name == 'cube': return self._cube ** 3

964 | Chapter 37: Managed Attributes

www.it-ebooks.info

else: raise TypeError('unknown attr:' + name) def __setattr__(self, name, value): if name == 'square': self.__dict__['_square'] = value else: self.__dict__[name] = value X = Powers(3, 4) print(X.square) print(X.cube) X.square = 5 print(X.square)

__getattribute__

class Powers: def __init__(self, square, cube): self._square = square self._cube = cube def __getattribute__(self, name): if name == 'square': return object.__getattribute__(self, '_square') ** 2 elif name == 'cube': return object.__getattribute__(self, '_cube') ** 3 else: return object.__getattribute__(self, name) def __setattr__(self, name, value): if name == 'square': self.__dict__['_square'] = value else: self.__dict__[name] = value X = Powers(3, 4) print(X.square) print(X.cube) X.square = 5 print(X.square)

9 64 25

__getattr__ and __getattribute__ | 965

www.it-ebooks.info

Intercepting Built-in Operation Attributes __getattr__

__getattribute__

__str__ __add__

__getitem__

+ __getattr__

__getattribute__

__getattr__ __getattribute__

__getattr__

__getattribute__

__getattr__ __getattribute__

966 | Chapter 37: Managed Attributes

www.it-ebooks.info

class GetAttr: eggs = 88 def __init__(self): self.spam = 77 def __len__(self): print('__len__: 42') return 42 def __getattr__(self, attr): print('getattr: ' + attr) if attr == '__str__': return lambda *args: '[Getattr str]' else: return lambda *args: None class GetAttribute(object): eggs = 88 def __init__(self): self.spam = 77 def __len__(self): print('__len__: 42') return 42 def __getattribute__(self, attr): print('getattribute: ' + attr) if attr == '__str__': return lambda *args: '[GetAttribute str]' else: return lambda *args: None for Class in GetAttr, GetAttribute: print('\n' + Class.__name__.ljust(50, '=')) X = Class() X.eggs X.spam X.other len(X) try:

X[0] except: print('fail []') try:

X + 99 except: print('fail +') try:

X() except: print('fail ()') X.__call__() print(X.__str__()) print(X)

__getattr__ and __getattribute__ | 967

www.it-ebooks.info

__getattr__ __getattribute__ C:\misc> c:\python26\python getattr.py GetAttr=========================================== getattr: other __len__: 42 getattr: __getitem__ getattr: __coerce__ getattr: __add__ getattr: __call__ getattr: __call__ getattr: __str__ [Getattr str] getattr: __str__ [Getattr str] GetAttribute====================================== getattribute: eggs getattribute: spam getattribute: other __len__: 42 fail [] fail + fail () getattribute: __call__ getattribute: __str__ [GetAttribute str]

__getattr__ __str__

__call__ __getattribute__

__getattribute__ object object __getattr__

C:\misc> c:\python30\python getattr.py GetAttr=========================================== getattr: other __len__: 42 fail [] fail + fail ()

968 | Chapter 37: Managed Attributes

www.it-ebooks.info

getattr: __call__ GetAttribute====================================== getattribute: eggs getattribute: spam getattribute: other __len__: 42 fail [] fail + fail () getattribute: __call__ getattribute: __str__ [GetAttribute str]

print __str__

__getattr__ object

__str__

__getattribute__

__call__ __str__ __call__

__getattr__

__len__ __getattr__ attribute__

__get

__len__

Private

__getattr__ and __getattribute__ | 969

www.it-ebooks.info

__getattribute__ __str__ __getattribute__

os.popen __getattr__ next(X) __next__ X.__next__() __str__ __call__ __getattr__

Delegation-Based Managers Revisited Manager

class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __str__(self): return '[Person: %s, %s]' % (self.name, self.pay) class Manager: def __init__(self, name, pay): self.person = Person(name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): self.person.giveRaise(percent + bonus) def __getattr__(self, attr): return getattr(self.person, attr)

970 | Chapter 37: Managed Attributes

www.it-ebooks.info

def __str__(self): return str(self.person) if __name__ == '__main__': sue = Person('Sue Jones', job='dev', pay=100000) print(sue.lastName()) sue.giveRaise(.10) print(sue) tom = Manager('Tom Jones', 50000) print(tom.lastName()) tom.giveRaise(.10) print(tom)

lastName __getattr__

Manager Person Person

giveRaise

Manager

C:\misc> c:\python30\python getattr.py Jones [Person: Sue Jones, 110000] Jones [Person: Tom Jones, 60000]

Manager __str__

Person Manager.__str__

__str__

class Manager: def __init__(self, name, pay): self.person = Person(name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): self.person.giveRaise(percent + bonus) def __getattr__(self, attr): return getattr(self.person, attr)

__getattr__ __str__ sue

Manager object Person

__str__

C:\misc> c:\python30\python person.py Jones [Person: Sue Jones, 110000] Jones

__str__

__getattr__

__str__ __getattr__ and __getattribute__ | 971

www.it-ebooks.info

C:\misc> c:\python26\python person.py Jones [Person: Sue Jones, 110000] Jones [Person: Tom Jones, 60000]

__getattribute__

__getattr__

class Manager: def __init__(self, name, pay): self.person = Person(name, 'mgr', pay) def giveRaise(self, percent, bonus=.10): self.person.giveRaise(percent + bonus) def __getattribute__(self, attr): print('**', attr) if attr in ['person', 'giveRaise']: return object.__getattribute__(self, attr) else: return getattr(self.person, attr)

__str__

Manager Person

C:\misc> c:\python30\python person.py Jones [Person: Sue Jones, 110000] ** lastName ** person Jones ** giveRaise ** person

__getattribute__ self.person self.person

__str__ __getattribute__

class Manager: def __init__(self, name, pay): self.person = Person(name, 'mgr', pay) def __getattribute__(self, attr): print('**', attr) person = object.__getattribute__(self, 'person') if attr == 'giveRaise': return lambda percent: person.giveRaise(percent+.10) else: return getattr(person, attr)

972 | Chapter 37: Managed Attributes

www.it-ebooks.info

def __str__(self): person = object.__getattribute__(self, 'person') return str(person)

__str__ Jones [Person: Sue Jones, 110000] ** lastName Jones ** giveRaise [Person: Tom Jones, 60000]

Manager __str__ __getattr__

Example: Attribute Validations CardHolder

Using Properties to Validate

Example: Attribute Validations | 973

www.it-ebooks.info

__init__ self.name

__name

setName __name

name name age

addr

acct remain

class CardHolder: acctlen = 8 retireage = 59.5 def __init__(self, acct, name, age, addr): self.acct = acct self.name = name self.age = age self.addr = addr def getName(self): return self.__name def setName(self, value): value = value.lower().replace(' ', '_') self.__name = value name = property(getName, setName) def getAge(self): return self.__age def setAge(self, value): if value < 0 or value > 150: raise ValueError('invalid age') else: self.__age = value age = property(getAge, setAge) def getAcct(self): return self.__acct[:-3] + '***' def setAcct(self, value): value = value.replace('-', '') if len(value) != self.acctlen: raise TypeError('invald acct number') else: self.__acct = value acct = property(getAcct, setAcct) def remainGet(self):

974 | Chapter 37: Managed Attributes

www.it-ebooks.info

return self.retireage - self.age remain = property(remainGet)

Self-test code

try bob = CardHolder('1234-5678', 'Bob Smith', 40, '123 main st') print(bob.acct, bob.name, bob.age, bob.remain, bob.addr, sep=' / ') bob.name = 'Bob Q. Smith' bob.age = 50 bob.acct = '23-45-67-89' print(bob.acct, bob.name, bob.age, bob.remain, bob.addr, sep=' / ') sue = CardHolder('5678-12-34', 'Sue Jones', 35, '124 main st') print(sue.acct, sue.name, sue.age, sue.remain, sue.addr, sep=' / ') try: sue.age = 200 except: print('Bad age for Sue') try:

sue.remain = 5 except: print("Can't set sue.remain") try:

sue.acct = '1234567' except: print('Bad acct for Sue')

12345*** / bob_smith / 40 / 19.5 / 123 main st 23456*** / bob_q._smith / 50 / 9.5 / 123 main st 56781*** / sue_jones / 35 / 24.5 / 124 main st Bad age for Sue Can't set sue.remain Bad acct for Sue

Using Descriptors to Validate

Example: Attribute Validations | 975

www.it-ebooks.info

__init__

__set__ self.name

Name.__set__() name name

CardHolder name name age

acct

addr remain remain

class CardHolder: acctlen = 8 retireage = 59.5 def __init__(self, acct, name, age, addr): self.acct = acct self.name = name self.age = age self.addr = addr class Name: def __get__(self, instance, owner): return self.name def __set__(self, instance, value): value = value.lower().replace(' ', '_') self.name = value name = Name() class Age: def __get__(self, instance, owner): return self.age def __set__(self, instance, value): if value < 0 or value > 150: raise ValueError('invalid age') else: self.age = value age = Age()

976 | Chapter 37: Managed Attributes

www.it-ebooks.info

class Acct: def __get__(self, instance, owner): return self.acct[:-3] + '***' def __set__(self, instance, value): value = value.replace('-', '') if len(value) != instance.acctlen: raise TypeError('invald acct number') else: self.acct = value acct = Acct() class Remain: def __get__(self, instance, owner): return instance.retireage - instance.age def __set__(self, instance, value): raise TypeError('cannot set remain') remain = Remain()

Using __getattr__ to Validate __getattr__

__getattr__

__setattr__ __init__ __setattr__

self.name

acct

__setattr__ name __getattr__ __getattr__

name acct

_acct name age

acct

addr remain

__getattr__

addr

Example: Attribute Validations | 977

www.it-ebooks.info

__getattr__ class CardHolder: acctlen = 8 retireage = 59.5 def __init__(self, acct, name, age, addr): self.acct = acct self.name = name self.age = age self.addr = addr def __getattr__(self, name): if name == 'acct': return self._acct[:-3] + '***' elif name == 'remain': return self.retireage - self.age else: raise AttributeError(name) def __setattr__(self, name, value): if name == 'name': value = value.lower().replace(' ', '_') elif name == 'age': if value < 0 or value > 150: raise ValueError('invalid age') elif name == 'acct': name = '_acct' value = value.replace('-', '') if len(value) != self.acctlen: raise TypeError('invald acct number') elif name == 'remain': raise TypeError('cannot set remain') self.__dict__[name] = value

Using __getattribute__ to Validate __getattribute__

__setattr__ __getattr__ __getattribute__ acct

978 | Chapter 37: Managed Attributes

acct

www.it-ebooks.info

addr

class CardHolder: acctlen = 8 retireage = 59.5 def __init__(self, acct, name, age, addr): self.acct = acct self.name = name self.age = age self.addr = addr def __getattribute__(self, name): superget = object.__getattribute__ if name == 'acct': return superget(self, 'acct')[:-3] + '***' elif name == 'remain': return superget(self, 'retireage') - superget(self, 'age') else: return superget(self, name) def __setattr__(self, name, value): if name == 'name': value = value.lower().replace(' ', '_') elif name == 'age': if value < 0 or value > 150: raise ValueError('invalid age') elif name == 'acct': value = value.replace('-', '') if len(value) != self.acctlen: raise TypeError('invald acct number') elif name == 'remain': raise TypeError('cannot set remain') self.__dict__[name] = value

Chapter Summary __getattr__

__getattribute__

Chapter Summary | 979

www.it-ebooks.info

Test Your Knowledge: Quiz __getattr__

__getattribute__

__getattr__

__getattri

bute__

Test Your Knowledge: Answers __getattr__ __getattribute__ __getattr__ __getattribute__

property

name = property(name) deleter

980 | Chapter 37: Managed Attributes

setter

www.it-ebooks.info

__getattr__

__getattribute__

__getattr__ __getattribute__ __setattr__ __getattr__ __getattribute__

An argument is a connected series of statements intended to establish a proposition. No it isn't. Yes it is! It's not just contradiction. Look, if I argue with you, I must take up a contrary position. Yes, but that's not just saying "No it isn't." Yes it is! No it isn't! Yes it is! No it isn't. Argument is an intellectual process. Contradiction is just the automatic gainsaying of any statement the other person makes. (short pause) No it isn't. It is. Not at all. Now look...

Test Your Knowledge: Quiz | 981

www.it-ebooks.info

www.it-ebooks.info

CHAPTER 38

Decorators

@ property

What’s a Decorator?

983

www.it-ebooks.info

def class

Managing Calls and Instances

def

Managing Functions and Classes

Using and Defining Decorators

984 | Chapter 38: Decorators

class

www.it-ebooks.info

Why Decorators?

What’s a Decorator? | 985

www.it-ebooks.info

The Basics

Function Decorators def

Usage def @

@decorator def F(arg): ... F(99)

decorator F def F(arg): ... F = decorator(F) F(99)

986 | Chapter 38: Decorators

www.it-ebooks.info

def F

func(6, 7) decorator(func)(6, 7)

class C: @staticmethod def meth(...): ... class C: @property def name(self): ...

def

Implementation

def decorator(F): return F @decorator def func(): ...

The Basics | 987

www.it-ebooks.info

def decorator(F):

@decorator def func(): ...

def decorator(F): def wrapper(*args): return wrapper @decorator def func(x, y): ... func(6, 7)

decorator

func wrapper

wrapper func

class decorator: def __init__(self, func): self.func = func def __call__(self, *args):

@decorator def func(x, y): ... func(6, 7)

func decorator

988 | Chapter 38: Decorators

__call__ __call__

www.it-ebooks.info

func

Supporting method decoration

class decorator: def __init__(self, func): self.func = func def __call__(self, *args): class C: @decorator def method(self, x, y):

self

__call__

decorator

C *args

def decorator(F): def wrapper(*args): return wrapper @decorator def func(x, y): ... func(6, 7) class C: @decorator def method(self, x, y): ... X = C() X.method(6, 7)

wrapper

C

self

The Basics | 989

www.it-ebooks.info

self

Class Decorators

Usage class decorator @decorator class C: ... x = C(99)

class C: ... C = decorator(C) x = C(99)

Implementation

990 | Chapter 38: Decorators

www.it-ebooks.info

def decorator(C): return C @decorator class C: ...

def decorator(C):

@decorator class C: ...

def decorator(cls): class Wrapper: def __init__(self, *args): self.wrapped = cls(*args) def __getattr__(self, name): return getattr(self.wrapped, name) return Wrapper @decorator class C: def __init__(self, x, y): self.attr = 'spam' x = C(6, 7) print(x.attr)

__getattr__

The Basics | 991

www.it-ebooks.info

__init__

__call__

Supporting multiple instances

class Decorator: def __init__(self, C): self.C = C def __call__(self, *args): self.wrapped = self.C(*args) return self def __getattr__(self, attrname): return getattr(self.wrapped, attrname) @Decorator class C: ... x = C() y = C()

Decorator __call__

def decorator(C): class Wrapper: def __init__(self, *args): self.wrapped = C(*args) return Wrapper class Wrapper: ... def decorator(C): def onCall(*args): return Wrapper(C(*args)) return onCall

992 | Chapter 38: Decorators

www.it-ebooks.info

Decorator Nesting

@A @B @C def f(...): ...

def f(...): ... f = A(B(C(f)))

@spam @eggs class C: ... X = C()

class C: ... C = spam(eggs(C)) X = C()

C spam

eggs

The Basics | 993

www.it-ebooks.info

def d1(F): return F def d2(F): return F def d3(F): return F @d1 @d2 @d3 def func(): print('spam') func()

def d1(F): return lambda: 'X' + F() def d2(F): return lambda: 'Y' + F() def d3(F): return lambda: 'Z' + F() @d1 @d2 @d3 def func(): return 'spam' print(func())

lambda

Decorator Arguments

@decorator(A, B) def F(arg): ... F(99)

decorator

994 | Chapter 38: Decorators

www.it-ebooks.info

def F(arg): ... F = decorator(A, B)(F) F(99)

def decorator(A, B): def actualDecorator(F): return callable return actualDecorator

Decorators Manage Functions and Classes, Too

def decorate(O): return O @decorator def F(): ... @decorator class C: ...

The Basics | 995

www.it-ebooks.info

Coding Function Decorators

Tracing Calls

class tracer: def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) self.func(*args) @tracer def spam(a, b, c): print(a + b + c)

*args

>>> from decorator1 import spam >>> spam(1, 2, 3) call 1 to spam 6 >>> spam('a', 'b', 'c') call 2 to spam abc >>> spam.calls 2

996 | Chapter 38: Decorators

www.it-ebooks.info

>>> spam

tracer spam tracer @

calls = 0 def tracer(func, *args): global calls calls += 1 print('call %s to %s' % (calls, func.__name__)) func(*args) def spam(a, b, c): print(a, b, c) >>> spam(1, 2, 3) 1 2 3 >>> tracer(spam, 1, 2, 3) call 1 to spam 1 2 3

@

State Information Retention Options

Coding Function Decorators | 997

www.it-ebooks.info

Class instance attributes

class tracer: def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args, **kwargs): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) return self.func(*args, **kwargs) @tracer def spam(a, b, c): print(a + b + c) @tracer def eggs(x, y): print(x ** y) spam(1, 2, 3) spam(a=4, b=5, c=6) eggs(2, 16) eggs(4, y=4)

spam call 1 6 call 2 15 call 1 65536 call 2 256

eggs

to spam to spam to eggs to eggs

Enclosing scopes and globals def

998 | Chapter 38: Decorators

def

www.it-ebooks.info

calls = 0 def tracer(func): def wrapper(*args, **kwargs): global calls calls += 1 print('call %s to %s' % (calls, func.__name__)) return func(*args, **kwargs) return wrapper @tracer def spam(a, b, c): print(a + b + c) @tracer def eggs(x, y): print(x ** y) spam(1, 2, 3) spam(a=4, b=5, c=6) eggs(2, 16) eggs(4, y=4)

call 1 6 call 2 15 call 3 65536 call 4 256

to spam to spam to eggs to eggs

Enclosing scopes and nonlocals

nonlocal

def tracer(func): calls = 0 def wrapper(*args, **kwargs):

Coding Function Decorators | 999

www.it-ebooks.info

nonlocal calls calls += 1 print('call %s to %s' % (calls, func.__name__)) return func(*args, **kwargs) return wrapper @tracer def spam(a, b, c): print(a + b + c) @tracer def eggs(x, y): print(x ** y) spam(1, 2, 3) spam(a=4, b=5, c=6) eggs(2, 16) eggs(4, y=4)

call 1 6 call 2 15 call 1 65536 call 2 256

to spam to spam to eggs to eggs

Function attributes nonlocal

func.attr=value wrapper.calls

nonlocal

def tracer(func): def wrapper(*args, **kwargs): wrapper.calls += 1 print('call %s to %s' % (wrapper.calls, func.__name__)) return func(*args, **kwargs) wrapper.calls = 0 return wrapper

wrapper wrapper.calls

tracer wrapper

1000 | Chapter 38: Decorators

nonlocal

www.it-ebooks.info

nonlocal

Class Blunders I: Decorating Class Methods tracer self tracer

*args self *args

class tracer: def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args, **kwargs): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) return self.func(*args, **kwargs)

@tracer def spam(a, b, c): print(a + b + c) spam(1, 2, 3) spam(a=4, b=5, c=6)

Coding Function Decorators | 1001

www.it-ebooks.info

Person class Person: def __init__(self, name, pay): self.name = name self.pay = pay @tracer def giveRaise(self, percent): self.pay *= (1.0 + percent) @tracer def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) bob.giveRaise(.25) print(bob.lastName())

self Person Person

tracer tracer self

tracer

__call__ Person

__call__

tracer

tracer

self tracer

Person

self

tracer

__call__ Person

(0.25,) {} call 1 to giveRaise Traceback (most recent call last): File "C:/misc/tracer.py", line 56, in bob.giveRaise(.25) File "C:/misc/tracer.py", line 9, in __call__ return self.func(*args, **kwargs) TypeError: giveRaise() takes exactly 2 positional arguments (1 given)

self

1002 | Chapter 38: Decorators

www.it-ebooks.info

Using nested functions to decorate methods

def self

Person *args

self

def tracer(func): calls = 0 def onCall(*args, **kwargs): nonlocal calls calls += 1 print('call %s to %s' % (calls, func.__name__)) return func(*args, **kwargs) return onCall

@tracer def spam(a, b, c): print(a + b + c) spam(1, 2, 3) spam(a=4, b=5, c=6)

class Person: def __init__(self, name, pay): self.name = name self.pay = pay @tracer def giveRaise(self, percent): self.pay *= (1.0 + percent) @tracer def lastName(self): return self.name.split()[-1] print('methods...') bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) print(bob.name, sue.name)

Coding Function Decorators | 1003

www.it-ebooks.info

sue.giveRaise(.10) print(sue.pay) print(bob.lastName(), sue.lastName())

call 1 to spam 6 call 2 to spam 15 methods... Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

Using descriptors to decorate methods

__get__ object class Descriptor(object): def __get__(self, instance, owner): ... class Subject: attr = Descriptor() X = Subject() X.attr

__set__

__del__ __get__

class tracer(object): def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args, **kwargs): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) return self.func(*args, **kwargs) def __get__(self, instance, owner): return wrapper(self, instance)

1004 | Chapter 38: Decorators

www.it-ebooks.info

class wrapper: def __init__(self, desc, subj): self.desc = desc self.subj = subj def __call__(self, *args, **kwargs): return self.desc(self.subj, *args, **kwargs) @tracer def spam(a, b, c): ...same as prior... class Person: @tracer def giveRaise(self, percent): ...same as prior...

__call__

__get__ instance.method

__call__

__get__

(args...)

sue.giveRaise(.10)

tracer.__get__

giveRaise

__call__ tracer.__call__

Person

wrapper

wrapper wrapper __call__ calls

class tracer(object): def __init__(self, func): self.calls = 0 self.func = func def __call__(self, *args, **kwargs): self.calls += 1 print('call %s to %s' % (self.calls, self.func.__name__)) return self.func(*args, **kwargs) def __get__(self, instance, owner): def wrapper(*args, **kwargs):

Coding Function Decorators | 1005

www.it-ebooks.info

return self(instance, *args, **kwargs) return wrapper

print

staticmethod

Timing Calls

map import time class timer: def __init__(self, func): self.func = func self.alltime = 0 def __call__(self, *args, **kargs): start = time.clock() result = self.func(*args, **kargs) elapsed = time.clock() - start self.alltime += elapsed print('%s: %.5f, %.5f' % (self.func.__name__, elapsed, self.alltime)) return result @timer def listcomp(N): return [x * 2 for x in range(N)] @timer def mapcall(N): return map((lambda x: x * 2), range(N))

1006 | Chapter 38: Decorators

www.it-ebooks.info

result = listcomp(5) listcomp(50000) listcomp(500000) listcomp(1000000) print(result) print('allTime = %s' % listcomp.alltime) print('') result = mapcall(5) mapcall(50000) mapcall(500000) mapcall(1000000) print(result) print('allTime = %s' % mapcall.alltime) print('map/comp = %s' % round(mapcall.alltime / listcomp.alltime, 3))

def

listcomp: listcomp: listcomp: listcomp: [0, 2, 4, allTime =

0.00002, 0.00002 0.00910, 0.00912 0.09105, 0.10017 0.17605, 0.27622 6, 8] 0.276223304917

mapcall: 0.00003, 0.00003 mapcall: 0.01363, 0.01366 mapcall: 0.13579, 0.14945 mapcall: 0.27648, 0.42593 [0, 2, 4, 6, 8] allTime = 0.425933533452 map/comp = 1.542

map map

map list(map()) map

map

Coding Function Decorators | 1007

www.it-ebooks.info

range ... import sys @timer def listcomp(N): return [x * 2 for x in range(N)] if sys.version_info[0] == 2: @timer def mapcall(N): return map((lambda x: x * 2), range(N)) else: @timer def mapcall(N): return list(map((lambda x: x * 2), range(N))) ...

__name__ == '__main__'

Adding Decorator Arguments

def timer(label=''): def decorator(func): def onCall(*args): ... print(label, ... return onCall return decorator @timer('==>') def listcomp(N): ... listcomp(...)

listcomp timer timer 1008 | Chapter 38: Decorators

decorator label

www.it-ebooks.info

import time def timer(label='', trace=True): class Timer: def __init__(self, func): self.func = func self.alltime = 0 def __call__(self, *args, **kargs): start = time.clock() result = self.func(*args, **kargs) elapsed = time.clock() - start self.alltime += elapsed if trace: format = '%s %s: %.5f, %.5f' values = (label, self.func.__name__, elapsed, self.alltime) print(format % values) return result return Timer

Timer timer Timer Timer

from mytools import timer @timer(label='[CCC]==>') def listcomp(N): return [x * 2 for x in range(N)] @timer(trace=True, label='[MMM]==>') def mapcall(N): return map((lambda x: x * 2), range(N)) for func in (listcomp, mapcall): print('') result = func(5) func(50000) func(500000) func(1000000) print(result)

Coding Function Decorators | 1009

www.it-ebooks.info

print('allTime = %s' % func.alltime) print('map/comp = %s' % round(mapcall.alltime / listcomp.alltime, 3))

map

[CCC]==> listcomp: 0.00003, [CCC]==> listcomp: 0.00640, [CCC]==> listcomp: 0.08687, [CCC]==> listcomp: 0.17911, [0, 2, 4, 6, 8] allTime = 0.272407666337 [MMM]==> mapcall: 0.00004, [MMM]==> mapcall: 0.01340, [MMM]==> mapcall: 0.13907, [MMM]==> mapcall: 0.27907, [0, 2, 4, 6, 8] allTime = 0.431572169089 map/comp = 1.584

0.00003 0.00643 0.09330 0.27241

0.00004 0.01343 0.15250 0.43157

>>> from mytools import timer >>> @timer(trace=False) ... def listcomp(N): ... return [x * 2 for x in range(N)] ... >>> x = listcomp(5000) >>> x = listcomp(5000) >>> x = listcomp(5000) >>> listcomp >>> listcomp.alltime 0.0051938863738243413 >>> @timer(trace=True, label='\t=>') ... def listcomp(N): ... return [x * 2 for x in range(N)] ... >>> x = listcomp(5000) => listcomp: 0.00155, 0.00155 >>> x = listcomp(5000) => listcomp: 0.00156, 0.00311 >>> x = listcomp(5000) => listcomp: 0.00174, 0.00486 >>> listcomp.alltime 0.0048562736325408196

1010 | Chapter 38: Decorators

list

www.it-ebooks.info

Coding Class Decorators

Singleton Classes

singleton @ instances = {} def getInstance(aClass, *args): if aClass not in instances: instances[aClass] = aClass(*args) return instances[aClass] def singleton(aClass): def onCall(*args): return getInstance(aClass, *args) return onCall

@singleton class Person: def __init__(self, name, hours, rate): self.name = name self.hours = hours

Coding Class Decorators | 1011

www.it-ebooks.info

self.rate = rate def pay(self): return self.hours * self.rate @singleton class Spam: def __init__(self, val): self.attr = val bob = Person('Bob', 40, 10) print(bob.name, bob.pay()) sue = Person('Sue', 50, 20) print(sue.name, sue.pay()) X = Spam(42) Y = Spam(99) print(X.attr, Y.attr)

Person

Spam onCall

getInstance Bob 400 Bob 400 42 42

nonlocal

def singleton(aClass): instance = None def onCall(*args): nonlocal instance if instance == None: instance = aClass(*args) return instance return onCall

class singleton: def __init__(self, aClass): self.aClass = aClass self.instance = None def __call__(self, *args):

1012 | Chapter 38: Decorators

www.it-ebooks.info

if self.instance == None: self.instance = self.aClass(*args) return self.instance

__name__ **kargs

Tracing Object Interfaces

__getattr__

__getattr__

class Wrapper: def __init__(self, object): self.wrapped = object def __getattr__(self, attrname): print('Trace:', attrname) return getattr(self.wrapped, attrname) >>> x = Wrapper([1,2,3]) >>> x.append(4) Trace: append >>> x.wrapped [1, 2, 3, 4] >>> x = Wrapper({"a": 1, "b": 2}) >>> list(x.keys()) Trace: keys ['a', 'b']

Wrapper getattr

Coding Class Decorators | 1013

www.it-ebooks.info

__getattr__

**kargs def Tracer(aClass): class Wrapper: def __init__(self, *args, **kargs): self.fetches = 0 self.wrapped = aClass(*args, **kargs) def __getattr__(self, attrname): print('Trace: ' + attrname) self.fetches += 1 return getattr(self.wrapped, attrname) return Wrapper @Tracer class Spam: def display(self): print('Spam!' * 8) @Tracer class Person: def __init__(self, name, hours, rate): self.name = name self.hours = hours self.rate = rate def pay(self): return self.hours * self.rate food = Spam() food.display() print([food.fetches]) bob = Person('Bob', 40, 50) print(bob.name) print(bob.pay()) print('') sue = Person('Sue', rate=100, hours=60) print(sue.name) print(sue.pay()) print(bob.name) print(bob.pay()) print([bob.fetches, sue.fetches])

1014 | Chapter 38: Decorators

www.it-ebooks.info

Spam Person food bob

Wrapper

__getattr__ Wrapper

Trace: display Spam!Spam!Spam!Spam!Spam!Spam!Spam!Spam! [1] Trace: name Bob Trace: pay 2000 Trace: Sue Trace: 6000 Trace: Bob Trace: 2000 [4, 2]

name pay name pay

class x

@

Wrapper

>>> from tracer import Tracer >>> @Tracer ... class MyList(list): pass >>> x = MyList([1, 2, 3]) >>> x.append(4) Trace: append >>> x.wrapped [1, 2, 3, 4] >>> WrapList = Tracer(list) >>> x = WrapList([4, 5, 6]) >>> x.append(7) Trace: append >>> x.wrapped [4, 5, 6, 7]

Coding Class Decorators | 1015

www.it-ebooks.info

@Tracer class Person: ... bob = Person('Bob', 40, 50) sue = Person('Sue', rate=100, hours=60) class Person: ... bob = Wrapper(Person('Bob', 40, 50)) sue = Wrapper(Person('Sue', rate=100, hours=60))

__getattr__ __str__ __repr__ object + __getattribute__

__getattr__

__getattr__

__repr__ Wrapper >>> x Trace: __repr__ [4, 5, 6, 7] >>> x

Private

Class Blunders II: Retaining Multiple Instances __init__ __call__ 1016 | Chapter 38: Decorators

@

www.it-ebooks.info

Tracer class Tracer: def __init__(self, aClass): self.aClass = aClass def __call__(self, *args): self.wrapped = self.aClass(*args) return self def __getattr__(self, attrname): print('Trace: ' + attrname) return getattr(self.wrapped, attrname) @Tracer class Spam: def display(self): print('Spam!' * 8) ... food = Spam() food.display()

__call__ Tracer @Tracer class Person: def __init__(self, name): self.name = name bob = Person('Bob') print(bob.name) Sue = Person('Sue') print(sue.name) print(bob.name)

Trace: name Bob Trace: name Sue Trace: name Sue

Coding Class Decorators | 1017

www.it-ebooks.info

Tracer Wrapper Tracer

Decorators Versus Manager Functions Tracer __getattr__

class Spam: ... food = Wrapper(Spam()) @Tracer class Spam: ... food = Spam()

instances = {} def getInstance(aClass, *args): if aClass not in instances: instances[aClass] = aClass(*args) return instances[aClass] bob = getInstance(Person, 'Bob', 40, 10)

instances = {} def getInstance(object): aClass = object.__class__ if aClass not in instances: instances[aClass] = object return instances[aClass] bob = getInstance(Person('Bob', 40, 10))

1018 | Chapter 38: Decorators

www.it-ebooks.info

def func(x, y): ... result = tracer(func, (1, 2)) @tracer def func(x, y): ... result = func(1, 2)

Why Decorators? (Revisited)

Coding Class Decorators | 1019

www.it-ebooks.info

@

__init__ X=Class().init() __init__

1020 | Chapter 38: Decorators

www.it-ebooks.info

Managing Functions and Classes Directly

registry = {} def register(obj): registry[obj.__name__] = obj return obj @register def spam(x): return(x ** 2) @register def ham(x): return(x ** 3) @register class Eggs: def __init__(self, x): self.data = x ** 4 def __str__(self): return str(self.data) print('Registry:') for name in registry: print(name, '=>', registry[name], type(registry[name])) print('\nManual calls:') print(spam(2)) print(ham(2)) X = Eggs(2) print(X) print('\nRegistry calls:') for name in registry: print(name, '=>', registry[name](3))

Managing Functions and Classes Directly | 1021

www.it-ebooks.info

Registry: Eggs => ham => spam => Manual calls: 4 8 16 Registry calls: Eggs => 81 ham => 27 spam => 9

def

>>> def decorate(func): ... func.marked = True ... return func ... >>> @decorate ... def spam(a, b): ... return a + b ... >>> spam.marked True >>> def annotate(text): ... def decorate(func): ... func.label = text ... return func ... return decorate ... >>> @annotate('spam data') ... def spam(a, b): ... return a + b ...

1022 | Chapter 38: Decorators

www.it-ebooks.info

>>> spam(1, 2), spam.label (3, 'spam data')

Example: “Private” and “Public” Attributes

Implementing Private Attributes Private

__getattr__

__setattr__ raise

try print

raise

""" Privacy for attributes fetched from class instances. See self-test code at end of file for a usage example. Decorator same as: Doubler = Private('data', 'size')(Doubler). Private returns onDecorator, onDecorator returns onInstance, and each onInstance instance embeds a Doubler instance. """

Example: “Private” and “Public” Attributes | 1023

www.it-ebooks.info

traceMe = False def trace(*args): if traceMe: print('[' + ' '.join(map(str, args)) + ']') def Private(*privates): def onDecorator(aClass): class onInstance: def __init__(self, *args, **kargs): self.wrapped = aClass(*args, **kargs) def __getattr__(self, attr): trace('get:', attr) if attr in privates: raise TypeError('private attribute fetch: ' + attr) else: return getattr(self.wrapped, attr) def __setattr__(self, attr, value): trace('set:', attr, value) if attr == 'wrapped': self.__dict__[attr] = value elif attr in privates: raise TypeError('private attribute change: ' + attr) else: setattr(self.wrapped, attr, value) return onInstance return onDecorator if __name__ == '__main__': traceMe = True @Private('data', 'size') class Doubler: def __init__(self, label, start): self.label = label self.data = start def size(self): return len(self.data) def double(self): for i in range(self.size()): self.data[i] = self.data[i] * 2 def display(self): print('%s => %s' % (self.label, self.data)) X = Doubler('X is', [1, 2, 3]) Y = Doubler('Y is', [−10, −20, −30]) # The followng all succeed print(X.label) X.display(); X.double(); X.display() print(Y.label) Y.display(); Y.double() Y.label = 'Spam' Y.display()

1024 | Chapter 38: Decorators

www.it-ebooks.info

""" print(X.size()) print(X.data) X.data = [1, 1, 1] X.size = lambda S: 0 print(Y.data) print(Y.size()) """

traceMe

True

[set: wrapped ] [set: wrapped ] [get: label] X is [get: display] X is => [1, 2, 3] [get: double] [get: display] X is => [2, 4, 6] [get: label] Y is [get: display] Y is => [−10, −20, −30] [get: double] [set: label Spam] [get: display] Spam => [−20, −40, −60]

Implementation Details I

Inheritance versus delegation __setattr__

__dict__

Example: “Private” and “Public” Attributes | 1025

www.it-ebooks.info

self

Decorator arguments. Private

Private Private

State retention and enclosing scopes

Private onDecorator

onInstance

onDecorator onInstance

Using __dict__ and __slots__ __setattr__

__dict__ onInstance

setattr getattr

__dict__

__slots__ __dict__ setattr __dict__

wrapped

__dict__ onInstance getattr

__slots__

Generalizing for Public Declarations, Too Private Public

1026 | Chapter 38: Decorators

Private

www.it-ebooks.info

Private

Public

Private Private Private Public Public Private Private

Public Public Public

Public

Private Private Public

lambda

""" Class decorator with Private and Public attribute declarations. Controls access to attributes stored on an instance, or inherited by it from its classes. Private declares attribute names that cannot be fetched or assigned outside the decorated class, and Public declares all the names that can. Caveat: this works in 3.0 for normally named attributes only: __X__ operator overloading methods implicitly run for built-in operations do not trigger either __getattr__ or __getattribute__ in new-style classes. Add __X__ methods here to intercept and delegate built-ins. """ traceMe = False def trace(*args): if traceMe: print('[' + ' '.join(map(str, args)) + ']') def accessControl(failIf): def onDecorator(aClass): class onInstance: def __init__(self, *args, **kargs): self.__wrapped = aClass(*args, **kargs) def __getattr__(self, attr): trace('get:', attr)

Example: “Private” and “Public” Attributes | 1027

www.it-ebooks.info

if failIf(attr): raise TypeError('private attribute fetch: ' + attr) else: return getattr(self.__wrapped, attr) def __setattr__(self, attr, value): trace('set:', attr, value) if attr == '_onInstance__wrapped': self.__dict__[attr] = value elif failIf(attr): raise TypeError('private attribute change: ' + attr) else: setattr(self.__wrapped, attr, value) return onInstance return onDecorator def Private(*attributes): return accessControl(failIf=(lambda attr: attr in attributes)) def Public(*attributes): return accessControl(failIf=(lambda attr: attr not in attributes))

Private Public Private Public >>> from access import Private, Public >>> @Private('age') ... class Person: ... def __init__(self, name, age): ... self.name = name ... self.age = age ... >>> X = Person('Bob', 40) >>> X.name 'Bob' >>> X.name = 'Sue' >>> X.name 'Sue' >>> X.age TypeError: private attribute fetch: age >>> X.age = 'Tom' TypeError: private attribute change: age >>> @Public('name') ... class Person: ... def __init__(self, name, age): ... self.name = name ... self.age = age ... >>> X = Person('bob', 40) >>> X.name 'bob' >>> X.name = 'Sue'

1028 | Chapter 38: Decorators

www.it-ebooks.info

>>> X.name 'Sue' >>> X.age TypeError: private attribute fetch: age >>> X.age = 'Tom' TypeError: private attribute change: age

Implementation Details II

Using __X pseudoprivate names __X wrapped wrapped

'_onInstance__wrapped'

__setattr__

Breaking privacy

wrapped bob._onInstance__wrapped.pay

bob.pay

#define private public

Decorator tradeoffs

X.__class__

isinstance(X, C)

Example: “Private” and “Public” Attributes | 1029

www.it-ebooks.info

Open Issues

Caveat: operator overloading methods fail to delegate under 3.0 __getattr__ __str__

__add__

__X__ __getattr__ __getattribute__ onInstance.__getattr__ object

onInstance

__str__ __add__ __getattr__ C:\misc> c:\python26\python >>> from access import Private >>> @Private('age') ... class Person: ... def __init__(self): ... self.age = 42 ... def __str__(self): ... return 'Person: ' + str(self.age) ... def __add__(self, yrs): ... self.age += yrs ...

1030 | Chapter 38: Decorators

+ Person

www.it-ebooks.info

>>> X = Person() >>> X.age TypeError: private attribute fetch: age >>> print(X) Person: 42 >>> X + 10 >>> print(X) Person: 52

__str__ __add__

__getattr__ print object

+

C:\misc> c:\python30\python >>> from access import Private >>> @Private('age') ... class Person: ... def __init__(self): ... self.age = 42 ... def __str__(self): ... return 'Person: ' + str(self.age) ... def __add__(self, yrs): ... self.age += yrs ... >>> X = Person() >>> X.age TypeError: private attribute fetch: age >>> print(X) >>> X + 10 TypeError: unsupported operand type(s) for +: 'onInstance' and 'int' >>> print(X)

__getattribute__ property

Private

Public

failIf

Example: “Private” and “Public” Attributes | 1031

www.it-ebooks.info

def accessControl(failIf): def onDecorator(aClass): class onInstance: def __init__(self, *args, **kargs): self.__wrapped = aClass(*args, **kargs) def __str__(self): return str(self.__wrapped) def __add__(self, other): return self.__wrapped + other def __getitem__(self, index): return self.__wrapped[index] def __call__(self, *args, **kargs): return self.__wrapped(*arg, *kargs) ...plus any others needed... def __getattr__(self, attr): ... def __setattr__(self, attr, value): ... return onInstance return onDecorator

__str__ __add__

Implementation alternatives: __getattribute__ inserts, call stack inspection

1032 | Chapter 38: Decorators

www.it-ebooks.info

__getattribute__

def accessControl(failIf): def onDecorator(aClass): def getattributes(self, attr): trace('get:', attr) if failIf(attr): raise TypeError('private attribute fetch: ' + attr) else: return object.__getattribute__(self, attr) aClass.__getattribute__ = getattributes return aClass return onDecorator def Private(*attributes): return accessControl(failIf=(lambda attr: attr in attributes)) def Public(*attributes): return accessControl(failIf=(lambda attr: attr not in attributes))

__setattr__

__setattr__ __setattr__

Person

__getattribute__ __slots__

__getattribute__ __str__ __str__ __getattribute__ __str__

__X__

Private

__getattribute__

Example: “Private” and “Public” Attributes | 1033

www.it-ebooks.info

Private

Python Isn’t About Control Private

Example: Validating Function Arguments

The Goal class Person: ...

1034 | Chapter 38: Decorators

Public

www.it-ebooks.info

def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent))

if

assert

class Person: def giveRaise(self, percent): if percent < 0.0 or percent > 1.0: raise TypeError, 'percent invalid' self.pay = int(self.pay * (1 + percent)) class Person: def giveRaise(self, percent): assert percent >= 0.0 and percent high: errmsg = 'Argument %s not in %s..%s' % (ix, low, high) raise TypeError(errmsg) return func(*args) return onCall return onDecorator

def onCall *args

True main.py

__debug__

__debug__ –O False

from devtools import rangetest print(__debug__) @rangetest((1, 0, 120)) def persinfo(name, age): print('%s is %s years old' % (name, age)) @rangetest([0, 1, 12], [1, 1, 31], [2, 0, 2009]) def birthday(M, D, Y): print('birthday = {0}/{1}/{2}'.format(M, D, Y)) class Person: def __init__(self, name, job, pay): self.job = job self.pay = pay @rangetest([1, 0.0, 1.0]) def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent))

persinfo('Bob Smith', 45) birthday(5, 31, 1963)

1036 | Chapter 38: Decorators

self

python –O

www.it-ebooks.info

sue = Person('Sue Jones', 'dev', 100000) sue.giveRaise(.10) print(sue.pay)

print C:\misc> C:\python30\python devtools_test.py True Bob Smith is 45 years old birthday = 5/31/1963 110000

TypeError

C:\misc> C:\python30\python devtools_test.py True Bob Smith is 45 years old birthday = 5/31/1963 110000 TypeError: Argument 1 not in 0.0..1.0

–O

C:\misc> C:\python30\python –O devtools_test.py False Bob Smith is 45 years old birthday = 5/31/1963 110000 231000

Generalizing for Keywords and Defaults, Too

Example: Validating Function Arguments | 1037

www.it-ebooks.info

*pargs

**kargs

""" File devtools.py: function decorator that performs range-test validation for passed arguments. Arguments are specified by keyword to the decorator. In the actual call, arguments may be passed by position or keyword, and defaults may be omitted. See devtools_test.py for example use cases. """ trace = True def rangetest(**argchecks): def onDecorator(func): if not __debug__: return func else: import sys code = func.__code__ allargs = code.co_varnames[:code.co_argcount] funcname = func.__name__ def onCall(*pargs, **kargs): positionals = list(allargs) positionals = positionals[:len(pargs)] for (argname, (low, high)) in argchecks.items(): if argname in kargs: if kargs[argname] < low or kargs[argname] > high: errmsg = '{0} argument "{1}" not in {2}..{3}' errmsg = errmsg.format(funcname, argname, low, high) raise TypeError(errmsg) elif argname in positionals: position = positionals.index(argname) if pargs[position] < low or pargs[position] > high: errmsg = '{0} argument "{1}" not in {2}..{3}' errmsg = errmsg.format(funcname, argname, low, high) raise TypeError(errmsg) else: if trace: print('Argument "{0}" defaulted'.format(argname))

1038 | Chapter 38: Decorators

www.it-ebooks.info

return func(*pargs, **kargs) return onCall return onDecorator

from devtools import rangetest

@rangetest(age=(0, 120)) def persinfo(name, age): print('%s is %s years old' % (name, age)) @rangetest(M=(1, 12), D=(1, 31), Y=(0, 2009)) def birthday(M, D, Y): print('birthday = {0}/{1}/{2}'.format(M, D, Y)) persinfo('Bob', 40) persinfo(age=40, name='Bob') birthday(5, D=1, Y=1963)

class Person: def __init__(self, name, job, pay): self.job = job self.pay = pay @rangetest(percent=(0.0, 1.0)) def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) bob = Person('Bob Smith', 'dev', 100000) sue = Person('Sue Jones', 'dev', 100000) bob.giveRaise(.10) sue.giveRaise(percent=.20) print(bob.pay, sue.pay)

@rangetest(a=(1, 10), b=(1, 10), c=(1, 10), d=(1, 10)) def omitargs(a, b=7, c=8, d=9):

Example: Validating Function Arguments | 1039

www.it-ebooks.info

print(a, b, c, d) omitargs(1, 2, 3, 4) omitargs(1, 2, 3) omitargs(1, 2, 3, d=4) omitargs(1, d=4) omitargs(d=4, a=1) omitargs(1, b=2, d=4) omitargs(d=8, c=7, a=1)

C:\misc> C:\python30\python devtools_test.py Bob is 40 years old Bob is 40 years old birthday = 5/1/1963 110000 120000 1 2 3 4 Argument "d" defaulted 1 2 3 9 1 2 3 4 Argument "c" defaulted Argument "b" defaulted 1 7 8 4 Argument "c" defaulted Argument "b" defaulted 1 7 8 4 Argument "c" defaulted 1 2 8 4 Argument "b" defaulted 1 7 7 8

–O TypeError: giveRaise argument "percent" not in 0.0..1.0

Implementation Details

1040 | Chapter 38: Decorators

www.it-ebooks.info

Function introspection

>>> def func(a, b, c, d): ... x = 1 ... y = 2 ... >>> code = func.__code__ >>> code.co_nlocals 6 >>> code.co_varnames ('a', 'b', 'c', 'd', 'x', 'y') >>> code.co_varnames[:code.co_argcount] ('a', 'b', 'c', 'd') >>> >>> (3, >>>

import sys sys.version_info 0, 0, 'final', 0) code = func.__code__ if sys.version_info[0] == 3 else func.func_code

func.__code__ __code__

func.func_code dir

Argument assumptions

def

Example: Validating Function Arguments | 1041

www.it-ebooks.info

Matching algorithm

*pargs

*pargs

**kargs *pargs

*pargs **kargs

Open Issues

omitargs() omitargs(d=8, c=7, b=6)

1042 | Chapter 38: Decorators

www.it-ebooks.info

*args

**args

**kargs **kargs

**kargs

*args *pargs

Decorator Arguments Versus Function Annotations

def

@rangetest(a=(1, 5), c=(0.0, 1.0)) def func(a, b, c): print(a + b + c)

Example: Validating Function Arguments | 1043

www.it-ebooks.info

@rangetest def func(a:(1, 5), b, c:(0.0, 1.0)): print(a + b + c)

def rangetest(**argchecks): def onDecorator(func): def onCall(*pargs, **kargs): print(argchecks) for check in argchecks: pass return func(*pargs, **kargs) return onCall return onDecorator @rangetest(a=(1, 5), c=(0.0, 1.0)) def func(a, b, c): print(a + b + c) func(1, 2, c=3)

def rangetest(func): def onCall(*pargs, **kargs): argchecks = func.__annotations__ print(argchecks) for check in argchecks: pass return func(*pargs, **kargs) return onCall @rangetest def func(a:(1, 5), b, c:(0.0, 1.0)): print(a + b + c) func(1, 2, c=3)

{'a': (1, 5), 'c': (0.0, 1.0)} 6 {'a': (1, 5), 'c': (0.0, 1.0)} 6

1044 | Chapter 38: Decorators

www.it-ebooks.info

def

Other Applications: Type Testing (If You Insist!)

def typetest(**argchecks): def onDecorator(func): .... def onCall(*pargs, **kargs): positionals = list(allargs)[:len(pargs)] for (argname, type) in argchecks.items(): if argname in kargs: if not isinstance(kargs[argname], type): ... raise TypeError(errmsg) elif argname in positionals: position = positionals.index(argname) if not isinstance(pargs[position], type): ... raise TypeError(errmsg)

Example: Validating Function Arguments | 1045

www.it-ebooks.info

else: return func(*pargs, **kargs) return onCall return onDecorator @typetest(a=int, c=float) def func(a, b, c, d): ... func(1, 2, 3.0, 4) func('spam', 2, 99, 4)

Public

@typetest def func(a: int, b, c: float, d): ...

Chapter Summary

1046 | Chapter 38: Decorators

www.it-ebooks.info

Test Your Knowledge: Quiz __call__ self

Public Private @ __debug__

–O

python –O main.py...

Test Your Knowledge: Answers self

import time def timer(label='', trace=True): def onDecorator(func): def onCall(*args, **kargs): start = time.clock() result = func(*args, **kargs) elapsed = time.clock() - start

Test Your Knowledge: Answers | 1047

www.it-ebooks.info

onCall.alltime += elapsed if trace: format = '%s%s: %.5f, %.5f' values = (label, func.__name__, elapsed, onCall.alltime) print(format % values) return result onCall.alltime = 0 return onCall return onDecorator

@timer(trace=True, label='[CCC]==>') def listcomp(N): return [x * 2 for x in range(N)] @timer(trace=True, label='[MMM]==>') def mapcall(N): return list(map((lambda x: x * 2), range(N))) for func in (listcomp, mapcall): result = func(5) func(5000000) print(result) print('allTime = %s\n' % func.alltime)

class Person: def __init__(self, name, pay): self.name = name self.pay = pay @timer() def giveRaise(self, percent): self.pay *= (1.0 + percent) @timer(label='**') def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) bob.giveRaise(.10) sue.giveRaise(.20) print(bob.pay, sue.pay) print(bob.lastName(), sue.lastName()) print('%.5f %.5f' % (Person.giveRaise.alltime, Person.lastName.alltime))

[CCC]==>listcomp: 0.00002, 0.00002 [CCC]==>listcomp: 1.19636, 1.19638 [0, 2, 4, 6, 8] allTime = 1.19637775192

1048 | Chapter 38: Decorators

www.it-ebooks.info

[MMM]==>mapcall: 0.00002, 0.00002 [MMM]==>mapcall: 2.29260, 2.29262 [0, 2, 4, 6, 8] allTime = 2.2926232943 giveRaise: 0.00001, 0.00001 giveRaise: 0.00001, 0.00002 55000.0 120000.0 **lastName: 0.00001, 0.00001 **lastName: 0.00001, 0.00002 Smith Jones 0.00002 0.00002

–O

__getattr__ traceMe = False def trace(*args): if traceMe: print('[' + ' '.join(map(str, args)) + ']') def accessControl(failIf): def onDecorator(aClass): if not __debug__: return aClass else: class onInstance: def __init__(self, *args, **kargs): self.__wrapped = aClass(*args, **kargs) def __getattr__(self, attr): trace('get:', attr) if failIf(attr): raise TypeError('private attribute fetch: ' + attr) else: return getattr(self.__wrapped, attr) def __setattr__(self, attr, value): trace('set:', attr, value) if attr == '_onInstance__wrapped': self.__dict__[attr] = value elif failIf(attr): raise TypeError('private attribute change: ' + attr) else: setattr(self.__wrapped, attr, value) return onInstance return onDecorator def Private(*attributes): return accessControl(failIf=(lambda attr: attr in attributes)) def Public(*attributes): return accessControl(failIf=(lambda attr: attr not in attributes))

Test Your Knowledge: Answers | 1049

www.it-ebooks.info

@Private('age') class Person: def __init__(self, name, age): self.name = name self.age = age X = Person('Bob', 40) print(X.name) X.name = 'Sue' print(X.name) #print(X.age) #X.age = 999 #print(X.age) @Public('name') class Person: def __init__(self, name, age): self.name = name self.age = age X = Person('bob', 40) print(X.name) X.name = 'Sue' print(X.name) #print(X.age) #X.age = 999 #print(X.age)

1050 | Chapter 38: Decorators

www.it-ebooks.info

CHAPTER 39

Metaclasses

class

1051

www.it-ebooks.info

To Metaclass or Not to Metaclass

Increasing Levels of Magic

1052 | Chapter 39: Metaclasses

www.it-ebooks.info

__class__

__dict__

__str__

__add__

__getattr__

__setattr__

__getattribute__

property

property __get__ __set__

__delete__

@callable

To Metaclass or Not to Metaclass | 1053

www.it-ebooks.info

class

The Downside of “Helper” Functions

1054 | Chapter 39: Metaclasses

www.it-ebooks.info

class Extras: def extra(self, args): ... class Client1(Extras): ... class Client2(Extras): ... class Client3(Extras): ... X = Client1() X.extra()

required def extra(self, arg): ... class Client1: ... if required(): Client1.extra = extra class Client2: ... if required(): Client2.extra = extra class Client3: ... if required(): Client3.extra = extra X = Client1() X.extra()

class self

def extra(self, arg): ... def extras(Class): if required(): Class.extra = extra class Client1: ...

To Metaclass or Not to Metaclass | 1055

www.it-ebooks.info

extras(Client1) class Client2: ... extras(Client2) class Client3: ... extras(Client3) X = Client1() X.extra()

class

def extra(self, arg): ... class Extras(type): def __init__(Class, classname, superclasses, attributedict): if required(): Class.extra = extra class Client1(metaclass=Extras): ... class Client2(metaclass=Extras): ... class Client3(metaclass=Extras): ... X = Client1() X.extra()

class

Metaclasses Versus Class Decorators: Round 1

1056 | Chapter 39: Metaclasses

www.it-ebooks.info

def extra(self, arg): ... def extras(Class): if required(): Class.extra = extra return Class class Client1: ... Client1 = extras(Client1) class Client2: ... Client2 = extras(Client2) class Client3: ... Client3 = extras(Client3) X = Client1() X.extra()

def extra(self, arg): ... def extras(Class): if required(): Class.extra = extra return Class @extras class Client1: ... @extras class Client2: ... @extras class Client3: ... X = Client1() X.extra()

To Metaclass or Not to Metaclass | 1057

www.it-ebooks.info

__init__

The Metaclass Model class

Classes Are Instances of type

type object type

1058 | Chapter 39: Metaclasses

type

www.it-ebooks.info

type type

type

C:\misc> c:\python30\python >>> type([]) >>> type(type([])) >>> type(list) >>> type(type)

type C:\misc> c:\python26\python >>> type([]) >>> type(type([])) >>> type(list) >>> type(type)

type

type

The Metaclass Model | 1059

www.it-ebooks.info

type __class__

type

__class__

C:\misc> c:\python30\python >>> class C: pass ... >>> X = C() >>> type(X) >>> X.__class__ >>> type(C) >>> C.__class__

type

type object C:\misc> c:\python26\python >>> class C(object): pass ... >>> X = C() >>> type(X) >>> type(C) >>> X.__class__ >>> C.__class__

__class__ type C:\misc> c:\python26\python >>> class C: pass ... >>> X = C()

1060 | Chapter 39: Metaclasses

www.it-ebooks.info

>>> type(X) >>> type(C) >>> X.__class__ >>> C.__class__ AttributeError: class C has no attribute '__class__'

Metaclasses Are Subclasses of Type type type type type

type type type

type

class __bases__

Class Statement Protocol type type class

The Metaclass Model | 1061

www.it-ebooks.info

def

class type

class

class = type(classname, superclasses, attributedict)

type

__call__ type

type.__new__(typeclass, classname, superclasses, attributedict) type.__init__(class, classname, superclasses, attributedict)

__new__

class

__init__

type class Spam(Eggs): data = 1 def meth(self, arg): pass

data meth class

type

class

Spam = type('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

class type

Declaring Metaclasses type

class class Spam(metaclass=Meta):

Spam Meta class Spam(Eggs, metaclass=Meta):

1062 | Chapter 39: Metaclasses

Eggs

www.it-ebooks.info

object

class spam(object): __metaclass__ = Meta

__metaclass__

object class class

type

class = Meta(classname, superclasses, attributedict)

type

type

__call__

class Meta.__new__(Meta, classname, superclasses, attributedict) Meta.__init__(class, classname, superclasses, attributedict)

class Spam(Eggs, metaclass=Meta): data = 1 def meth(self, arg): pass

class class Spam = Meta('Spam', (Eggs,), {'data': 1, 'meth': meth, '__module__': '__main__'})

__new__ __init__ type __call__

Coding Metaclasses type class class type Coding Metaclasses | 1063

www.it-ebooks.info

A Basic Metaclass type __new__

type __new__

__call__

type type

__new__ class Meta(type): def __new__(meta, classname, supers, classdict): return type.__new__(meta, classname, supers, classdict)

type

type

__call__

__new__

class __init__

class MetaOne(type): def __new__(meta, classname, supers, classdict): print('In MetaOne.new:', classname, supers, classdict, sep='\n...') return type.__new__(meta, classname, supers, classdict) class Eggs: pass print('making class') class Spam(Eggs, metaclass=MetaOne): data = 1 def meth(self, arg): pass print('making instance') X = Spam() print('data:', X.data)

Spam

Eggs

MetaOne

X

Spam class making class In MetaOne.new: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } making instance data: 1

1064 | Chapter 39: Metaclasses

www.it-ebooks.info

Customizing Construction and Initialization __init__ __call__

__new__

__init__

class MetaOne(type): def __new__(meta, classname, supers, classdict): print('In MetaOne.new: ', classname, supers, classdict, sep='\n...') return type.__new__(meta, classname, supers, classdict) def __init__(Class, classname, supers, classdict): print('In MetaOne init:', classname, supers, classdict, sep='\n...') print('...init class object:', list(Class.__dict__.keys())) class Eggs: pass print('making class') class Spam(Eggs, metaclass=MetaOne): data = 1 def meth(self, arg): pass print('making instance') X = Spam() print('data:', X.data)

class making class In MetaOne.new: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } In MetaOne init: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } ...init class object: ['__module__', 'data', 'meth', '__doc__'] making instance data: 1

Other Metaclass Coding Techniques type

__new__

__init__

Coding Metaclasses | 1065

www.it-ebooks.info

Using simple factory functions class

def MetaFunc(classname, supers, classdict): print('In MetaFunc: ', classname, supers, classdict, sep='\n...') return type(classname, supers, classdict) class Eggs: pass print('making class') class Spam(Eggs, metaclass=MetaFunc): data = 1 def meth(self, args): pass print('making instance') X = Spam() print('data:', X.data)

class type

__call__

making class In MetaFunc: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } making instance data: 1

Overloading class creation calls with metaclasses class __call__

class SuperMeta(type): def __call__(meta, classname, supers, classdict): print('In SuperMeta.call: ', classname, supers, classdict, sep='\n...') return type.__call__(meta, classname, supers, classdict) class SubMeta(type, metaclass=SuperMeta): def __new__(meta, classname, supers, classdict):

1066 | Chapter 39: Metaclasses

type

www.it-ebooks.info

print('In SubMeta.new: ', classname, supers, classdict, sep='\n...') return type.__new__(meta, classname, supers, classdict) def __init__(Class, classname, supers, classdict): print('In SubMeta init:', classname, supers, classdict, sep='\n...') print('...init class object:', list(Class.__dict__.keys())) class Eggs: pass print('making class') class Spam(Eggs, metaclass=SubMeta): data = 1 def meth(self, arg): pass print('making instance') X = Spam() print('data:', X.data)

type making class In SuperMeta.call: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } In SubMeta.new: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } In SubMeta init: ...Spam ...(,) ...{'__module__': '__main__', 'data': 1, 'meth': } ...init class object: ['__module__', 'data', 'meth', '__doc__'] making instance data: 1

Overloading class creation calls with normal classes

__call__

__new__ SubMeta

__init__

Coding Metaclasses | 1067

www.it-ebooks.info

class SuperMeta: def __call__(self, classname, supers, classdict): print('In SuperMeta.call: ', classname, supers, classdict, sep='\n...') Class = self.__New__(classname, supers, classdict) self.__Init__(Class, classname, supers, classdict) return Class class SubMeta(SuperMeta): def __New__(self, classname, supers, classdict): print('In SubMeta.new: ', classname, supers, classdict, sep='\n...') return type(classname, supers, classdict) def __Init__(self, Class, classname, supers, classdict): print('In SubMeta init:', classname, supers, classdict, sep='\n...') print('...init class object:', list(Class.__dict__.keys())) class Eggs: pass print('making class') class Spam(Eggs, metaclass=SubMeta()): data = 1 def meth(self, arg): pass print('making instance') X = Spam() print('data:', X.data)

type

__new__

__init__

Instances Versus Inheritance

class type type

__new__

__init__ __call__ type

1068 | Chapter 39: Metaclasses

www.it-ebooks.info

metaclass=M

__dict__

class MetaOne(type): def __new__(meta, classname, supers, classdict): print('In MetaOne.new:', classname) return type.__new__(meta, classname, supers, classdict) def toast(self): print('toast') class Super(metaclass=MetaOne): def spam(self): print('spam') class C(Super): def eggs(self): print('eggs') X = C() X.eggs() X.spam() X.toast()

In MetaOne.new: Super In MetaOne.new: C eggs spam AttributeError: 'C' object has no attribute 'toast'

Coding Metaclasses | 1069

www.it-ebooks.info

Example: Adding Methods to Classes

Manual Augmentation

class Client1: def __init__(self, value): self.value = value def spam(self): return self.value * 2 class Client2: value = 'ni?' def eggsfunc(obj): return obj.value * 4 def hamfunc(obj, value): return value + 'ham' Client1.eggs = eggsfunc Client1.ham = hamfunc Client2.eggs = eggsfunc

1070 | Chapter 39: Metaclasses

www.it-ebooks.info

Client2.ham

= hamfunc

X = Client1('Ni!') print(X.spam()) print(X.eggs()) print(X.ham('bacon')) Y = Client2() print(Y.eggs()) print(Y.ham('bacon'))

self

Ni!Ni! Ni!Ni!Ni!Ni! baconham ni?ni?ni?ni? baconham

Metaclass-Based Augmentation

def eggsfunc(obj): return obj.value * 4

Example: Adding Methods to Classes | 1071

www.it-ebooks.info

def hamfunc(obj, value): return value + 'ham' class Extender(type): def __new__(meta, classname, supers, classdict): classdict['eggs'] = eggsfunc classdict['ham'] = hamfunc return type.__new__(meta, classname, supers, classdict) class Client1(metaclass=Extender): def __init__(self, value): self.value = value def spam(self): return self.value * 2 class Client2(metaclass=Extender): value = 'ni?' X = Client1('Ni!') print(X.spam()) print(X.eggs()) print(X.ham('bacon')) Y = Client2() print(Y.eggs()) print(Y.ham('bacon'))

Ni!Ni! Ni!Ni!Ni!Ni! baconham ni?ni?ni?ni? baconham

class MetaExtend(type): def __new__(meta, classname, supers, classdict): if sometest(): classdict['eggs'] = eggsfunc1 else: classdict['eggs'] = eggsfunc2 if someothertest():

1072 | Chapter 39: Metaclasses

www.it-ebooks.info

classdict['ham'] = hamfunc else: classdict['ham'] = lambda *args: 'Not supported' return type.__new__(meta, classname, supers, classdict)

Metaclasses Versus Class Decorators: Round 2

class class

Decorator-based augmentation

__init__

def eggsfunc(obj): return obj.value * 4 def hamfunc(obj, value): return value + 'ham' def Extender(aClass): aClass.eggs = eggsfunc aClass.ham = hamfunc return aClass @Extender class Client1: def __init__(self, value): self.value = value def spam(self): return self.value * 2

Example: Adding Methods to Classes | 1073

www.it-ebooks.info

@Extender class Client2: value = 'ni?' X = Client1('Ni!') print(X.spam()) print(X.eggs()) print(X.ham('bacon')) Y = Client2() print(Y.eggs()) print(Y.ham('bacon'))

Managing instances instead of classes

def Tracer(aClass): class Wrapper: def __init__(self, *args, **kargs): self.wrapped = aClass(*args, **kargs) def __getattr__(self, attrname): print('Trace:', attrname) return getattr(self.wrapped, attrname) return Wrapper @Tracer class Person: def __init__(self, name, hours, rate): self.name = name self.hours = hours self.rate = rate def pay(self): return self.hours * self.rate bob = Person('Bob', 40, 50)

1074 | Chapter 39: Metaclasses

www.it-ebooks.info

print(bob.name) print(bob.pay())

Trace: name Bob Trace: pay 2000

def Tracer(classname, supers, classdict): aClass = type(classname, supers, classdict) class Wrapper: def __init__(self, *args, **kargs): self.wrapped = aClass(*args, **kargs) def __getattr__(self, attrname): print('Trace:', attrname) return getattr(self.wrapped, attrname) return Wrapper class Person(metaclass=Tracer): def __init__(self, name, hours, rate): self.name = name self.hours = hours self.rate = rate def pay(self): return self.hours * self.rate bob = Person('Bob', 40, 50) print(bob.name) print(bob.pay())

type type

class

Trace: name Bob Trace: pay 2000

Example: Adding Methods to Classes | 1075

www.it-ebooks.info

Example: Applying Decorators to Methods class

Tracing with Decoration Manually

def tracer(func): calls = 0 def onCall(*args, **kwargs): nonlocal calls calls += 1 print('call %s to %s' % (calls, func.__name__)) return func(*args, **kwargs) return onCall import time def timer(label='', trace=True): def onDecorator(func): def onCall(*args, **kargs): start = time.clock() result = func(*args, **kargs) elapsed = time.clock() - start onCall.alltime += elapsed if trace: format = '%s%s: %.5f, %.5f' values = (label, func.__name__, elapsed, onCall.alltime) print(format % values)

1076 | Chapter 39: Metaclasses

www.it-ebooks.info

return result onCall.alltime = 0 return onCall return onDecorator

@ from mytools import tracer class Person: @tracer def __init__(self, name, pay): self.name = name self.pay = pay @tracer def giveRaise(self, percent): self.pay *= (1.0 + percent) @tracer def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) print(bob.name, sue.name) sue.giveRaise(.10) print(sue.pay) print(bob.lastName(), sue.lastName())

call 1 to __init__ call 2 to __init__ Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

Tracing with Metaclasses and Decorators

Example: Applying Decorators to Methods | 1077

www.it-ebooks.info

from types import FunctionType from mytools import tracer class MetaTrace(type): def __new__(meta, classname, supers, classdict): for attr, attrval in classdict.items(): if type(attrval) is FunctionType: classdict[attr] = tracer(attrval) return type.__new__(meta, classname, supers, classdict) class Person(metaclass=MetaTrace): def __init__(self, name, pay): self.name = name self.pay = pay def giveRaise(self, percent): self.pay *= (1.0 + percent) def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) print(bob.name, sue.name) sue.giveRaise(.10) print(sue.pay) print(bob.lastName(), sue.lastName())

call 1 to __init__ call 2 to __init__ Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

1078 | Chapter 39: Metaclasses

www.it-ebooks.info

Applying Any Decorator to Methods

from types import FunctionType from mytools import tracer, timer def decorateAll(decorator): class MetaDecorate(type): def __new__(meta, classname, supers, classdict): for attr, attrval in classdict.items(): if type(attrval) is FunctionType: classdict[attr] = decorator(attrval) return type.__new__(meta, classname, supers, classdict) return MetaDecorate class Person(metaclass=decorateAll(tracer)): def __init__(self, name, pay): self.name = name self.pay = pay def giveRaise(self, percent): self.pay *= (1.0 + percent) def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) print(bob.name, sue.name) sue.giveRaise(.10) print(sue.pay) print(bob.lastName(), sue.lastName())

call 1 to __init__ call 2 to __init__ Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

class

Example: Applying Decorators to Methods | 1079

www.it-ebooks.info

class Person(metaclass=decorateAll(tracer)): class Person(metaclass=decorateAll(timer())): class Person(metaclass=decorateAll(timer(label='**'))):

print('-'*40) print('%.5f' % Person.__init__.alltime) print('%.5f' % Person.giveRaise.alltime) print('%.5f' % Person.lastName.alltime)

**__init__: 0.00001, 0.00001 **__init__: 0.00001, 0.00002 Bob Smith Sue Jones **giveRaise: 0.00001, 0.00001 110000.0 **lastName: 0.00001, 0.00001 **lastName: 0.00001, 0.00002 Smith Jones ---------------------------------------0.00002 0.00001 0.00002

Metaclasses Versus Class Decorators: Round 3

from types import FunctionType from mytools import tracer, timer def decorateAll(decorator): def DecoDecorate(aClass):

1080 | Chapter 39: Metaclasses

www.it-ebooks.info

for attr, attrval in aClass.__dict__.items(): if type(attrval) is FunctionType: setattr(aClass, attr, decorator(attrval)) return aClass return DecoDecorate @decorateAll(tracer) class Person: def __init__(self, name, pay): self.name = name self.pay = pay def giveRaise(self, percent): self.pay *= (1.0 + percent) def lastName(self): return self.name.split()[-1] bob = Person('Bob Smith', 50000) sue = Person('Sue Jones', 100000) print(bob.name, sue.name) sue.giveRaise(.10) print(sue.pay) print(bob.lastName(), sue.lastName())

call 1 to __init__ call 2 to __init__ Bob Smith Sue Jones call 1 to giveRaise 110000.0 call 1 to lastName call 2 to lastName Smith Jones

Person Person

Example: Applying Decorators to Methods | 1081

www.it-ebooks.info

@decorateAll(tracer) @decorateAll(timer()) @decorateAll(timer(label='@@'))

print('-'*40) print('%.5f' % Person.__init__.alltime) print('%.5f' % Person.giveRaise.alltime) print('%.5f' % Person.lastName.alltime)

@@__init__: 0.00001, 0.00001 @@__init__: 0.00001, 0.00002 Bob Smith Sue Jones @@giveRaise: 0.00001, 0.00001 110000.0 @@lastName: 0.00001, 0.00001 @@lastName: 0.00001, 0.00002 Smith Jones ---------------------------------------0.00002 0.00001 0.00002

“Optional” Language Features

1082 | Chapter 39: Metaclasses

www.it-ebooks.info

Example: Applying Decorators to Methods | 1083

www.it-ebooks.info

Chapter Summary

class

Test Your Knowledge: Quiz

1084 | Chapter 39: Metaclasses

www.it-ebooks.info

Test Your Knowledge: Answers type

type class

__new__

__init__

class C(metaclass=M) =M class metaclass

class __metaclass__

class

class

Test Your Knowledge: Answers | 1085

www.it-ebooks.info

www.it-ebooks.info

PART IX

Appendixes

www.it-ebooks.info

www.it-ebooks.info

APPENDIX A

Installation and Configuration

Installing the Python Interpreter

Is Python Already Present?

python python

1089

www.it-ebooks.info

Where to Get Python

1090 | Appendix A: Installation and Configuration

www.it-ebooks.info

Installation Steps

tkinter Tkinter

tkinter

config make Installing the Python Interpreter | 1091

www.it-ebooks.info

The Python MSI Installer on Windows Vista

cd C:\user\downloads msiexec /i python-2.5.1.msi

1092 | Appendix A: Installation and Configuration

www.it-ebooks.info

Configuring Python

Python Environment Variables

Variable

Role

PATH (or path)

System shell search path (for finding “python”)

PYTHONPATH

Python module search path (for imports)

PYTHONSTARTUP

Path to Python interactive startup file

TCL_LIBRARY, TK_LIBRARY

GUI extension variables (tkinter)

PATH PATH

cd C:\Python30

PATH C:\Python30\python PATH

python

Configuring Python | 1093

www.it-ebooks.info

PYTHONPATH PYTHONPATH PYTHONPATH

PATH

sys.path

PYTHONPATH PYTHONSTARTUP PYTHONSTARTUP

tkinter tkinter

Tkinter PYTHONPATH tkinter

Getting tkinter (and IDLE) GUI Support on Linux tkinter tkinter

Tkinter

yum tkinter

1094 | Appendix A: Installation and Configuration

tkinter

www.it-ebooks.info

How to Set Configuration Options

import spam

Unix/Linux shell variables

setenv PYTHONPATH /usr/home/pycode/utilities:/usr/lib/pycode/package1

export PYTHONPATH="/usr/home/pycode/utilities:/usr/lib/pycode/package1"

DOS variables (Windows)

set PYTHONPATH=c:\pycode\utilities;d:\pycode\package1

Windows environment variable GUI PYTHONPATH

Configuring Python | 1095

www.it-ebooks.info

PYTHONPATH set

Windows registry

regedit

Path files PYTHONPATH c:\pycode\utilities d:\pycode\package1

sys.path

1096 | Appendix A: Installation and Configuration

www.it-ebooks.info

Python Command-Line Options

python [-bBdEhiOsSuvVWx?] [-c command | -m module-name | script | - ] [args]

script

args

sys.argv import sys print(sys.argv)

python a b –c

main.py sys.argv

sys.argv c:\Python30> python main.py a b –c ['main.py', 'a', 'b', '-c']

-c – c:\Python30> python -c "print(2 ** 100)" 1267650600228229401496703205376 c:\Python30> python -c "import main" ['-c'] c:\Python30> python - < main.py a b –c ['-', 'a', 'b', '-c'] c:\Python30> python - a b -c < main.py ['-', 'a', 'b', '-c']

–m sys.path

__main__

c:\Python30> python -m main a b –c ['c:\\Python30\\main.py', 'a', 'b', '-c']

–m pdb

profile

Configuring Python | 1097

www.it-ebooks.info

profile

execfile

pdb

io c:\Python30> python -m pdb main.py a b -c --Return-> c:\python30\lib\io.py(762)closed()->False -> return self.raw.closed (Pdb) c c:\Python30> C:\python26\python -m pdb main.py a b -c > c:\python30\main.py(1)() -> import sys (Pdb) c c:\Python30> python -m profile main.py a b -c c:\Python30> python -m cProfile main.py a b -c

-u

-O –i

c:\Python30> python –u main.py a b -c

−3 -Q -t

c:\Python30> python -?

getopt

optparse

For More Help

1098 | Appendix A: Installation and Configuration

www.it-ebooks.info

For More Help | 1099

www.it-ebooks.info

www.it-ebooks.info

APPENDIX B

Solutions to End-of-Part Exercises

Part I, Getting Started

% python ...copyright information lines... >>> "Hello World!" 'Hello World!' >>>

print('Hello module world!') % python module1.py Hello module world!

% python >>> import module1 Hello module world! >>>

1101

www.it-ebooks.info

#! #! #!/usr/local/bin/python print('Hello module world!') % chmod +x module1.py

(or #!/usr/bin/env python)

% module1.py Hello module world!

try

% python >>> 2 ** 500 32733906078961418700131896968275991522166420460430647894832913680961337964046745 54883270092325904157150886684127560071009217256545885393053328527589376 >>> >>> 1 / 0 Traceback (most recent call last): File "", line 1, in ZeroDivisionError: int division or modulo by zero >>> >>> spam Traceback (most recent call last): File "", line 1, in NameError: name 'spam' is not defined

L = [1, 2] L.append(L)

[1, 2, [1, 2, [1, 2, [1, 2

1102 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

[[...]]

L append append L

L

Part II, Types and Operations

; ; Part II, Types and Operations | 1103

www.it-ebooks.info

/ list

>>> 2 ** 16 65536 >>> 2 / 5, 2 / 5.0 (0.40000000000000002, 0.40000000000000002)

>>> "spam" + "eggs" 'spameggs' >>> S = "ham" >>> "eggs " + S 'eggs ham' >>> S * 5 'hamhamhamhamham' >>> S[:0] '' >>> "green %s and %s" % ("eggs", S) 'green eggs and ham' >>> 'green {0} and {1}'.format('eggs', S) 'green eggs and ham'

>>> ('x',)[0] 'x' >>> ('x', 'y')[1] 'y'

>>> L = [1,2,3] + [4,5,6] >>> L, L[:], L[:0], L[-2], L[-2:] ([1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5, 6], [], 5, [5, 6]) >>> ([1,2,3]+[4,5,6])[2:4] [3, 4] >>> [L[2], L[3]] [3, 4] >>> L.reverse(); L [6, 5, 4, 3, 2, 1] >>> L.sort(); L [1, 2, 3, 4, 5, 6] >>> L.index(4) 3

>>> {'a':1, 'b':2}['b']

1104 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

2 >>> >>> >>> 1 >>>

D = {'x':1, 'y':2, 'z':3} D['w'] = 0 D['x'] + D['w'] D[(1,2,3)] = 4

>>> D {'w': 0, 'z': 3, 'y': 2, (1, 2, 3): 4, 'x': 1} >>> list(D.keys()), list(D.values()), (1,2,3) in D (['w', 'z', 'y', (1, 2, 3), 'x'], [0, 3, 2, 4, 1], True)

>>> [[]], ["",[],(),{},None] ([[]], ['', [], (), {}, None])

L[4] L[-1000:100]

L[3:1]

[ ] L[3:1]

L[3:3]

3

L[3:1:-1] >>> L = [1, 2, 3, 4] >>> L[4] Traceback (innermost last): File "", line 1, in ? IndexError: list index out of range >>> L[-1000:100] [1, 2, 3, 4] >>> L[3:1] [] >>> L [1, 2, 3, 4] >>> L[3:1] = ['?'] >>> L [1, 2, 3, '?', 4]

Part II, Types and Operations | 1105

www.it-ebooks.info

>>> L = [1,2,3,4] >>> L[2] = [] >>> L [1, 2, [], 4] >>> L[2:3] = [] >>> L [1, 2, 4] >>> del L[0] >>> L [2, 4] >>> del L[1:] >>> L [2] >>> L[1:2] = 1 Traceback (innermost last): File "", line 1, in ? TypeError: illegal argument type for built-in operation

X

Y =

>>> X = 'spam' >>> Y = 'eggs' >>> X, Y = Y, X >>> X 'eggs' >>> Y 'spam'

>>> >>> >>> >>> >>> {1:

D = {} D[1] = 'a' D[2] = 'b' D[(1, 2, 3)] = 'c' D 'a', 2: 'b', (1, 2, 3): 'c'}

D['d'] D['d']='spam'

1106 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

>>> D = {'a':1, 'b':2, 'c':3} >>> D['a'] 1 >>> D['d'] Traceback (innermost last): File "", line 1, in ? KeyError: d >>> D['d'] = 4 >>> D {'b': 2, 'd': 4, 'a': 1, 'c': 3} >>> >>> L = [0, 1] >>> L[2] Traceback (innermost last): File "", line 1, in ? IndexError: list index out of range >>> L[2] = 3 Traceback (innermost last): File "", line 1, in ? IndexError: list assignment index out of range

+

+

+

+ append

keys append

>>> "x" + 1 Traceback (innermost last): File "", line 1, in ? TypeError: illegal argument type for built-in operation >>> >>> {} + {} Traceback (innermost last): File "", line 1, in ? TypeError: bad operand type(s) for + >>> >>> [].append(9) >>> "".append('s') Traceback (innermost last): File "", line 1, in ? AttributeError: attribute-less object >>> >>> list({}.keys()) [] >>> [].keys() Traceback (innermost last): File "", line 1, in ? AttributeError: keys

Part II, Types and Operations | 1107

www.it-ebooks.info

>>> >>> [][:] [] >>> ""[:] ''

S[0][0][0][0][0]

>>> >>> 's' >>> >>> 's'

S = "spam" S[0][0][0][0][0] L = ['s', 'p'] L[0][0][0]

>>> S = "spam" >>> S = S[0] + 'l' + S[2:] >>> S 'slam' >>> S = S[0] + 'l' + S[2] + S[3] >>> S 'slam'

bytearray

>>> me = {'name':('John', 'Q', 'Doe'), 'age':'?', 'job':'engineer'} >>> me['job'] 'engineer' >>> me['name'][2] 'Doe'

ls dir file = open('myfile.txt', 'w') file.write('Hello file world!\n') file.close() file = open('myfile.txt') print(file.read()) % python maker.py % python reader.py Hello file world!

1108 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

% ls -l myfile.txt -rwxrwxrwa 1 0

0

19 Apr 13 16:33 myfile.txt

Part III, Statements and Syntax

>>> S = 'spam' >>> for c in S: ... print(ord(c)) ... 115 112 97 109 >>> x = 0 >>> for c in S: x += ord(c) ... >>> x 433 >>> x = [] >>> for c in S: x.append(ord(c)) ... >>> x [115, 112, 97, 109] >>> list(map(ord, S)) [115, 112, 97, 109]

\a

keys

sort

sort

None keys

for key in D: sorted >>> D = {'a':1, 'b':2, 'c':3, 'd':4, 'e':5, 'f':6, 'g':7} >>> D {'f': 6, 'c': 3, 'a': 1, 'g': 7, 'e': 5, 'd': 4, 'b': 2} >>> >>> keys = list(D.keys())

Part III, Statements and Syntax | 1109

www.it-ebooks.info

>>> keys.sort() >>> for key in keys: ... print(key, '=>', D[key]) ... a => 1 b => 2 c => 3 d => 4 e => 5 f => 6 g => 7 >>> for key in sorted(D): ... print(key, '=>', D[key])

e 2 ** X

L = [1, 2, 4, 8, 16, 32, 64] X = 5 i = 0 while i < len(L): if 2 ** X == L[i]: print('at index', i) break i += 1 else: print(X, 'not found')

L = [1, 2, 4, 8, 16, 32, 64] X = 5 for p in L: if (2 ** X) == p: print((2 ** X), 'was found at', L.index(p)) break else: print(X, 'not found')

L = [1, 2, 4, 8, 16, 32, 64] X = 5 if (2 ** X) in L: print((2 ** X), 'was found at', L.index(2 ** X))

1110 | Appendix B: Solutions to End-of-Part Exercises

a

b

www.it-ebooks.info

else: print(X, 'not found')

X = 5 L = [] for i in range(7): L.append(2 ** i) print(L) if (2 ** X) in L: print((2 ** X), 'was found at', L.index(2 ** X)) else: print(X, 'not found') # X = 5 L = list(map(lambda x: 2**x, range(7))) print(L) if (2 ** X) in L: print((2 ** X), 'was found at', L.index(2 ** X)) else: print(X, 'not found')

Part IV, Functions print

% python >>> def func(x): print(x) ... >>> func("spam") spam >>> func(42) 42 >>> func([1, 2, 3]) [1, 2, 3] >>> func({'food': 'spam'}) {'food': 'spam'}

print

Part IV, Functions | 1111

www.it-ebooks.info

def adder(x, y): return x + y print(adder(2, 3)) print(adder('spam', 'eggs')) print(adder(['a', 'b'], ['c', 'd'])) % python mod.py 5 spameggs ['a', 'b', 'c', 'd']

adder

min +

def adder1(*args): print('adder1', end=' ') if type(args[0]) == type(0): sum = 0 else: sum = args[0][:0] for arg in args: sum = sum + arg return sum def adder2(*args): print('adder2', end=' ') sum = args[0] for next in args[1:]: sum += next return sum for func in (adder1, adder2): print(func(2, 3, 4)) print(func('spam', 'eggs', 'toast')) print(func(['a', 'b'], ['c', 'd'], ['e', 'f'])) % python adders.py adder1 9 adder1 spameggstoast adder1 ['a', 'b', 'c', 'd', 'e', 'f'] adder2 9 adder2 spameggstoast adder2 ['a', 'b', 'c', 'd', 'e', 'f']

1112 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

args.values()

**args for x in args.keys(): use args[x] *args

def adder(good=1, bad=2, ugly=3): return good + bad + ugly print(adder()) print(adder(5)) print(adder(5, 6)) print(adder(5, 6, 7)) print(adder(ugly=7, good=6, bad=5)) % python mod.py 6 10 14 18 18

def adder1(*args): tot = args[0] for arg in args[1:]: tot += arg return tot def adder2(**args): argskeys = list(args.keys()) tot = args[argskeys[0]] for key in argskeys[1:]: tot += args[key] return tot def adder3(**args): args = list(args.values()) tot = args[0] for arg in args[1:]: tot += arg return tot def adder4(**args): return adder1(*args.values()) print(adder1(1, 2, 3), adder1('aa', 'bb', 'cc')) print(adder2(a=1, b=2, c=3), adder2(a='aa', b='bb', c='cc')) print(adder3(a=1, b=2, c=3), adder3(a='aa', b='bb', c='cc')) print(adder4(a=1, b=2, c=3), adder4(a='aa', b='bb', c='cc'))

Part IV, Functions | 1113

www.it-ebooks.info

D.copy()

D1.update(D2) X[:] e = d d

def copyDict(old): new = {} for key in old.keys(): new[key] = old[key] return new def addDict(d1, d2): new = {} for key in d1.keys(): new[key] = d1[key] for key in d2.keys(): new[key] = d2[key] return new % python >>> from dicts import * >>> d = {1: 1, 2: 2} >>> e = copyDict(d) >>> d[2] = '?' >>> d {1: 1, 2: '?'} >>> e {1: 1, 2: 2} >>> >>> >>> >>> {1:

x = {1: 1} y = {2: 2} z = addDict(x, y) z 1, 2: 2}

def f1(a, b): print(a, b) def f2(a, *b): print(a, b) def f3(a, **b): print(a, b) def f4(a, *b, **c): print(a, b, c) def f5(a, b=2, c=3): print(a, b, c)

1114 | Appendix B: Solutions to End-of-Part Exercises

e

www.it-ebooks.info

def f6(a, b=2, *c): print(a, b, c) % python >>> f1(1, 2) 1 2 >>> f1(b=2, a=1) 1 2 >>> f2(1, 2, 3) 1 (2, 3) >>> f3(1, x=2, y=3) 1 {'x': 2, 'y': 3} >>> f4(1, 2, 3, x=2, y=3) 1 (2, 3) {'x': 2, 'y': 3} >>> 1 2 >>> 1 4

f5(1) 3 f5(1, 4) 3

>>> 1 2 >>> 1 3

f6(1) () f6(1, 3, 4) (4,)

if 0

1

/

//

/ from //

/

#from __future__ import division def prime(y): if y 1: if y % x == 0: print(y, 'has factor', x) break x -= 1 else: print(y, 'is prime') prime(13); prime(15); prime(3); prime(1);

prime(13.0) prime(15.0) prime(2) prime(-3)

Part IV, Functions | 1115

www.it-ebooks.info

// % python primes.py 13 is prime 13.0 is prime 15 has factor 5 15.0 has factor 5.0 3 is prime 2 is prime 1 not prime -3 not prime

for while time def timer(reps, func, *args): import time start = time.clock() for i in range(reps): func(*args) return time.clock() - start

>>> values = [2, 4, 9, 16, 25] >>> import math >>> res = [] >>> for x in values: res.append(math.sqrt(x)) ... >>> res [1.4142135623730951, 2.0, 3.0, 4.0, 5.0] >>> list(map(math.sqrt, values)) [1.4142135623730951, 2.0, 3.0, 4.0, 5.0] >>> [math.sqrt(x) for x in values] [1.4142135623730951, 2.0, 3.0, 4.0, 5.0]

...same as listed in Chapter 20...

1116 | Appendix B: Solutions to End-of-Part Exercises

range(y, 1, −1)

www.it-ebooks.info

import sys, mytimer reps = 10000 repslist = range(reps) from math import sqrt def mathMod(): for i in repslist: res = sqrt(i) return res def powCall(): for i in repslist: res = pow(i, .5) return res def powExpr(): for i in repslist: res = i ** .5 return res print(sys.version) for tester in (mytimer.timer, mytimer.best): print('' % tester.__name__) for test in (mathMod, powCall, powExpr): elapsed, result = tester(test) print ('-'*35) print ('%s: %.5f => %s' % (test.__name__, elapsed, result))

math

**

pow

c:\misc> c:\python30\python timesqrt.py 3.0.1 (r301:69561, Feb 13 2009, 20:04:18) [MSC v.1500 32 bit (Intel)] ----------------------------------mathMod: 5.33906 => 99.994999875 ----------------------------------powCall: 7.29689 => 99.994999875 ----------------------------------powExpr: 5.95770 => 99.994999875 ----------------------------------mathMod: 0.00497 => 99.994999875 ----------------------------------powCall: 0.00671 => 99.994999875 ----------------------------------powExpr: 0.00540 => 99.994999875 c:\misc> c:\python26\python timesqrt.py

Part IV, Functions | 1117

www.it-ebooks.info

2.6.1 (r261:67517, Dec 4 2008, 16:51:00) [MSC v.1500 32 bit (Intel)] ----------------------------------mathMod: 2.61226 => 99.994999875 ----------------------------------powCall: 4.33705 => 99.994999875 ----------------------------------powExpr: 3.12502 => 99.994999875 ----------------------------------mathMod: 0.00236 => 99.994999875 ----------------------------------powCall: 0.00402 => 99.994999875 ----------------------------------powExpr: 0.00287 => 99.994999875

for

c:\misc> c:\python30\python >>> >>> def dictcomp(I): ... return {i: i for i in range(I)} ... >>> def dictloop(I): ... new = {} ... for i in range(I): new[i] = i ... return new ... >>> dictcomp(10) {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} >>> dictloop(10) {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9} >>> >>> from mytimer import best, timer >>> best(dictcomp, 10000)[0] 0.0013519874732672577 >>> best(dictloop, 10000)[0] 0.001132965223233029 >>> >>> best(dictcomp, 100000)[0] 0.01816089754424155 >>> best(dictloop, 100000)[0] 0.01643484018219965 >>> >>> best(dictcomp, 1000000)[0] 0.18685105229855026 >>> best(dictloop, 1000000)[0]

1118 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

0.1769041177020938 >>> >>> timer(dictcomp, 1000000, _reps=50)[0] 10.692516087938543 >>> timer(dictloop, 1000000, _reps=50)[0] 10.197276050447755

Part V, Modules

len def countLines(name): file = open(name) return len(file.readlines()) def countChars(name): return len(open(name).read()) def test(name): return countLines(name), countChars(name) % python >>> import mymod >>> mymod.test('mymod.py') (10, 291)

def countLines(name): tot = 0 for line in open(name): tot += 1 return tot def countChars(name): tot = 0 for line in open(name): tot += len(line) return tot

wc \r\n \n 'rb'

Part V, Modules | 1119

www.it-ebooks.info

seek fseek seek seek file.seek(0)

read

def countLines(file): file.seek(0) return len(file.readlines()) def countChars(file): file.seek(0) return len(file.read()) def test(name): file = open(name) return countLines(file), countChars(file) >>> import mymod2 >>> mymod2.test("mymod2.py") (11, 392)

from from *

from *

*

countChars

% python >>> from mymod import * >>> countChars("mymod.py") 291

__main__ def countLines(name): file = open(name) return len(file.readlines()) def countChars(name): return len(open(name).read()) def test(name): return countLines(name), countChars(name) if __name__ == '__main__': print(test('mymod.py')) % python mymod.py (13, 346)

sys.argv

1120 | Appendix B: Solutions to End-of-Part Exercises

input

www.it-ebooks.info

if __name__ == '__main__': print(test(input('Enter file name:')) if __name__ == '__main__': import sys print(test(sys.argv[1]))

from mymod import countLines, countChars print(countLines('mymod.py'), countChars('mymod.py')) % python myclient.py 13 346

mymod myclient from mymod def

myclient

import myclient myclient.countLines(...) from myclient import countChars countChars(...)

myclient

import mymod

from myclient

import myclient myclient.mymod.countLines(...) from myclient import mymod mymod.countChars(...)

somename mod1.somename collector.somename

__main__.somename somename

somename = 42 from mod1 import * from mod2 import * from mod3 import * >>> from collector import somename

mv

vi

move

edit

Part V, Modules | 1121

www.it-ebooks.info

print C:\python30> mkdir mypkg C:\Python30> move mymod.py mypkg\mymod.py C:\Python30> edit mypkg\__init__.py ...coded a print statement... C:\Python30> python >>> import mypkg.mymod initializing mypkg >>> mypkg.mymod.countLines('mypkg\mymod.py') 13 >>> from mypkg.mymod import countChars >>> countChars('mypkg\mymod.py') 346

recur2 recur1 recur1 recur2 recur2 import from recur2 recur2

import

from

recur2

recur2 recur2 recur1 recur1 from

from

Y

recur1

recur1 recur2

recur1

Part VI, Classes and OOP

__add__ add class Adder: def add(self, x, y):

1122 | Appendix B: Solutions to End-of-Part Exercises

recur2

www.it-ebooks.info

print('not implemented!') def __init__(self, start=[]): self.data = start def __add__(self, other): return self.add(self.data, other) class ListAdder(Adder): def add(self, x, y): return x + y class DictAdder(Adder): def add(self, x, y): new = {} for k in x.keys(): new[k] = x[k] for k in y.keys(): new[k] = y[k] return new % python >>> from adder import * >>> x = Adder() >>> x.add(1, 2) not implemented! >>> x = ListAdder() >>> x.add([1], [2]) [1, 2] >>> x = DictAdder() >>> x.add({1:1}, {2:2}) {1: 1, 2: 2} >>> x = Adder([1]) >>> x + [2] not implemented! >>> >>> x = ListAdder([1]) >>> x + [2] [1, 2] >>> [2] + x Traceback (innermost last): File "", line 1, in ? TypeError: __add__ nor __radd__ defined for these operands

+

__radd__

add class Adder: def __init__(self, start=[]): self.data = start def __add__(self, other): return self.add(other) def add(self, y):

Part VI, Classes and OOP | 1123

www.it-ebooks.info

print('not implemented!') class ListAdder(Adder): def add(self, y): return self.data + y class DictAdder(Adder): def add(self, y): pass x = ListAdder([1, 2, 3]) y = x + [4, 5, 6] print(y)

add __add__

__getattr__

class MyList: def __init__(self, start): #self.wrapped = start[:] self.wrapped = [] for x in start: self.wrapped.append(x) def __add__(self, other): return MyList(self.wrapped + other) def __mul__(self, time): return MyList(self.wrapped * time) def __getitem__(self, offset): return self.wrapped[offset] def __len__(self): return len(self.wrapped) def __getslice__(self, low, high): return MyList(self.wrapped[low:high]) def append(self, node): self.wrapped.append(node) def __getattr__(self, name): return getattr(self.wrapped, name) def __repr__(self): return repr(self.wrapped) if __name__ == '__main__': x = MyList('spam') print(x) print(x[2]) print(x[1:])

1124 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

print(x + ['eggs']) print(x * 3) x.append('a') x.sort() for c in x: print(c, end=' ') % python mylist.py ['s', 'p', 'a', 'm'] a ['p', 'a', 'm'] ['s', 'p', 'a', 'm', 'eggs'] ['s', 'p', 'a', 'm', 's', 'p', 'a', 'm', 's', 'p', 'a', 'm'] a a m p s

append MyList

from mylist import MyList class MyListSub(MyList): calls = 0 def __init__(self, start): self.adds = 0 MyList.__init__(self, start) def __add__(self, other): MyListSub.calls += 1 self.adds += 1 return MyList.__add__(self, other) def stats(self): return self.calls, self.adds if __name__ == '__main__': x = MyListSub('spam') y = MyListSub('foo') print(x[2]) print(x[1:]) print(x + ['eggs']) print(x + ['toast']) print(y + ['bar']) print(x.stats()) % python mysub.py

Part VI, Classes and OOP | 1125

www.it-ebooks.info

a ['p', 'a', ['s', 'p', ['s', 'p', ['f', 'o', (3, 2)

'm'] 'a', 'm', 'eggs'] 'a', 'm', 'toast'] 'o', 'bar']

__getattr__ __getattr__ __X__ >>> class Meta: ... def __getattr__(self, name): ... print('get', name) ... def __setattr__(self, name, value): ... print('set', name, value) ... >>> x = Meta() >>> x.append get append >>> x.spam = "pork" set spam pork >>> >>> x + 2 get __coerce__ Traceback (innermost last): File "", line 1, in ? TypeError: call of non-function >>> >>> x[1] get __getitem__ Traceback (innermost last): File "", line 1, in ? TypeError: call of non-function >>> x[1:5] get __len__ Traceback (innermost last): File "", line 1, in ? TypeError: call of non-function

% python >>> from setwrapper import Set >>> x = Set([1, 2, 3, 4]) >>> y = Set([3, 4, 5]) >>> x & y Set:[3, 4] >>> x | y

1126 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

Set:[1, 2, 3, 4, 5] >>> z = Set("hello") >>> z[0], z[-1] ('h', 'o') >>> ... h e >>> (4,

for c in z: print(c, end=' ') l o len(z), z Set:['h', 'e', 'l', 'o'])

>>> z & "mello", z | "mello" (Set:['e', 'l', 'o'], Set:['h', 'e', 'l', 'o', 'm'])

from setwrapper import Set class MultiSet(Set): """ Inherits all Set names, but extends intersect and union to support multiple operands; note that "self" is still the first argument (stored in the *args argument now); also note that the inherited & and | operators call the new methods here with 2 arguments, but processing more than 2 requires a method call, not an expression: """ def intersect(self, *others): res = [] for x in self: for other in others: if x not in other: break else: res.append(x) return Set(res) def union(*args): res = [] for seq in args: for x in seq: if not x in res: res.append(x) return Set(res)

& intersect

intersect &

MultiSet setwrapper.Set

Set multiset

Part VI, Classes and OOP | 1127

www.it-ebooks.info

>>> >>> >>> >>>

from multiset import * x = MultiSet([1,2,3,4]) y = MultiSet([3,4,5]) z = MultiSet([0,1,2])

>>> x & y, x | y (Set:[3, 4], Set:[1, 2, 3, 4, 5]) >>> x.intersect(y, z) Set:[] >>> x.union(y, z) Set:[1, 2, 3, 4, 5, 0] >>> x.intersect([1,2,3], [2,3,4], [1,2,3]) Set:[2, 3] >>> x.union(range(10)) Set:[1, 2, 3, 4, 0, 5, 6, 7, 8, 9]

dir class ListInstance: def __str__(self): return '' % ( self.__class__.__name__, self.__supers(), id(self), self.__attrnames()) ) def __attrnames(self): ...unchanged... def __supers(self): names = [] for super in self.__class__.__bases__: names.append(super.__name__) return ', '.join(names) C:\misc> python testmixin.py

class Lunch: def __init__(self): self.cust = Customer() self.empl = Employee() def order(self, foodName): self.cust.placeOrder(foodName, self.empl) def result(self): self.cust.printFood()

1128 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

class Customer: def __init__(self): self.food = None def placeOrder(self, foodName, employee): self.food = employee.takeOrder(foodName) def printFood(self): print(self.food.name) class Employee: def takeOrder(self, foodName): return Food(foodName) class Food: def __init__(self, name): self.name = name if __name__ == '__main__': x = Lunch() x.order('burritos') x.result() x.order('pizza') x.result() % python lunch.py burritos pizza

self.speak speak

class Animal: def reply(self): def speak(self):

self.speak() print('spam')

class Mammal(Animal): def speak(self):

print('huh?')

class Cat(Mammal): def speak(self):

print('meow')

class Dog(Mammal): def speak(self):

print('bark')

Animal

class Primate(Mammal): def speak(self): print('Hello world!') class Hacker(Primate): pass

Part VI, Classes and OOP | 1129

www.it-ebooks.info

line

Actor

self.name

self

self.says()

class Actor: def line(self): print(self.name + ':', repr(self.says())) class Customer(Actor): name = 'customer' def says(self): return "that's one ex-bird!" class Clerk(Actor): name = 'clerk' def says(self): return "no it isn't..." class Parrot(Actor): name = 'parrot' def says(self): return None class Scene: def __init__(self): self.clerk = Clerk() self.customer = Customer() self.subject = Parrot() def action(self): self.customer.line() self.clerk.line() self.subject.line()

Part VII, Exceptions and Tools try except

oops oops

KeyError

IndexError

try KeyError Error __builtin__ def oops(): raise IndexError() def doomed(): try: oops() except IndexError:

1130 | Appendix B: Solutions to End-of-Part Exercises

dir

Index builtins

www.it-ebooks.info

print('caught an index error!') else: print('no error caught...') if __name__ == '__main__': doomed() % python oops.py caught an index error!

class MyError(Exception): pass def oops(): raise MyError('Spam!') def doomed(): try: oops() except IndexError: print('caught an index error!') except MyError as data: print('caught error:', MyError, data) else: print('no error caught...') if __name__ == '__main__': doomed() % python oops.py caught error: Spam!

__init__

__repr__

Spam! __str__

Exception

import sys, traceback def safe(entry, *args): try: entry(*args) except: traceback.print_exc() print('Got', sys.exc_info()[0], sys.exc_info()[1]) import oops safe(oops.oops) % python safe2.py

Part VII, Exceptions and Tools | 1131

www.it-ebooks.info

Traceback (innermost last): File "safe2.py", line 5, in safe entry(*args) File "oops.py", line 4, in oops raise MyError, 'world' hello: world Got hello world

import os, glob dirname = r'C:\Python30\Lib' allsizes = [] allpy = glob.glob(dirname + os.sep + '*.py') for filename in allpy: filesize = os.path.getsize(filename) allsizes.append((filesize, filename)) allsizes.sort() print(allsizes[:2]) print(allsizes[-2:])

import sys, os, pprint if sys.platform[:3] == 'win': dirname = r'C:\Python30\Lib' else: dirname = '/usr/lib/python' allsizes = [] for (thisDir, subsHere, filesHere) in os.walk(dirname): for filename in filesHere: if filename.endswith('.py'): fullname = os.path.join(thisDir, filename) fullsize = os.path.getsize(fullname) allsizes.append((fullsize, fullname)) allsizes.sort() pprint.pprint(allsizes[:2]) pprint.pprint(allsizes[-2:])

import sys, os, pprint visited = {} allsizes = [] for srcdir in sys.path: for (thisDir, subsHere, filesHere) in os.walk(srcdir): thisDir = os.path.normpath(thisDir)

1132 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

if thisDir.upper() in visited: continue else: visited[thisDir.upper()] = True for filename in filesHere: if filename.endswith('.py'): pypath = os.path.join(thisDir, filename) try: pysize = os.path.getsize(pypath) except: print('skipping', pypath) allsizes.append((pysize, pypath)) allsizes.sort() pprint.pprint(allsizes[:3]) pprint.pprint(allsizes[-3:])

filename = 'data.txt' sums = {} for line in open(filename): cols = line.split(',') nums = [int(col) for col in cols] for (ix, num) in enumerate(nums): sums[ix] = sums.get(ix, 0) + num for key in sorted(sums): print(key, '=', sums[key])

import sys filename = sys.argv[1] numcols = int(sys.argv[2]) totals = [0] * numcols for line in open(filename): cols = line.split(',') nums = [int(x) for x in cols] totals = [(x + y) for (x, y) in zip(totals, nums)] print(totals)

import os testscripts = [dict(script='test1.py', args=''), dict(script='test2.py', args='spam')] for testcase in testscripts:

Part VII, Exceptions and Tools | 1133

www.it-ebooks.info

commandline = '%(script)s %(args)s' % testcase output = os.popen(commandline).read() result = testcase['script'] + '.result' if not os.path.exists(result): open(result, 'w').write(output) print('Created:', result) else: priorresult = open(result).read() if output != priorresult: print('FAILED:', testcase['script']) print(output) else: print('Passed:', testcase['script'])

from tkinter import * import random fontsize = 25 colors = ['red', 'green', 'blue', 'yellow', 'orange', 'white', 'cyan', 'purple'] def reply(text): print(text) popup = Toplevel() color = random.choice(colors) Label(popup, text='Popup', bg='black', fg=color).pack() L.config(fg=color) def timer(): L.config(fg=random.choice(colors)) win.after(250, timer) def grow(): global fontsize fontsize += 5 L.config(font=('arial', fontsize, 'italic')) win.after(100, grow) win = Tk() L = Label(win, text='Spam', font=('arial', fontsize, 'italic'), fg='yellow', bg='navy', relief=RAISED) L.pack(side=TOP, expand=YES, fill=BOTH) Button(win, text='press', command=(lambda: reply('red'))).pack(side=BOTTOM,fill=X) Button(win, text='timer', command=timer).pack(side=BOTTOM, fill=X) Button(win, text='grow', command=grow).pack(side=BOTTOM, fill=X) win.mainloop()

from tkinter import * import random

1134 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

class MyGui: """ A GUI with buttons that change color and make the label grow """ colors = ['blue', 'green', 'orange', 'red', 'brown', 'yellow'] def __init__(self, parent, title='popup'): parent.title(title) self.growing = False self.fontsize = 10 self.lab = Label(parent, text='Gui1', fg='white', bg='navy') self.lab.pack(expand=YES, fill=BOTH) Button(parent, text='Spam', command=self.reply).pack(side=LEFT) Button(parent, text='Grow', command=self.grow).pack(side=LEFT) Button(parent, text='Stop', command=self.stop).pack(side=LEFT) def reply(self): "change the button's color at random on Spam presses" self.fontsize += 5 color = random.choice(self.colors) self.lab.config(bg=color, font=('courier', self.fontsize, 'bold italic')) def grow(self): "start making the label grow on Grow presses" self.growing = True self.grower() def grower(self): if self.growing: self.fontsize += 5 self.lab.config(font=('courier', self.fontsize, 'bold')) self.lab.after(500, self.grower) def stop(self): "stop the button growing on Stop presses" self.growing = False class MySubGui(MyGui): colors = ['black', 'purple'] MyGui(Tk(), 'main') MyGui(Toplevel()) MySubGui(Toplevel()) mainloop()

""" scan pop email box, fetching just headers, allowing deletions without downloading the complete message """ import poplib, getpass, sys

Part VII, Exceptions and Tools | 1135

www.it-ebooks.info

mailserver = 'your pop email server name here' mailuser = 'your pop email user name here' mailpasswd = getpass.getpass('Password for %s?' % mailserver) print('Connecting...') server = poplib.POP3(mailserver) server.user(mailuser) server.pass_(mailpasswd) try:

print(server.getwelcome()) msgCount, mboxSize = server.stat() print('There are', msgCount, 'mail messages, size ', mboxSize) msginfo = server.list() print(msginfo) for i in range(msgCount): msgnum = i+1 msgsize = msginfo[1][i].split()[1] resp, hdrlines, octets = server.top(msgnum, 0) print('-'*80) print('[%d: octets=%d, size=%s]' % (msgnum, octets, msgsize)) for line in hdrlines: print(line) if input('Print?') in ['y', 'Y']: for line in server.retr(msgnum)[1]: print(line) if input('Delete?') in ['y', 'Y']: print('deleting') server.dele(msgnum) else: print('skipping')

finally: server.quit() input('Bye.')

#!/usr/bin/python import cgi form = cgi.FieldStorage() print("Content-type: text/html\n") print("") print("Reply Page") print("") if not 'user' in form: print("Who are you?") else: print("Hello %s!" % cgi.escape(form['user'].value)) print("")

from MySQLdb import Connect

1136 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

conn = Connect(host='localhost', user='root', passwd='darling') curs = conn.cursor() try: curs.execute('drop database testpeopledb') except: pass curs.execute('create database testpeopledb') curs.execute('use testpeopledb') curs.execute('create table people (name char(30), job char(10), pay int(4))') curs.execute('insert people values (%s, %s, %s)', ('Bob', 'dev', 50000)) curs.execute('insert people values (%s, %s, %s)', ('Sue', 'dev', 60000)) curs.execute('insert people values (%s, %s, %s)', ('Ann', 'mgr', 40000)) curs.execute('select * from people') for row in curs.fetchall(): print(row) curs.execute('select * from people where name = %s', ('Bob',)) print(curs.description) colnames = [desc[0] for desc in curs.description] while True: print('-' * 30) row = curs.fetchone() if not row: break for (name, value) in zip(colnames, row): print('%s => %s' % (name, value)) conn.commit()

# see also Chapter 27 shelve and Chapter 30 pickle examples rec1 = {'name': {'first': 'Bob', 'last': 'Smith'}, 'job': ['dev', 'mgr'], 'age': 40.5} rec2 = {'name': {'first': 'Sue', 'last': 'Jones'}, 'job': ['mgr'], 'age': 35.0} import shelve db = shelve.open('dbfile') db['bob'] = rec1 db['sue'] = rec2 db.close()

import shelve db = shelve.open('dbfile')

Part VII, Exceptions and Tools | 1137

www.it-ebooks.info

for key in db: print(key, '=>', db[key]) bob = db['bob'] bob['age'] += 1 db['bob'] = bob db.close()

1138 | Appendix B: Solutions to End-of-Part Exercises

www.it-ebooks.info

Index

Symbols

1139

www.it-ebooks.info

A

1140 | Index

www.it-ebooks.info

B

C

Index | 1141

www.it-ebooks.info

1142 | Index

www.it-ebooks.info

D

Index | 1143

www.it-ebooks.info

E

1144 | Index

www.it-ebooks.info

F

Index | 1145

www.it-ebooks.info

1146 | Index

www.it-ebooks.info

H

I G

Index | 1147

www.it-ebooks.info

J

K

1148 | Index

www.it-ebooks.info

L

M

Index | 1149

www.it-ebooks.info

1150 | Index

www.it-ebooks.info

N

Index | 1151

www.it-ebooks.info

O

1152 | Index

www.it-ebooks.info

P

Index | 1153

www.it-ebooks.info

1154 | Index

www.it-ebooks.info

Q

R

Index | 1155

www.it-ebooks.info

S

1156 | Index

www.it-ebooks.info

Index | 1157

www.it-ebooks.info

T

1158 | Index

www.it-ebooks.info

U

V

Index | 1159

www.it-ebooks.info

W

X Y

Z

1160 | Index

www.it-ebooks.info

About the Author

Colophon

www.it-ebooks.info

View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF