Kickstart to Python 3 : An Ultra-Rapid Programming Course 9783895764868, 9783895764875

An Ultra-Rapid Programming Course This book serves as the very first step to for novices to learn Python programming. T

339 131 9MB

English Pages 208 Year 2022

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Contents
Chapter 1 • Introduction to Python
1.1 History of Python Programming Language
1.2 Installing Python on various platforms
1.2.1 Installation on Linux
1.2.2 Installation on Windows
1.3 IDLE
1.4 Script Mode of Python
1.5 Python IDEs
1.6 Python Implementations and Distributions
1.7 Python Package Index
Summary
Chapter 2 • Built-in Data Structures
2.1 IPython
2.2 Lists
2.3 Tuples
2.4 Sets
2.5 Dictionaries
Summary
Chapter 3 • Strings, Functions, and Recursion
3.1 Strings in Python
3.2 Functions
3.3 Recursion
3.3.1 Indirect Recursion
Summary
Chapter 4 • Object-Oriented Programming
4.1 Objects and Classes
4.1.1 Everything is an Object in Python
4.2 Getting Started with Classes
4.2.1 Docstrings
4.2.2 Adding attributes to a class
4.2.3 Adding a method to the class
4.2.4 Initializer method
4.2.5 Multiline Docstrings in Python
4.3 Modules and Packages
4.3.1 Modules
4.3.2 Packages
4.4 Inheritance
4.4.2 Method Overriding
4.4.3 super()
4.5 More Inheritance
4.5.1 Multiple Inheritance
4.5.2 Method Resolution order
4.6 Abstract class and method
4.7 Access Modifiers in Python
4.8 Polymorphism
4.8.1 Method Overloading
4.8.2 Operator overloading
4.9 Syntax Errors
4.10 Exceptions
4.10.1 Handling Exceptions
4.10.2 Handling exceptIons by types
4.10.3 else block
4.10.4 Raising an exception
4.10.5 finally clause
4.10.6 User-Defined Exceptions
Summary
Chapter 5 • Data Structures
5.1 Introduction to Data Structures
5.1.1 Jupyter Notebook
5.2 Linked Lists
5.2.1 Doubly Linked List
5.3 Stack
5.4 Queue
5.4.1 Double ended queues
5.4.2 Circular Queue
Summary
Chapter 6 • Turtle Graphics
6.1 History of Turtle
6.2 Getting Started
6.3 Exploring Turtle methods
6.4 Recipes with Turtle
6.5 Visualizing Recursion
6.6 Multiple turtles
Summary
Chapter 7 • Programming animations and games
7.1 GettIng Started with Pygame
7.2 Recursion with Pygame
7.3 Sierpinski Triangle by Chaos Game
7.4 Simple animation with Pygame
7.5 Snake Game
Summary
Chapter 8 • Working with files
Handling plaintext file
8.2 CSV Files
8.3 Handling Spreadsheets
Summary
Chapter 9 • Image Processing with Python
9.1 Digital Image Processing and Wand Library
9.2 Getting Started
9.3 Image Effects
9.4 Special Effects
9.5 Transformations
9.6 Statistical Operations
9.7 Color Enhancement
9.8 Image Quantization
9.9 Threshold
9.10 Distortions
9.11 Affine Transformations and Projections
9.11.1 Arc
9.11.2 Barrel and Barrel Inverse
9.11.3 Bilinear Transformation
9.11.4 Cylinder and Planar
9.11.5 Polar and Depolar
9.11.6 Polynomial
9.11.7 Shepards
Summary
Chapter 10 • A few useful topics in Python
10.1 Command Line Arguments
10.2 Worldcloud
Summary
Conclusion
Index
Recommend Papers

Kickstart to Python 3 : An Ultra-Rapid Programming Course
 9783895764868, 9783895764875

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

books

Kickstart to Python 3 An Ultra-Rapid Programming Course

Ashwin Pajankar

Kickstart to Python 3 An Ultra-Rapid Programming Course

● Ashwin Panjakar

● This is an Elektor Publication. Elektor is the media brand of Elektor International Media B.V.

PO Box 11, NL-6114-ZG Susteren, The Netherlands Phone: +31 46 4389444

● All rights reserved. No part of this book may be reproduced in any material form, including photocopying, or

storing in any medium by electronic means and whether or not transiently or incidentally to some other use of this publication, without the written permission of the copyright holder except in accordance with the provisions of the Copyright Designs and Patents Act 1988 or under the terms of a licence issued by the Copyright Licencing Agency Ltd., 90 Tottenham Court Road, London, England W1P 9HE. Applications for the copyright holder's permission to reproduce any part of the publication should be addressed to the publishers.

● Declaration

The Author and Publisher have used their best efforts in ensuring the correctness of the information contained in this book. They do not assume, and hereby disclaim, any liability to any party for any loss or damage caused by errors or omissions in this book, whether such errors or omissions result from negligence, accident, or any other cause. All the programs given in the book are Copyright of the Author and Elektor International Media. These programs may only be used for educational purposes. Written permission from the Author or Elektor must be obtained before any of these programs can be used for commercial purposes.

● British Library Cataloguing in Publication Data

A catalogue record for this book is available from the British Library

● ISBN 978-3-89576-486-8 Print

ISBN 978-3-89576-487-5 eBook

● © Copyright 2022: Elektor International Media B.V. Prepress Production: D-Vision, Julian van den Berg

Elektor is part of EIM, the world's leading source of essential technical information and electronics products for pro engineers, electronics designers, and the companies seeking to engage them. Each day, our international team develops and delivers high-quality content - via a variety of media channels (including magazines, video, digital media, and social media) in several languages - relating to electronics design and DIY electronics. www.elektormagazine.com

●4

Contents Chapter 1 • Introduction to Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.1 History of Python Programming Language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.2 Installing Python on various platforms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2.1 Installation on Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.2.2 Installation on Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.3 IDLE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.4 Script Mode of Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.5 Python IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.6 Python Implementations and Distributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 1.7 Python Package Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 Chapter 2 • Built-in Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.1 IPython . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2 Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.3 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 2.4 Sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.5 Dictionaries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Chapter 3 • Strings, Functions, and Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.1 Strings in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.2 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.3 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.3.1 Indirect Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Chapter 4 • Object-Oriented Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.1 Objects and Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 4.1.1 Everything is an Object in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2 Getting Started with Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2.1 Docstrings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2.2 Adding attributes to a class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2.3 Adding a method to the class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 4.2.4 Initializer method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

●5

Kickstart to Python 3 4.2.5 Multiline Docstrings in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.3 Modules and Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.3.1 Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.3.2 Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.4 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.4.1 Basic Inheritance in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 4.4.2 Method Overriding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4.4.3 super() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.5 More Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 4.5.1 Multiple Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.5.2 Method Resolution order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.6 Abstract class and method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.7 Access Modifiers in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.8 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.8.1 Method Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.8.2 Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.9 Syntax Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.10 Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.10.1 Handling Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 4.10.2 Handling Exceptions by types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.10.3 else block . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 4.10.4 Raising an Exception . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 4.10.5 finally clause . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.10.6 User-Defined Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Chapter 5 • Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.1 Introduction to Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.1.1 Jupyter Notebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.2 Linked Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 5.2.1 Doubly Linked List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 5.3 Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 5.4 Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

●6

 5.4.1 Double Ended Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 5.4.2 Circular Queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Chapter 6 • Turtle Graphics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.1 History of Turtle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.3 Exploring Turtle methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 6.4 Recipes with Turtle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 6.5 Visualizing Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.6 Multiple Turtles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 Chapter 7 • Programming animations and games . . . . . . . . . . . . . . . . . . . . . . . . 112 7.1 GettIng Started with Pygame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 7.2 Recursion with Pygame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 7.3 Sierpinski Triangle by Chaos Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 7.4 Simple animation with Pygame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 7.5 Snake Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Chapter 8 • Working with files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 8.1 Handling plaintext file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 8.2 CSV Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 8.3 Handling Spreadsheets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 Chapter 9 • Image Processing with Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 9.1 Digital Image Processing and Wand Library . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 9.2 Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 9.3 Image Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 9.4 Special Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 9.5 Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 9.6 Statistical Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 9.7 Color Enhancement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

●7

Kickstart to Python 3 9.8 Image Quantization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 9.9 Threshold . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 9.10 Distortions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 9.11 Affine Transformations and Projections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 9.11.1 Arc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 9.11.2 Barrel and Barrel Inverse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 9.11.3 Bilinear Transformation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 9.11.4 Cylinder and Planar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 9.11.5 Polar and Depolar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 9.11.6 Polynomial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 9.11.7 Shepards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Chapter 10 • A few useful topics in Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 10.1 Command Line Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 10.2 Worldcloud . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207

●8

Chapter 1 • Introduction to Python

Chapter 1 • Introduction to Python I hope that you have glanced through the table of contents of the book. If not, then I request you do as it will present a fair idea to the readers about the contents of this chapter. If you are not a total beginner to Python, you may find this chapter very basic. However, if you are new to Python or computer programming, you will find this chapter very useful. We will begin our journey with small and easy steps in this chapter. We will learn the following topics in this chapter: • The history of Python Programming Language • Installing Python on various platforms • IDLE • Script Mode of Python • Python IDEs • Python Implementations and Distributions • Python Package Index After this chapter, we will be comfortable with the basics of Python and running simple Python programs.

1.1 History of Python Programming Language Python is an interpreted, high-level, and general-purpose programming language. It was created with the intention of easily readable code. Python code can be characterised by English Language-like syntax. It is easy to read and comprehend for a person who has just started learning to program. It borrows a lot of features from other programming languages. Python programming language is heavily influenced by ABC which was developed at Centrum Wiskunde & Informatica (CWI). ABC itself influenced was heavily by SETL and ALGOL 68. The principal author of Python is Guido Von Rossum. He used to work with the ABC programming language at CWI. He was given the title of Benevolent Dictator for Life (BDFL) by the Python Community. The idea of Python programming language was conceived in the late 1980s as a successor to the ABC programming language. Guido also borrowed the system of modules from Modula-3. He started implementing the language in 1989. Version 0.9.0 was published at alt.sources in February 1991. Version 1.0 was published in 1994. Python 2.0 was released in October 2000. In December 2008, a new (and backward incompatible) version of the Python programming language, known as Python 3, was released. On January 1, 2020, Python 2 support ended and is no longer developed. Now the only version is Python 3. It is under active development and supported by the Python Software Foundation. We will be using Python 3 for all demonstrations in the book. Whenever I mention Python, I mean Python 3.

●9

Kickstart to Python 3

1.2 Installing Python on various platforms In this section, we will learn how to install Python 3 on Windows and Linux. Python supports a wide variety of operating systems. However, the most commonly used Operating systems for development with Python are Windows and Linux. I will therefore explain Python 3 with these two platforms.

1.2.1 Installation on Linux Almost all major distributions of Linux come with Python 2 and 3 installed. Python 2 interpreter is the binary executable python and Python 3 interpreter is the binary executable python3. I am using a Debian Linux variant Raspberry Pi OS with a Raspberry Pi 4 as my preferred Linux platform for Python development. Open the terminal emulator of your Linux distribution and run the following command: pi@raspberrypi:~ $ python3 -V

It will show the following output,

Python 3.7.3

1.2.2 Installation on Windows It is very easy and straightforward to install Python on a Windows computer. We have to visit Python's homepage on the internet located at www.python.org. If we hover the mouse pointer over the Downloads section, the website automatically detects the Operating system and shows the appropriate download file. Click on the button saying Python 3.9.7. This version number will change in the future but the process is the same. Once you click on the button, it will download a windows executable setup file to your user's Downloads directory on your computer. It detects the architecture of your computer and downloads the relevant file. For instance, I have an x86 64-bit Windows computer. It downloaded the file python-3.9.7-amd64.exe. The screenshot of the www.python.org is shown in Figure 1-1 as follows:

● 10

Chapter 1 • Introduction to Python

Figure 1-1: Python Setup Download for Windows Now, open the setup file and it will launch an installation program. NOTE: You can open the file by clicking the downloaded file in your browser. You can also find the file's physical location by going through the downloads option in your browser. It will point to a location in the Downloads directory of your Windows user. The setup program window is shown in the screenshot in Figure 1-2 as follows:

Figure 1-2: Python Installation program Check all the checkboxes and click on the Customize installation option. It will open the next screen for the installation as shown in Figure 1-3 as follows:

● 11

Kickstart to Python 3

Figure 1-3: Options for the installation Click the Next button and it will take you to the screen shown in Figure 1-4:

Figure 1-4: Options for the installation Check all the options and click the Install button. It will then ask for administrator credentials. Enter the credentials and it will start installing Python and other relevant programs to your Windows computer. Once the installation completes, it shows the following screen (Figure 1-5):

● 12

Chapter 1 • Introduction to Python

Figure 1-5: Installation Success message Before we begin celebrating, we need to verify a few things. Open the command prompt of windows (cmd) and run the following command: C:\Users\Ashwin>python -V

The output is as follows, Python 3.9.7 Congratulations! We have installed Python 3 on your Windows computer.

1.3 IDLE Python Software Foundation has developed an integrated development environment (IDE) for Python. It is christened as 'IDLE' which stands for Integrated Development and Learning Environment. It comes with the Python setup when we install it on Windows. On Linux distributions, we have to install it separately. For Debian and derivatives, run the following command on the command prompt (Terminal emulator): pi@raspberrypi:~ $ sudo apt-get install idle -y

It will install IDLE on your Linux distribution. Let's work with IDLE. We can find it by typing IDLE in the Windows search bar. We can find it in the Linux Menu. In Linux, we can launch it from the command prompt by running the following command, pi@raspberrypi:~ $ idle &

● 13

Kickstart to Python 3

The IDLE window looks like this (Figure 1-6):

Figure 1-6: IDLE In the menubar, under the menu item Options, we can find the option Configure IDLE where we can set the font size and other details (Figure 1-7),

Figure 1-7: IDLE Configuration Options Change the font and size as per your choice and click the Apply and OK buttons respectively. Now, let's try to understand the interactive mode of Python. When invoked, the Windows IDLE shows interactive mode. We can use it to run Python statements directly without sav-

● 14

Chapter 1 • Introduction to Python

ing them as a file. The Python statements are fed directly to the interpreter and the output is immediately displayed in the same window. If you have ever worked with the command prompt of the OS then it is almost the same. Interactive mode is usually used to run single statements or short blocks of code. Let's try running a simple statement: >>> print("Hello World!")

This produces the following result and prints it in the same window: Hello World! The cursor returns to the prompt and is ready to fetch the new command by the user. This way we have run our first Python statement. We can exit the interpreter by running the command exit() in the interpreter. Alternatively, we can press CTRL + D to exit. We can also invoke the interactive mode from the command prompt by running the commands python on Windows and python3 on the Linux command prompt.

1.4 Script Mode of Python Python interactive mode is good for single-line statements and small blocks of code. However, it does not save the statements as programs. This can be done in scripting mode. In the Python interpreter of IDLE under the menu File in the menubar, choose New File. Type in the following code into this: print("Hello World!")

From the menu File in the menubar, choose Save. It will open the window Save As. Save it in the location of your choice. The IDLE automatically adds the *.py extension to the end of the filename. Then click menu Run from the menubar and click Run Module. This will execute the program and display the output in the IDLE's interactive mode window as shown below (Figure 1-8).

Figure 1-8: Python Script Output

● 15

Kickstart to Python 3

Another way to execute the program is to run it from the command prompt. To run the program in the command prompt, navigate to the directory where the program is stored and then run the following command in Windows: C:\ Python Book Project\Code\Chapter01>python prog01.py

This produces the following output:

Hello World!

In Linux, the command is as follows: pi@raspberrypi:~ $ python3 prog01.py

There is another way to run the program in Linux by running the following command: pi@raspberrypi:~ $ which python3

This returns the location of the Python 3 interpreter: /usr/bin/python3 Now let's add the following line of code to the Python code file we created: #!/usr/bin/python3

Thus the entire code file will look like below: #!/usr/bin/python3 print("Hello World!")

Now let's change the file permissions of the Python program file. Assuming that we saved the file with the name prog01.py, run the following command: pi@raspberrypi:~ $ chmod +x prog01.py

This way we have changed the permissions of the file and made it executable. We can run the file as follows: pi@raspberrypi:~ $ ./prog01.py

Note that we prefixed the filename with ./. This produces the following output:

● 16

Hello World!

Chapter 1 • Introduction to Python

We added the first line so the operating system shell knows what interpreter to use to run the program. These are a few ways we can run Python programs.

1.5 Python IDEs Until now, we have used IDLE for Python programming. We can also use other editors and IDEs. On the Linux command line, we can use editor programs like vi, vim, and nano. The vi editor comes with most Linux distributions. We can install the other two on Debian (and derivative distributions) using the following command: pi@raspberrypi:~ $ sudo apt-get install vim nano -y

We can also use a plaintext editor like Notepad on Windows or Leafpad on Linux. We can install Leafpad editor on Debian and other distributions using the following command: pi@raspberrypi:~ $ sudo apt-get install leafpad -y

Raspberry Pi OS (my preferred Debian derivative) comes with the Thonny, Geany, and Mu IDEs. We can install them on other Debian derivatives with the following command: pi@raspberrypi:~ $ sudo apt-get install thonny geany mu-editor -y

If you are more comfortable with Eclipse, there is a nice plugin known as Pydev. This can be installed from the Eclipse Marketplace.

1.6 Python Implementations and Distributions The program that interprets and runs a Python program is known as a Python interpreter. The one that comes with Linux by default and is provided by Python Software Foundation is known as CPython. Other organizations have created Python interpreters that adhere to Python standards. These interpreters are known as Python implementations. Just as C and C++ have many compilers, Python has many implementations of interpreters. Throughout the book, we will use the standard CPython interpreter that comes with Linux by default. The following is the partial list of the other popular alternative implementations of the Python interpreter: 1. 2. 3. 4. 5.

IronPython Jython PyPy Stackless Python MicroPython

Many organizations bundle the Python interpreter of their choice with many modules and libraries and distribute it. These packages are known as Python distributions. We can get a list of Python implementations and distributions at the following URLs:

● 17

Kickstart to Python 3

https://www.python.org/download/alternatives/ https://wiki.python.org/moin/PythonDistributions https://wiki.python.org/moin/PythonImplementations

1.7 Python Package Index Python comes with a lot of libraries. This is known as the "Batteries included" philosophy of Python. Many more libraries are developed by many third-party developers and organizations. Based on your work profile, you may these libraries useful in carrying out intended tasks. All such third-party libraries are hosted at the Python Package Index. This is located at https://pypi.org/. We can search for the library on this page. Python comes with the utility known as pip. Pip is a reverse acronym. It means that the expansion of the term has the term itself. Pip means pip installs packages or pip installs python. It is a package management tool that installs Python packages, coming as a command-line utility. We can check its version by running the following command on the command prompt (cmd and terminal emulator) of the operating system: pi@raspberrypi:~ $ pip3 -V

It will print the currently installed version of pip. If we wish to see the list of currently installed packages we need to run the following command: pi@raspberrypi:~ $ pip3 list

It will return the list of all the packages already installed. NOTE: All pip-related commands are the same on Windows and Linux. If we wish to install a new library, we can search for it in PyPI. We can install a new package as follows: pi@raspberrypi:~ $ pip3 install numpy

This will install the NumPy library on the computer. We will use this utility throughout the book to install required third-party packages.

Summary In this chapter, we went through the history of the Python Programming language and its installation on Windows and Linux. We also saw how to use the Python interpreter and how to write and execute Python scripts in various ways. We had an overview of IDLE and also looked at various other IDEs for Python. Finally, we learned how to use pip, the package manager of Python. The next chapter will be much more hands-on. We will learn how to write programs with built-in data structures.

● 18

Chapter 2 • Built-in Data Structures

Chapter 2 • Built-in Data Structures In the previous chapter, we installed Python 3 on various platforms. We wrote a simple introductory program and learned how to run it in various ways. We also learned how to work with the interpreter (interactive) mode. This chapter was introductory and was not very heavy on programming. This chapter is a little bit more focused on programming (also known as coding). We will be introduced to various built-in data structures in Python. We will be focusing on the following topics in detail: • IPython • List • Tuple • Set • Dictionary Following this chapter, we will be comfortable with IPython and built-in data structures in Python.

2.1 IPython IPython means Interactive Python Shell. It is a program that provides us with more facilities than python's built-in interactive shell. We have to install it separately using the following command: pi@raspberrypi:~ $ pip3 install ipython

The command is the same for Windows and macOS. If you are installing it on a Linux distribution (like me), you may see the following message in the installation log: The scripts iptest, iptest3, ipython, and ipython3 are installed in '/ home/pi/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location. This means we need to add the mentioned directory location to ~/.bashrc and ~/.bash_ profile files. We have to add the following line to both the files (so it works for login and non-login shells): PATH=$PATH:/home/pi/.local/bin

It will show a similar message for Windows too. We have to add the path of the directory mentioned in the installation log to the PATH variables (User and System variables, both) in Windows too.

● 19

Kickstart to Python 3

Once we change the path, we have to close and re-invoke the command line utilities of the operating systems. After this, run the following command: pi@raspberrypi:~$ ipython3

This will launch the IPython for Python 3 in the command prompt. The command is the same for Windows and other platforms. Figure 2-1 is the screenshot of an IPython session in progress on a Windows desktop computer:

Figure 2-1: IPython session in progress We can use IPython like this to write small programs. So, let's roll.

2.2 Lists We can store more than one value in lists. Lists are a built-in feature of Python. We do not have to install or import anything when using lists. List elements are enclosed in square brackets and separated by commas. But lists are in no way linear data structures as we can also have a list of lists. We will see more about the list of lists later when we are comfortable with the basics. Lists are mutable, meaning we can change them. Let us see a few examples of lists and associated built-in routines for the same. Open IPython on the command prompt of your operating system and follow the code: In [1]: fruits = ['babana', 'pineapple', 'orange']

This will create a list. Now, we can print it on the console in two ways: In [2]: fruits

This produces the following output:

● 20

Out[2]: ['babana', 'pineapple', 'orange']

Chapter 2 • Built-in Data Structures

We can also use the built-in print function as follows:

In [3]: print(fruits)

The following is the output:

['banana', 'pineapple', 'orange']

A list is an ordered data structure, meaning that the members of the lists are stored and retrieved in a particular order. We can use this to our advantage to retrieve the elements of the lists. The very first element has an index of 0. If the size of the list is n, the last element is indexed as n-1. This is similar to the indices of an array in C, C++, and Java. If you have programmed before with these programming languages, you will find this scheme of indexing familiar. We can retrieve the elements of the lists by using the combination of the name of the list and the index of the element. The following is an example: In [4]: fruits[0] Out[4]: 'banana' In [5]: fruits[1] Out[5]: 'pineapple' In [6]: fruits[2] Out[6]: 'orange'

We can also use negative indices. -1 refers to the last element and -2 refers to the last but one element. The following is the example: In [7]: fruits[-1] Out[7]: 'orange' In [8]: fruits[-2] Out[8]: 'pineapple'

If we try to use an invalid index, we see the following results:

In [9]: fruits[3]

--------------------------------------------------------------------------IndexError

Traceback (most recent call last)

in ----> 1 fruits[3]

IndexError: list index out of range



In [10]: fruits[-4]

--------------------------------------------------------------------------IndexError

Traceback (most recent call last)

● 21

Kickstart to Python 3

in ----> 1 fruits[-4]

IndexError: list index out of range

We can retrieve the length of the list as follows: In [12]: len(fruits) Out[12]: 3

We can also see the data type of the list as follows: In [13]: type(fruits) Out[13]: list In [14]: print(type(fruits))

As we can see in the output, the class of the variable is list. We will learn about this in detail in the fourth chapter of this book. We can use the constructor list() to create a list: In [15]: fruits = list(('banana', 'pineapple', 'orange')) In [16]: fruits Out[16]: ['banana', 'pineapple', 'orange']

We can retrieve the range of the elements from the lists follows: In [17]: SBC = ['Raspberry Pi', 'Orange Pi', 'Banana Pi', 'Banana Pro', 'NanoPi', 'Arduin ...: o Yun', 'Beaglebone'] In [18]: SBC[2:5] Out[18]: ['Banana Pi', 'Banana Pro', 'NanoPi']

In this example, we are retrieving the elements indexed with 2, 3, and 4. Also, consider the following example: In [19]: SBC[2:] Out[19]: ['Banana Pi', 'Banana Pro', 'NanoPi', 'Arduino Yun', 'Beaglebone']

This way we can retrieve the elements from index 2 and onwards. In [20]: SBC[:2] Out[20]: ['Raspberry Pi', 'Orange Pi']

● 22

Chapter 2 • Built-in Data Structures

And this way we can retrieve all elements before index 2. We can also use negative indices to retrieve multiple elements as follows: In [21]: SBC[-4:-1] Out[21]: ['Banana Pro', 'NanoPi', 'Arduino Yun']

We can also use the if construct to check if an element exists in the list as follows: In [23]: if 'Apple Pie' in SBC: ...:

print('Found')

...: else: ...:

print('Not Found')

...:

Not Found

We can change an item in the list as follows:

In [25]: SBC[0] = 'RPi 4B 8GB'

We can also insert an item into a list at an index as follows:

In [36]: SBC.insert(2, 'Test Board')

The item at index 2 in this list is shifted one position forward. The same is true for the rest of the items. We can append an item to the list as follows,

In [38]: SBC.append('Test Board 1')

This will add the item to the end of the list. We can also use extend operation with lists. This adds one list to the end of another list. In [39]: list1 = [1, 2, 3]; list2 = [4, 5, 6]; In [40]: list1.extend(list2) In [41]: list1 Out[41]: [1, 2, 3, 4, 5, 6]

We can remove an item from the list as follows:

In [43]: SBC.remove('Test Board')

● 23

Kickstart to Python 3

We can use two different approaches to remove the item at a specified index. Both the approaches are demonstrated below: In [44]: SBC.pop(0) Out[44]: 'RPi 4B 8GB' In [46]: del SBC[0]

If we specify no index, it will pop (meaning remove and return) the last item: In [47]: SBC.pop() Out[47]: 'Test Board 1'

We can remove all elements from the list as follows: In [48]: SBC.clear()

We can also delete an entire list as follows, In [49]: del SBC

If we try to access the list now, it will return an error as follows: In [50]: SBC --------------------------------------------------------------------------NameError

Traceback (most recent call last)

in ----> 1 SBC NameError: name 'SBC' is not defined

We will now understand how to use the lists with loops. Create a list as follows: In [51]: fruits = ['apple', 'banana', 'cherry', 'pineapple', 'watermelon', 'papaya']

We can use the for loop construct as follows, In [52]: for member in fruits: ...: ...: apple banana cherry pineapple watermelon papaya

● 24

print(member)

Chapter 2 • Built-in Data Structures

The following code also produces the same result: In [53]: for i in range(len(fruits)): ...:

print(fruits[i])

...: apple banana cherry pineapple watermelon papaya

We can also use the while loop as follows: In [54]: i = 0 In [55]: while i < len(fruits): ...:

print(fruits[i])

...:

i = i + 1

...: apple banana cherry pineapple watermelon papaya

Before proceeding further, I want to cover an important feature. We have worked with examples of many lists. Most of the lists we worked with are lists of character strings. A couple are lists of numbers. We can also have lists of other data types. The following are examples: In [56]: l1 = [1.2, 2.3, 3.4] In [57]: l2 = ['a', 'b', 'c'] In [58]: l3 = [True, False, False, True, True, False]

Here we created lists of floats, characters, and Boolean values respectively. We can also create a list of mixed types as follows: In [59]: l4 = [1, 'Test', 'a', 1.2, True, False]

● 25

Kickstart to Python 3

We can sort a list as follows: In [60]: fruits.sort() In [61]: fruits Out[61]: ['apple', 'banana', 'cherry', 'papaya', 'pineapple', 'watermelon']

We can also sort a list in reverse order as follows: In [62]: fruits.sort(reverse = True) In [63]: fruits Out[63]: ['watermelon', 'pineapple', 'papaya', 'cherry', 'banana', 'apple']

As an exercise, sort numeric and boolean lists. We can copy a list into another as follows:

In [64]: newlist = fruits.copy()

We saw earlier that the routine extend() can join two lists. We can use the addition (+) operator to join two lists as follows: In [65]: l1 + l2 Out[65]: [1.2, 2.3, 3.4, 'a', 'b', 'c']

We can use the multiplication operator with lists as follows, In [66]: l1 * 3 Out[66]: [1.2, 2.3, 3.4, 1.2, 2.3, 3.4, 1.2, 2.3, 3.4]

2.3 Tuples Tuples are similar to lists. We have to use brackets while creating them. Their difference with lists is that they are immutable, meaning once they are created, they cannot be modified. Let's see a simple example as follows, In [1]: fruits = ('apple', 'grape', 'mango') In [2]: fruits Out[2]: ('apple', 'grape', 'mango') In [3]: print(type(fruits))

The indexing, looping, and concatenation (the + operator) for tuples are the same as lists. As tuples are immutable, we cannot directly change any information stored in the tuples. However, we can convert them to lists and then assign the changed list to any tuple. See the following example:

● 26

Chapter 2 • Built-in Data Structures

In [4]: temp_list = list(fruits) In [5]: temp_list.append('papaya') In [6]: fruits = tuple(temp_list) In [7]: fruits Out[7]: ('apple', 'grape', 'mango', 'papaya')

In the example above, we demonstrated the usage of the routine tuple(). This way, we can use all the routines of lists cleverly to work with tuples. Let's see the demonstration of the method count() to count how many times a particular member element occurs in the tuple: In [8]: test_tuple = (2, 3, 1, 3, 1, 4, 5, 6, 3, 6) In [9]: x = test_tuple.count(3) In [10]: print(x) 3

2.4 Sets Lists and tuples are ordered data structures and both allow duplicate values. Sets are different than both as they are unordered and hence do not allow duplicate values. Sets are defined using curly brackets. The following is an example of simple sets: In [12]: set1 = {'apple', 'banana', 'orange'} In [13]: set1 Out[13]: {'apple', 'banana', 'orange'} In [14]: set2 = set(('apple', 'banana', 'orange')) In [15]: set2 Out[15]: {'apple', 'banana', 'orange'} In [16]: print(type(set1))

We cannot use indexes to retrieve the elements of any set, as sets are unordered. But we can use loop constructs for and while. Try this as an exercise. We can add new items with the routine add() as follows: In [17]: set1 Out[17]: {'apple', 'banana', 'orange'} In [18]: set1.add('pineapple') In [19]: set1 Out[19]: {'apple', 'banana', 'orange', 'pineapple'}

● 27

Kickstart to Python 3

We can use the routines remove() or discard() to remove an item from any list as follows: In [20]: set1.remove('banana') In [21]: set1.discard('apple')

Both the routines raise errors if we try to remove non-existent items. Let's see a few set methods. First, we will see how to compute the union of two sets. Let's create sets for this: In [22]: set1 = {1, 2, 3, 4, 5} In [23]: set2 = {3, 4, 5, 6, 7} In [24]: set3 = set1.union(set2) In [25]: set3 Out[25]: {1, 2, 3, 4, 5, 6, 7}

Here, we store the union in a new set. A slight alternate approach stores the union in the first set as follows, In [29]: set1.update(set2) In [30]: set1 Out[30]: {1, 2, 3, 4, 5, 6, 7}

We can also remove all elements in a set as follows: In [31]: set3.clear()

The routine copy() works in the similar way to list. Let's compute the difference as follows: In [32]: set3 = set1.difference(set2) In [33]: set3 Out[33]: {1, 2}

This example returns a new output set. We can remove the matching elements from one of the sets using this: In [34]: set1.difference_update(set2) In [35]: set1 Out[35]: {1, 2}

We can compute intersection as follows: In [37]: set3 = set1.intersection(set2) In [38]: set3 Out[38]: {3, 4, 5}

● 28

Chapter 2 • Built-in Data Structures

We can check if a set is a subset of another set as follows: In [39]: set2 = {1, 2, 3, 4, 5, 6, 7, 8} In [40]: set1.issubset(set2) Out[40]: True

Similarly, we can check if a set is a superset of another set: In [41]: set2.issuperset(set1) Out[41]: True

We can also check if two sets are disjoint (do not have any common elements) as follows: In [42]: set1 = {1, 2, 3} In [43]: set2 = {4, 5, 6} In [44]: set1.isdisjoint(set2) Out[44]: True

We can compute the symmetric difference between two sets as follows: In [45]: set1 = {1, 2, 3} In [46]: set2 = {2, 3, 4} In [47]: set3 = set1.symmetric_difference(set2) In [48]: set3 Out[48]: {1, 4}

We can also compute union and intersection with operators | and s as follows: In [49]: set1 | set2 Out[49]: {1, 2, 3, 4} In [50]: set1 & set2 Out[50]: {2, 3}

2.5 Dictionaries Dictionaries are ordered, changeable, and do not allow duplicates. Dictionaries in Python 3.6 are unordered. Dictionaries from Python 3.7 are ordered. The items are stored in dictionary in key:value pairs and can be referred to by using the name of the key. Let's create a simple dictionary as follows: In [52]: test_dict = { "fruit": "mango", "colors": ["red", "green", "yellow"]}

We can access the items of the dictionary using the keys as follows: In [53]: test_dict["fruit"] Out[53]: 'mango'

● 29

Kickstart to Python 3

In [54]: test_dict["colors"] Out[54]: ['red', 'green', 'yellow']

We can retrieve keys and values as follows: In [55]: test_dict.keys() Out[55]: dict_keys(['fruit', 'colors']) In [56]: test_dict.values() Out[56]: dict_values(['mango', ['red', 'green', 'yellow']])

We can update a value as follows: In [60]: test_dict.update({"fruit": "grapes"}) In [61]: test_dict Out[61]: {'fruit': 'grapes', 'colors': ['red', 'green', 'yellow']}

We can add into the dictionary as follows: In [62]: test_dict["taste"] = ["sweet", "sour"] In [63]: test_dict Out[63]: {'fruit': 'grapes', 'colors': ['red', 'green', 'yellow'], 'taste': ['sweet', 'sour']}

We can also pop items: In [64]: test_dict.pop("colors") Out[64]: ['red', 'green', 'yellow'] In [65]: test_dict Out[65]: {'fruit': 'grapes', 'taste': ['sweet', 'sour']}

We can also pop the last inserted item: In [66]: test_dict.popitem() Out[66]: ('taste', ['sweet', 'sour'])

We can also delete an item, In [67]: del test_dict["fruit"] In [68]: test_dict Out[68]: {}

● 30

Chapter 2 • Built-in Data Structures

We can also delete a dictionary as follows: In [69]: del test_dict In [70]: test_dict --------------------------------------------------------------------------NameError

Traceback (most recent call last)

in ----> 1 test_dict NameError: name 'test_dict' is not defined

We can loop through dictionaries using for and while loop constructs. Try doing this as an exercise.

Summary In this chapter, we learned the basics of sets, tuples, lists, and dictionaries in Python. Together, these are known as collections in Python. In the next chapter, we will learn the basics of strings, functions, and recursion.

● 31

Kickstart to Python 3

Chapter 3 • Strings, Functions, and Recursion In the previous chapter, we explored python collections which included lists, tuples, sets, and dictionaries. We also started writing small snippets of code. We have learned a lot of built-in functions for the collections. We also explored the IPython console. In this chapter, we will dive deeper into Python programming. We will also start using IDLE. The following is the list of topics we will be covering in this chapter: • Strings in Python • Functions • Recursion • Direct and Indirect Recursion After completing this chapter, you should be comfortable with the concepts and programming of strings and functions in Python.

3.1 Strings in Python In earlier chapters, while working with the routine print() and collections (lists, tuples, sets, and dictionaries), we worked extensively with strings. I did not mention this because I wished to cover it in a separate chapter as it is important to understand it from the very basics. In this section, we will take a detailed tour of strings in Python: We will learn the basics and associated routines. Let's open IPython in the command prompt and start learning. We can create a string by using a pair of single or double-quotes. The following are examples: In [1]: 'Python' Out[1]: 'Python' In [2]: "Python" Out[2]: 'Python'

As we can see, we can use a pair of single or double quotes but not a combination of both. The following is the demonstration: In [3]: 'Python" File "", line 1 'Python" ^ SyntaxError: EOL while scanning string literal

The data on the strings is stored in the same way whether we use a pair of single or double-quotes. The following demonstration compares strings created using a pair of single and double quotes:

● 32

Chapter 3 • Strings, Functions, and Recursion

In [4]: 'Python' == "Python" Out[4]: True

As we can see, the output is the Boolean value True. This means both strings are equal. The data within the quotes is case-sensitive. The following demonstration explains it well: In [5]: 'Python' == "python" Out[5]: False

This returned False as both the strings are not equal. Notice the first characters in both strings. Their cases do not match. This is why the comparison returns the Boolean value False. We have already seen how to print strings. Let's see this once more. Also, we can store a string into a variable and print it. The following is the demonstration: In [6]: print('Python') Python In [7]: var1 = 'Python' In [8]: print(var1) Python

We can also have multiline strings. The following are examples: In [9]: var2 = '''test string, ...: test string''' In [10]: var3 = """test string, ...: test string"""

We can consider strings as arrays. The following is an example: In [13]: var1[0] Out[13]: 'H' In [14]: var1[1] Out[14]: 'e' In [15]: var1[2] Out[15]: 'l' In [16]: var1[3] Out[16]: 'l' In [17]: var1[4] Out[17]: 'o'

We can also compute the length of the string: In [20]: len(var1) Out[20]: 5

● 33

Kickstart to Python 3

We can use loops to access the strings: In [19]: for x in var1: ...:

print(x)

...: H e l l o

C and C++ have a data type known as character. Character holds a single ASCII character. Python does not have a character data type. It treats a single character as a string of one size. We can also check if a string is a substring of another string. The following is an example: In [21]: 'test' in 'This is a test.' Out[21]: True In [22]: 'test' not in 'This is a test.' Out[22]: False

We can also slice the strings just as lists in Python: In [23]: var1 = 'This is a test.' In [24]: var1[2:] Out[24]: 'is is a test.' In [25]: var1[:2] Out[25]: 'Th' In [26]: var1[2:7] Out[26]: 'is is'

We can also use negative indices: In [27]: var1[-2:] Out[27]: 't.' In [28]: var1[:-2] Out[28]: 'This is a tes' In [29]: var1[-4:-2] Out[29]: 'es'

We can change the case of alphabetical characters in the string: In [30]: var1 = 'MiXed CaSe' In [31]: var1.upper() Out[31]: 'MIXED CASE'

● 34

Chapter 3 • Strings, Functions, and Recursion

In [32]: var1.lower() Out[32]: 'mixed case'

We can remove unwanted whitespaces from both the ends of the strings: In [33]: var1 = ' whitespace

'

In [34]: var1.strip() Out[34]: 'whitespace'

We can also replace characters in a string: In [35]: var1 = 'love' In [36]: var1.replace("o", "0") Out[36]: 'l0ve'

We can split a string around a character as follows: In [37]: var1 = 'a,b,c,d,e,f' In [38]: var1.split(',') Out[38]: ['a', 'b', 'c', 'd', 'e', 'f']

We can also capitalize the first character in a string: In [39]: var1 = 'hello!' In [40]: var1.capitalize() Out[40]: 'Hello!'

We can concatenate two strings: In [41]: var1 = "Hello, " In [42]: var2 = "World!" In [44]: var1 + var2 Out[44]: 'Hello, World!'

We cannot concatenate numbers with strings. It will return an error as follows: In [45]: age = 35 In [46]: var1 = 'I am ' In [47]: var1 + age ------------------------------------------------------------------------TypeError

Traceback (most recent call last)

in ----> 1 var1 + age TypeError: can only concatenate str (not "int") to str

● 35

Kickstart to Python 3

We have to convert the numbers to strings and then concatenate them with strings: In [48]: var1 + str(age) Out[48]: 'I am 35'

We can also format a string: In [49]: var1 = 'I am {}'.format(35) In [50]: var1 Out[50]: 'I am 35'

Here is another example: In [51]: var1 = 'I am {} and my brother is {}'.format(35, 30) In [52]: var1 Out[52]: 'I am 35 and my brother is 30'

Let's see how to use escape characters in a string. Consider the following example where we have to use double quotes within double quotes: In [53]: var1 = "He said, "I am fine"." File "", line 1 var1 = "He said, "I am fine"." ^ SyntaxError: invalid syntax

It throws an error because syntactically it is wrong. We can use escape character \ for using double quotes as follows: In [54]: var1 = "He said, \"I am fine\"." In [55]: var1 Out[55]: 'He said, "I am fine".'

3.2 Functions Almost all high-level programming languages have the provision of reusable blocks of codes. These are known as routines, subroutines, or functions. A function is a named block of code that can be called from another block of code. In this section, we will learn functions in detail. Also, until now, we have been using the IPython console. Now we will start saving the programs in scripts. Consider the following simple program demonstrating a simple function: prog00.py def func1(): print('Test')

● 36

Chapter 3 • Strings, Functions, and Recursion

func1()

In the example above we create a simple function (in the first two lines) that prints a string when called. In the last, we call the function. When called the function performs the intended action. Run the code and see the output. We can also call this function multiple times: prog00.py def func1(): print('Test') func1() func1()

Here, we are calling the function twice. It will print the message twice in the console. Now, let's see a more pythonic way of defining and calling functions. Take a look at the following code: prog01.py def func1(): print('Test') def main(): print("This is the main() function block...") func1() if __name__ == "__main__": main()

This code is written in a pythonic way. First, we define two functions. The second function named main() is intended to serve as analogous to the main function we write in C or C++ programs. We can assign any valid name of our choice to this function (as an exercise, you may wish to change the name of the function in the declaration and the function call). Then the line if __name__ == "__main__": checks if we are directly executing the module or calling it from another Python program. If we execute it directly, it will run the code listed under it. We will learn more about this feature when we study packages in the next chapter. Run the code and see the output. This is a professional and pythonic way of organizing our Python code files. We will be using this template for our programs quite often throughout the book. Let's see how we can pass values to functions. In the function definition, we have to declare a placeholder variable for the value to be passed. It is known as a parameter. The actual value to be passed is known as an argument. Let's see this in action.

● 37

Kickstart to Python 3

prog02.py def print_msg(msg): print(msg) def main(): print_msg("Test String...")

if __name__ == "__main__": main()

We can also have multiple arguments: prog03.py def print_msg(msg, count): for i in range(count): print(msg) def main(): print_msg("Test String...", 5) if __name__ == "__main__": main()

In this example, we accept two arguments. The first is the message and the second is the number of times the message is to be printed. We can also have a default value of the argument as shown below: prog04.py def print_msg(msg, count=2): for i in range(count): print(msg) def main(): print_msg("Test String...")

if __name__ == "__main__": main()

In the example code above, we are setting the default value of the last argument to 2. When we do not pass the argument in the function call, it assumes the default value and proceeds accordingly. We can also have a function to return a value. Let's define a function that accepts two values and returns their sum. It's easy to code:

● 38

Chapter 3 • Strings, Functions, and Recursion

prog05.py def add(a, b): return a + b def main(): print(add( 3, 5))

if __name__ == "__main__": main()

We can also write a function that returns multiple values: prog06.py def compute(a, b): return (a + b, a-b) def main(): (r1, r2) = compute( 3, 5) print(r1) print(r2) if __name__ == "__main__": main()

As we can see in the example above, we can pack the return values in a single tuple and return the tuple.

3.3 Recursion

We can call tthe instance of the function from within the same function. This is known as recursion. Let's rewrite an earlier example to accommodate recursion as follows: prog07.py def print_msg(msg, count): if count != 0: print(msg) print_msg(msg, count-1) def main(): print_msg("Test String...", 6) if __name__ == "__main__": main()

● 39

Kickstart to Python 3

In recursion, there are two main things. The first is the condition for the termination. In the absence of this, the recursion will run forever. And the second part is the recursive call where the function calls itself. In the above program, we are using the if condition termination. Most of the recursion examples use an if condition for termination. The if statement will have the condition or the criteria for termination. Let's write a program for the computation of the factorial of a positive integer: prog08.py def factorial(n): if n == 1: return n else: return n*factorial(n-1) def main(): print(factorial(5)) if __name__ == "__main__": main()

We can also write a program for computing the nth number in the Fibonacci series: prog09.py def fibo(n): if n >> a = 5 >>> print (a) 5 >>> print(type(a))

When I say Almost everything is an object in Python, I mean it. For example, the routine print() is a built-in method. We can print its type by running the following statement on the interactive prompt: >>> print(type(print))

We can also see the documentation of the routine print() by running the following statement: >>> print(print.__doc__) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

Prints the values to a stream, or sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space.

● 43

Kickstart to Python 3

end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.

4.2 Getting Started with Classes In the last section, we studied the basics of classes. In this section, we will see how to create inline documentation in the Python code file with docstrings. We will also study the members of a class in detail. We will use the same Point class for the demonstration. We will keep adding more features to the same code file.

4.2.1 Docstrings We can provide inline documentation to Python code with Docstrings. Python docstrings stand for Python Documentation Strings. They are the most convenient way to provide documentation for developers. The following is a simple docstring example: prog00.py 'This is the DocString example' class Point: 'This class represent the data-structure Point' pass p1 = Point() print(p1) print(p1.__doc__) print(help(p1))

Run this code (prog01.py) as see the output. As we discussed earlier and in this example, we can use the built-in routine __doc__ to print docstrings if available. We can also use the built-in routine help() to display the docstrings.

4.2.2 Adding attributes to a class If you are familiar with Java or C++, we declare a class variable in the definition of the class. In Python, there is no such thing as the variable declaration. We can add the attributes to the class Point by adding a variable for each co-ordinate as follows: prog01.py 'This is the DocString example' class Point: 'This class represent the data-structure Point' pass p1 = Point() p1.x = 1 p1.y = 2 p1.z = 2

● 44

Chapter 4 • Object-Oriented Programming

print(p1.x, p1.y, p1.z)

Run the program and see the output.

4.2.3 Adding a method to the class We can add behaviors to the class by adding methods to the class. The following is an example of the class Point with custom methods assign() and printPoint(): prog01.py 'This is the DocString example' class Point: 'This class represent the data-structure Point' def assign(self, x, y, z): 'This assigns the value to the co-ordinates' self.x = x self.y = y self.z = z def printPoint(self): 'This prints the values 0f co-ordinates' print(self.x, self.y, self.z) p1 = Point() p1.assign(0, 0, 0) p1.printPoint()

Run the code above and check the output. As we can see, the method declaration is not much different from a function declaration. The main difference is that it is mandatory to have a self-reference parameter. In this case, it is named as self in both the methods. We can name it anything. However, I am yet to see any other name for the variable. The other difference between a function and a method is that a method is always associated with a class. In the code above, the method assign() assigns values to the coordinates, and the method printPoint() prints the values of coordinates of the point.

4.2.4 Initializer method There is a special Python method for initializing objects: __init__. We can use this to assign values to the attributes of an object at the time of the object creation itself. The following is an example of this: prog01.py 'This is the DocString example' class Point: 'This class represent the data-structure Point' def __init__(self, x, y, z):

● 45

Kickstart to Python 3

'The initializer' self.assign(x, y, z) def assign(self, x, y, z): 'This assigns the value to the co-ordinates' self.x = x self.y = y self.z = z def printPoint(self): 'This prints the values 0f co-ordinates' print(self.x, self.y, self.z) p1 = Point(0, 1, 2) p1.printPoint()

Run the program above and see the output.

4.2.5 Multiline Docstrings in Python Let's see an example of multiline docstrings in Python. Multiline docstrings are used to spread the docstrings over multiple lines. The following is an example of a multiline docstring: prog01.py 'This is the DocString example' class Point: """This class represent the data-structure Point This is an example of the multiline docstring... """ def __init__(self, x, y, z): '''The initializer --This initializes the object with the passed arguments ''' self.assign(x, y, z) def assign(self, x, y, z): 'This assigns the value to the co-ordinates' self.x = x self.y = y self.z = z def printPoint(self): 'This prints the values 0f co-ordinates' print(self.x, self.y, self.z)

● 46

Chapter 4 • Object-Oriented Programming

p1 = Point(0, 1, 2) p1.printPoint()

Run the program and see the output.

4.3 Modules and Packages In this section, we will begin with the concept of Modularity in Python. We will take a look at modules and packages. We will also write programs to demonstrate these concepts.

4.3.1 Modules In the earlier section, we demonstrated examples of the concept of Class in Python. We had a class named Point in the python code file prog02.py. We can also have multiple classes in a single python code file. Consider the following program: prog02.py class Class01: def __init__(self): print("Just created an object for Class01...")

class Class02: def __init__(self): print("Just created an object for Class02...")

o1 = Class01() o2 = Class02()

In the above example, we are defining two classes Class01 and Class02 in a single python code file. Python code files are also known as python modules. Suppose you have a directory where you have created a bunch of python code files, we can just refer to each code file as a module. Run the example above and see the output. Also, classes and functions in a python module can be accessed in another module. This is known as modularity. We can group the related classes in a module and import them when required in other modules. To see this in action, create another python module prog03 in the same directory. The following is the code for it: prog03.py import prog02 o1 = prog02.Class01() o2 = prog02.Class02()

● 47

Kickstart to Python 3

When we execute the module prog03, we get the following output: Just Just Just Just

created created created created

an an an an

object object object object

for for for for

Class01... Class02... Class01... Class02...

Can you guess why this is happening?? It is printing the statements twice because we're importing the entire module. So, it's importing the part from prog02 where we're creating the objects. Hence, the objects are created twice. To mitigate this, Python provides an efficient approach. Make the following changes to prog02.py: prog02.py class Class01: def __init__(self): print("Just created an object for Class01...")

class Class02: def __init__(self): print("Just created an object for Class02...") if __name__ == "__main__": o1 = Class01() o2 = Class02()

Writing our main code under the block starting with if __name__ == "__main__": ensures it is called only when the module is executed directly. When we import the entire module in another module, the code with the main logic is not imported to the other module. Only functions and classes are imported to the other module. If you run both the modules one by one, you'll see that objects are created only once during each run. But what if we want to import and run the main logic code of the module on-demand in the module where it's imported? There is a more Pythonic way to organize our code to make this possible. Rewrite the prog02 module as follows: prog02.py class Class01: def __init__(self): print("Just created an object for Class01...")

class Class02:

● 48

Chapter 4 • Object-Oriented Programming

def __init__(self): print("Just created an object for Class02...") def main(): o1 = Class01() o2 = Class02() if __name__ == "__main__": main()

Also, make changes to the prog02 module as follows: prog03.py import prog02 prog01.main()

In the module prog03, we're directly importing the main() function of the prog02 module. Now, execute the module prog03 to see the output. It will be the same. However, the prog02 module is now more organized than before. Let's make the same changes to the prog03 code as follows: prog03.py import prog02

def main(): prog02.main()

if __name__ == "__main__": main()

Run the code again to see the output. Finally, to add more clarity, we will modify prog02 as follows: prog02.py class Class01: def __init__(self): print("Just created an object for Class01...")

class Class02: def __init__(self): print("Just created an object for Class02...")

● 49

Kickstart to Python 3

def main(): o1 = Class01() o2 = Class02() if __name__ == "__main__": print("Module prog01 is being run directly...") main() else: print("Module prog02 has been imported in the current module...")

Run both the modules and see the output. What happened here?? We notice that the code before the else statement is executed if we directly run the module and the code after the else statement is executed when we import an entire module. Let's move on to understand the other way to import the members of a module. In the earliest version of module prog03 we had the following code, prog03.py import prog02 o1 = prog01.Class01() o2 = prog01.Class02()

Here, we addressed the members of module prog02 with prog2.member notation. This is because the statement import prog02 imports all members to the prog03 module. There is another way to import the members of modules. The following is an example of this: prog04.py from prog02 import Class01, Class02 def main(): o1 = Class01() o2 = Class02() if __name__ == "__main__": main()

With from import syntax, we do not have to use the . convention in the calling module. We can directly address the members of an imported module as shown in the above example. Run the program and see the output. In the program above we're importing both members of prog02. Modify the program above to import a single member of the prog02 module. Also, add the else part after the call to the main() function.

● 50

Chapter 4 • Object-Oriented Programming

4.3.2 Packages Up until now, we have seen how to organize the functions and the classes in Python in separate modules. We can organize the modules into packages. Let's create a package of the code we have already written. Create a subdirectory in the directory where you're saving all the python modules. Name the subdirectory mypackage. Move the module prog02 into the directory mypackage. Create an empty file __init__.py and save it inside the mypackage directory. We just created our very own Python package! That's correct. Creating a package is easy. We just need to put the modules in a directory and create an empty __init__.py file in the same directory. The name of the directory containing the modules is the package name. Now, in the original parent directory, create a python module prog05 as follows: prog05.py import mypackage.prog02 def main(): mypackage.prog02.main() if __name__ == "__main__": main()

Now run the prog05 module and see the output. Notice we are using .. notation to access a member of a package. We do not have to do this if we use from import syntax for importing. As an exercise, write the program by importing the prog02 modules with the syntax from prog02 import Class01, Class02 and run the code. We can even have a package within a package. To do this we just have to create another directory within the directory mypackage and add the desired module file and an empty __init__.py file to it. As an exercise, create a subpackage in mypackage. Import and use it in a module in the parent directory.

4.4 Inheritance Modern programming languages have different mechanisms for code reuse. Procedures give an excellent way to reuse code. Functions and class methods in Python are great examples of how code re-usability can be achieved by procedure. Object-oriented programming languages like Python also have another approach for code reuse. This is known as Inheritance. Python has a mechanism for Class-based Inheritance. A class in Python can inherit the attributes and the behaviors of the other class. The base class from which we derive other class(es) is also known as Parent Class or SuperClass. The class which inherits (or derives or extends from, keep in mind that these terms are used interchangeably in the world of object-oriented programming) the attributes and the behaviors is known as Child class or Sub Class.

● 51

Kickstart to Python 3

4.4.1 Basic Inheritance in Python Technically all Python classes are subclasses of a specific built-in class known as object. This mechanism allows Python to treat objects alike. So, if you are creating any class in Python, it means that you're using inheritance implicitly. It's implicit because you do not have to make any special provision in the code for it. You can also explicitly derive your custom class from the built-in class object as follows: prog01.py class MyClass(object): pass

The code above has the same meaning and functionality: prog01.py class MyClass: pass

Let's define a custom class and derive another class from it: prog02.py class Person: pass class Employee: pass def main(): print(issubclass(Employee, Person)) if __name__ == "__main__": main()

Run the above code. The routine issubclass() returns true if the class mentioned in the first argument is a subclass of the class mentioned in the second argument. The above program prints false. OOPS! We forgot to derive the class Employee from Person. For this modify the code as follows: prog02.py class Person: pass class Employee(Person): pass def main(): print(issubclass(Employee, Person)) print(issubclass(Person, object)) print(issubclass(Employee, object)) if __name__ == "__main__": main()

● 52

Chapter 4 • Object-Oriented Programming

You must have noticed that we added two more statements in the section main(). Run the code. It must print True three times.

4.4.2 Method Overriding Let's add more functionality to the class Person. Modify the module prog01.py: prog01.py class Person: def __init__(self, first, last, age): self.firstname = first self.lastname = last self.age = age def __str__(self): return self.firstname + " " + self.lastname + ", " + str(self.age) class Employee(Person): pass def main(): x = Person("Ashwin", "Pajankar", 31) print(x) if __name__ == "__main__": main()

We want to define the behavior of the class Employee. As we have derived it from Person, we can reuse the methods in the class Person to define the behavior of the class Employee: prog02.py class Person: def __init__(self, first, last, age): self.firstname = first self.lastname = last self.age = age def __str__(self): return self.firstname + " " + self.lastname + ", " + str(self.age) class Employee(Person): pass def main(): x = Person("Ashwin", "Pajankar", 31) print(x) y = Employee("James", "Bond", 32) print(y) if __name__ == "__main__": main()

● 53

Kickstart to Python 3

As you can see, the class Employee inherits the initializer and method __str__() from class Person. However, we wish to add another additional attribute called empno to the class Employee. We also have to change the behavior of the method __str__() accordingly. The following code does this: prog02.py class Person: def __init__(self, first, last, age): self.firstname = first self.lastname = last self.age = age def __str__(self): return (self.firstname + " " + self.lastname + ", " + str(self.age)) class Employee(Person): def __init__(self, first, last, age, empno): self.firstname = first self.lastname = last self.age = age self.empno = empno def __str__(self): return (self.firstname + " " + self.lastname + ", " + str(self.age) + ", " + str(self.empno)) def main(): x = Person("Ashwin", "Pajankar", 31) print(x) y = Employee("James", "Bond", 32, 0x007) print(y) if __name__ == "__main__": main()

In the code above, we are overriding the definitions of the methods __init__() and __str__() from the class Person in Employee to suit our needs. Run the code and see the output. This way we can override any method of the base class in the subclass.

4.4.3 super() In the last example, we saw how to override the base class's methods in the subclass. There is another way of doing this. See the below code example: class Person: def __init__(self, first, last, age): self.firstname = first self.lastname = last

● 54

Chapter 4 • Object-Oriented Programming

self.age = age def __str__(self): return (self.firstname + " " + self.lastname + ", " + str(self.age))

class Employee(Person): def __init__(self, first, last, age, empno): super().__init__(first, last, age) self.empno = empno def __str__(self): return (super().__str__() + ", " + str(self.empno)) def main(): x = Person("Ashwin", "Pajankar", 31) print(x) y = Employee("James", "Bond", 32, 0x007) print(y) if __name__ == "__main__": main()

As you can see, we have used the super() method. This is a special method that returns the object as an instance of the base class. We can use super() in any method. As super returns the object as an instance of the base class, the code super(). calls the respective method of the base class. The method super() can be used inside any method of a subclass to invoke an instance of the parent class as an object. It does not have to be called in the first line of the subclass method definition. It can be called at any place in the body. Thus, we can also re-write the method __init__() of Employee as follows, def __init__(self, first, last, age, empno): self.empno = empno super().__init__(first, last, age)

4.5 More Inheritance In the last section we studied basic inheritance, overriding, and the method super(). In this section, we will study multiple inheritance, the diamond problem, access modifiers, and abstract classes and methods.

● 55

Kickstart to Python 3

4.5.1 Multiple Inheritance When a class is derived from more than one class, the mechanism is known as Multiple Inheritance. The following diagram depicts this:

Figure 4-1: Multiple Inheritance Let's try writing some simple code for the same: prog01.py class A: pass

class B: pass

class C(A, B): pass

def main(): pass

if __name__ == "__main__": main()

Multiple inheritance is used in cases where we need attributes and behaviors of more than one class to be derived in a single class.

4.5.2 Method Resolution order Consider the following example:

● 56

Chapter 4 • Object-Oriented Programming

prog02.py class A: def m(self): print('This is m() from Class A.') class B: def m(self): print('This is m() from Class B.') class C(A, B): pass def main(): x = C() x.m() if __name__ == "__main__": main()

When we run the code, the output is as follows: This is m() from Class A.

This is because of the order in which class C is derived from the parent classes. If we change the class C code as follows: class C(B, A): pass

The output is: This is m() from Class B.

The mechanism with which the derived methods of a subclass are resolved is known as Method Resolution Order.

4.6 Abstract class and method A class with an object that is not created is known as an abstract class. Also, a method that is declared without implementation is known as an abstract method. An abstract class may or may not have an abstract method. In Python, we explicitly declare a class and a method as abstract. An explicitly declared abstract class can only be subclassed. In many situations, we need to have a class and methods only to be derived and overridden respectively. The following is a perfect real-life example: prog03.py from abc import ABC, abstractmethod class Animal(ABC): @abstractmethod def move(self): pass

● 57

Kickstart to Python 3

class Human(Animal): def move(self): print("I Walk.") class Snake(Animal): def move(self): print("I Crawl.") def main(): a = Human() b = Snake() a.move() b.move() if __name__ == "__main__": main()

To make a class abstract explicit, we need to derive it from a built-in class ABC. If we want to make a class methods abstract explicitly, we have to use the decorator @abstractmethod with the method to be made abstract. Run the code above and see the output.

4.7 Access Modifiers in Python In many programming languages like C++ and Java, there is the concept called Access Control for the members of classes. In Python, there is no strict measure for controlling the access of a member from outside the class. If you do not want a method or class variable to be accessed from outside, you can mention this in the docstrings of the class. The other way to let others know not to access a variable or a method made for internal use is to prefix the variable with an underscore. The other person who is modifying the code or using it by importing it will understand that the variable or method is for internal use only. However, (s)he still can access it from outside. Another way to strongly suggest to others not to access the variable or method from the outside is to use the mechanism of name mangling. To do this, we have to prefix the method or variable with a double underscore. Then it can only be accessed with a special syntax which is demonstrated in the below program: prog04.py class A: def __init__(self): self.a = "Public" self._b = "Internal use" self.__c = "Name Mangling in action" def main(): x = A() print(x.a) print(x._b) print(x.__c) if __name__ == "__main__": main()

● 58

Chapter 4 • Object-Oriented Programming

The output displays the values for the first two attributes. For the third attribute, it displays an error that contains the message: AttributeError: 'A' object has no attribute '__c' To see the value of the attribute __c, make the following changes in the last print(): print(x._A__c)

The output is as follows: Public Internal use Name Mangling in action

4.8 Polymorphism In the last section, we studied advanced inheritance and access modifiers in Python. In this section, we will study polymorphism. Polymorphism means the ability to assume various forms. In terms of programming languages, it refers to the provision of a single interface to entities of different types. Most object-oriented programming languages allow various degrees of polymorphism. If you have worked with the programming languages like C++ or Java, then you must have a fair idea of this. We studied Overriding in the earlier section. It is a form of Polymorphism. So, we already studied a type of Polymorphism in Python 3. In this section, we will first study Method Overloading and then Operator Overloading which falls under the concept of Polymorphism.

4.8.1 Method Overloading When a method can be invoked with a different number of arguments, it is known as method overloading. In programming languages like C++, we can have multiple definitions of member functions of a class. However, Python does not allow this as we know that Everything is an object in Python. To make this possible, we use methods with default arguments. The example is as follows: prog01.py class A: def method01(self, i=None): if i is None: print("Sequence 01") else: print("Sequence 02") def main(): obj1 = A() obj1.method01() obj1.method01(5)

● 59

Kickstart to Python 3

if __name__ == "__main__": main()

Run the code above and see the output.

4.8.2 Operator Overloading Operators operate on operands and perform various operations. As we know that everything is an object in Python, the operands the operators function on in Python are all objects. The operations and the outcomes of operations of operators on built-in objects in Python are already well defined in Python. We can assign additional responsibility to the operators for the objects of user-defined classes. This concept is known as Operator Overloading. The following is a simple example for the addition operator: prog02.py class Point: def __init__(self, x, y, z): self.assign(x, y, z) def assign(self, x, y, z): self.x = x self.y = y self.z = z def printPoint(self): print(self.x, self.y, self.z) def __add__(self, other): x = self.x + other.x y = self.y + other.y z = self.z + other.z return Point(x, y, z) def __str__(self): return("({0},{1},{2})".format(self.x, self.y, self.z))

def main(): p1 = Point(1, 2, 3) p2 = Point(4, 5, 6) print(p1 + p2)

if __name__ == "__main__": main()

● 60

Chapter 4 • Object-Oriented Programming

Run the code and check the output. When we perform the operation p1 + p2 in the code, Python will call p1.__add__(p2) which in turn calls Point.__add__(p1,p2). Similarly, we can overload other operators as well. The special functions we need to implement for binary operations are tabulated below: .

Operator + - * // / % ** > & ˆ |

Special Function object.add(self, other) object.sub(self, other) object.mul(self, other) object.floordiv(self, other) object.truediv(self, other) object.mod(self, other) object.pow(self, other[, modulo]) object.lshift(self, other) object.rshift(self, other) object.and(self, other) object.xor(self, other) object.or(self, other)

The following is the table for the extended operations: Operator += -= *= //= /= %= **= = &= ˆ= |=

Special Function object.add(self, other) object.sub(self, other) object.mul(self, other) object.floordiv(self, other) object.truediv(self, other) object.mod(self, other) object.pow(self, other[, modulo]) object.lshift(self, other) object.rshift(self, other) object.and(self, other) object.xor(self, other) object.or(self, other)

This table is for the unary operators: Operator + - abs() ~ complex() int() long() float()

Special Function object.pos(self) object.neg(self) object.abs(self) object.invert(self) object.complex(self) object.int(self) object.long(self) object.float(self)

● 61

Kickstart to Python 3

oct() hex()

object.oct(self) object.hex(self)

This table is for comparison operators: Operator < = >

Special Function object.lt(self, other) object.le(self, other) object.eq(self, other) object.ne(self, other) object.ge(self, other) object.gt(self, other)

4.9 Syntax Errors When we write programs in Python (or any programming language, for that matter), we do not usually get them correct the very first time. This is where the terms Errors and Exceptions come into discussion. In this chapter, we will get started with Errors and Exceptions in Python. >>> print("Hello) SyntaxError: EOL while scanning string literal

In the print() statement above, we forgot to add " after the string Hello. This is a Syntax error. So, the Python interpreter highlighted it by throwing SyntaxError.

4.10 Exceptions We say that the syntax/parsing errors are handled with SyntaxError. Even though that statement is correct in Syntax, it may encounter an error (which is not related to syntax) during execution. Errors detected during execution are called exceptions. Consider the following statements and their executions in the interpreter: >>> 1/0 Traceback (most recent call last): File "", line 1, in 1/0 ZeroDivisionError: division by zero >>> '1' + 1 Traceback (most recent call last): File "", line 1, in '1' + 1 TypeError: can only concatenate str (not "int") to str >>> a = 8 + b Traceback (most recent call last): File "", line 1, in a = 8 + b

● 62

Chapter 4 • Object-Oriented Programming

NameError: name 'b' is not defined

The last line of the output of the execution of each statement shows what is wrong with the statement. This demonstrates that an exception is implicitly raised whenever there is an error during execution. The exceptions defined in the Python library are known as Built-in exceptions. https://docs.python.org/3/library/exceptions.html#bltin-exceptions has all the built-in exceptions listed.

4.10.1 Handling Exceptions Now we know the interpreter automatically raises an exception when an error is encountered during the runtime. Consider the following piece of code: prog01.py def main(): a = 1/0 print("DEBUG:

We are here...")

if __name__ == "__main__": main()

When we execute this, notice the interpreter encounters the following exception at the line a = 1/0. ZeroDivisionError: division by zero Once this exception is encountered, it does not run the statements which follow the statement where the exception is encountered. This is how exceptions are handled by default in Python. However, Python has a better provision for handling exceptions. We can enclose the code in a try block where we are likely to encounter an exception and the logic to handle it in except block as follows: prog01.py def main(): try: a = 1/0 print("DEBUG:

We are here...")

except Exception: print("Exception Occured") if __name__ == "__main__": main()

Run the code. It will invoke the except block when an exception is encountered rather than abruptly terminating. Note the code after the statement where the exception occurred is not executed.

● 63

Kickstart to Python 3

In the except block, Exception is the base class for all the built-in exceptions in Python. In the later parts of this chapter, we will also study user-defined exceptions which will be derived from the Exception class. Let's modify the code and add some more code to function main() which is not the part of try or except blocks. prog01.py def main(): try: a = 1/0 print("DEBUG:

We are here...")

except Exception: print("Exception Occured") print("This line will be executed...") if __name__ == "__main__": main()

When executed, you will notice the line outside the try and except block is executed in spite the exception is encountered.

4.10.2 Handling Exceptions by types When we run a program, we may encounter exceptions of multiple types. We can have the provision of handling various types of exceptions. A simple example is as follows: prog02.py def main(): try: a = 1/1 except ZeroDivisionError as err: print("Error: {0}".format(err)) except TypeError as err: print("Error: {0}".format(err)) except Exception as err: print("Error: {0}".format(err)) if __name__ == "__main__": main()

In the code above, we have except blocks for handling ZeroDivisionError and TypeError. If any other unexpected exception is encountered, it is handled by the last except block which is a generic exception handler block. Note that, the generic block should always be the last except block (as shown in the code above). If this is the very first except block, whenever any exception is encountered, the generic exception handling block will always be executed. This is because class Exception is the base class for all exceptions and it takes precedence.

● 64

Chapter 4 • Object-Oriented Programming

As an exercise, rewrite the above program with except Exception as the first exception handling block. As we know, everything is an object in python. SyntaxError is also a type of exception. This is a special type of exception that cannot be handled in the except block.

4.10.3 else block We can add an else block to the code after the except block. If any error is not encountered in the try block, else block is executed. The following program demonstrates this: prog02.py def main(): try: a = 1/1 except ZeroDivisionError as err: print("Error: {0}".format(err)) except TypeError as err: print("Error: {0}".format(err)) except Exception as err: print("Error: {0}".format(err)) else: print("This line will be executed...") if __name__ == "__main__": main()

4.10.4 Raising an Exception We know that an exception is automatically raised when there is a runtime error. We can raise an exception explicitly and deliberately using a raise statement. The following code demonstrates this: prog03.py def main(): try: raise Exception("Exception has been raised!") except Exception as err: print("Error: {0}".format(err)) else: print("This line will be executed...")

if __name__ == "__main__": main()

● 65

Kickstart to Python 3

The output is as follows, Error: Exception has been raised!

4.10.5 finally clause finally is a clause for the try statement, which is always executed on the way out. This means that it is essentially the Clean Up Clause. It is always executed at the end of the try clause irrespective of whether an exception occurred in the try statement. If any exception is not handled in the except block, it is reraised in finally. The following is an example of the same: prog04.py def divide(x, y): try: result = x / y except ZeroDivisionError: print("division by zero!") else: print("result is", result) finally: print("executing finally clause") def main(): divide(2, 1) divide("2", "1")

if __name__ == "__main__": main()

The following is the output: result is 2.0 executing finally clause executing finally clause Traceback (most recent call last): File "C:\Users\Ashwin\Google Drive\Elektor\Python Book Project\ Code\Chapter04\Exceptions\prog04.py", line 17, in main() File "C:\Users\Ashwin\Google Drive\Elektor\Python Book Project\ Code\Chapter04\Exceptions\prog04.py", line 13, in main divide("2", "1") File "C:\Users\Ashwin\Google Drive\Elektor\Python Book Project\ Code\Chapter04\Exceptions\prog04.py", line 3, in divide result = x / y TypeError: unsupported operand type(s) for /: 'str' and 'str'

● 66

Chapter 4 • Object-Oriented Programming

As we can see, in the code above there is no provision for handling TypeError exceptions. So, the finally clause raises it once more.

4.10.6 User-Defined Exceptions We can define our own exceptions derived from the Exception class. Usually, in a module, we define a single base class derived from Exception and obtain all the other exception classes from that. An example of this is shown below: prog05.py class Error(Exception): pass

class ValueTooSmallError(Error): pass

class ValueTooLargeError(Error): pass

def main(): number = 10 try: i_num = int(input("Enter a number: ")) if i_num < number: raise ValueTooSmallError elif i_num > number: raise ValueTooLargeError else: print("Perfect!") except ValueTooSmallError: print("This value is too small!") except ValueTooLargeError: print("This value is too large!")

if __name__ == "__main__": main()

Please run the program above and see the output. In the above program, the class Error is inherited from the built-in class Exception. We inherit more sub-classes from the class Error.

● 67

Kickstart to Python 3

Summary We explored the Object-Oriented programming paradigm in depth. We are now very much comfortable with OOP when using Python. We will use OOP extensively in the coming chapters which is why I covered it in this dedicated chapter as early as I could. The next chapter focuses on data structures using Python. It will also be a long and detailed chapter that will use all concepts learned in earlier chapters to implement and use data structures.

● 68

Chapter 5 • Data Structures

Chapter 5 • Data Structures In the previous chapter, we explored Object-Oriented Programming in detail. We should now be comfortable with this style of programming. In this chapter, we use the knowledge of Object-Oriented programming to create and work with data structures. We will explore the most popular and the most used linear data structures with the Python programming language. The following is a list of useful topics on data structures that we will explore in this chapter: • Introduction to data structures • Jupyter Notebook • Linked Lists • Doubly Linked List • Stack • Queue • Double-ended queues • Circular Queue Following this chapter, we will be able to write programs for data structures and their applications.

5.1 Introduction to Data Structures A data structure is a specialized way of organizing, storing, processing, and retrieving data. There are many data structures. The concept of data structures predates Python. Many of the data structures we will explore in this chapter were designed after considering the limitations of programming languages such as C, C++, and Java. Most programming languages are equipped with basic data structures like arrays. Python has built-in support for a better version and more versatile arrays, known as collections. Python also supports strings. We have already explored all the built-in data structures. In this chapter, we will focus on the most popular and most used data structures. Many third-party Python libraries already implement these. However, we want to learn to implement all of them on our own from scratch. Therefore, we will write our own code for all these data structures.

5.1.1 Jupyter Notebook We are going to use a web-based environment for demonstrating programs in this chapter. The environment is known as Jupyter notebook and before learning the actual topics, we will explore it in the brief. We have used the interactive mode of the Python interpreter and also IDLE. Interactive mode offers quick feedback but it does not save programs. IDLE offers functionality to save but does not offer quick feedback on the code. Before working with Jupyter notebook, I wished to have such functionality in a special editor. Jupyter Notebook fulfills this. It offers a web-based environment for Python and many other programming languages like R, Julia,

● 69

Kickstart to Python 3

and GNU Octave. It provides quick feedback for the execution of code and we can write our code in small snippets in the cells of a web-based editor. Let's install it on your platform using the following command on the command prompt of your OS: pip3 install jupyter pip3 install notebook

On the command prompt, navigate to the directory where you wish to save your notebooks and run the following command: jupyter notebook

It will launch the notebook server in the command prompt and show the log. You can find a URL in the log. If you copy and paste it in a browser of your choice and visit the webpage, it will show you the homepage of Jupyter notebook. Also, when you launch the server with the above command, it automatically launches the Jupyter notebook homepage. If you accidentally close the browser tab or window showing the Jupyter homepage, you can always get the URL from the server log in command prompt. The homepage will show you all the files and directories in the current directory. When Jupyter server launches, it creates a session and for the remainder of this, the directory from which we launched the server becomes the root-like starting-point of the whole session as long as it is alive. We can explore the files and directories in this directory from the web interface but cannot visit the parent directory of this directory or any other directory that is not a subdirectory of the current directory from where we launched the server process. There is a button labeled as New in this interface. When clicked, it shows options for creating a new sub-session for Python (and other languages like R and Octave, if installed). It also has options for creating a new folder, terminal session, and text file. You can explore those options too if you wish so. They are useful when working on projects. Also, on the same homepage, there is a tab labeled as Running. This shows all the notebooks and terminal sessions running. For now, click the New button and create a Python notebook. It will open a web-based interface in a new tab. That tab has all the functionality of a desktop IDE like IDLE. Also, there will be an empty cell where you can write a single line or block of code and run it. The output is shown on the same page itself when we execute the code. There is a dropdown that can set the meaning of the current cell. We can set a cell as a code or markdown. Code cells are used for running the code. Markdown cells are used for saving the rich text elements. Explore all options in the menu of the interface. It is pretty intuitive for a new user with a bit of programming experience. I will mention at the beginning of the chapter or section where I am using Jupyter notebook. We will be using it quite a lot.

5.2 Linked Lists Linked lists are dynamic data structures where we can insert and remove data with ease. The data elements are not stored in contiguous memory locations (like arrays) in the linked list. A linked list is made of nodes. A node is made of two parts: a data and a pointer part. The pointer points to the next node. The first node of a linked list is known as the Head Node and if it is empty, the list is empty. Let's start coding as we learn the concepts related

● 70

Chapter 5 • Data Structures

to Linked Lists. Let's create a new Jupyter Notebook and add the following code: class Node: def __init__(self, data): self.data = data self.next = None

This is a custom class for nodes. Let's create a class for linked lists: class LinkedList: def __init__(self): self.head = None

We can create an empty linked list, create nodes, and assign the nodes to the list: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third;

This creates a linked list. The following is the convention most people follow to visually represent the linked list:

Figure 5-1: Linked List To be specific, this is known as a singly linked list. There are other types of lists too. Let's see how we can traverse it and print the data part of all nodes in sequence starting from the head node. Modify the cell where we defined the linked list as follows: class LinkedList: def __init__(self): self.head = None def printList(self): temp = self.head while (temp): print (temp.data) temp = temp.next

● 71

Kickstart to Python 3

We are traversing until we encounter the last node where the temporary variable becomes None. Since we redefined the definition of the class, re-execute the cell where we are creating the linked list and run the following code in the same or different cell: llist.printList()

It will print the data part of the linked list. Let's learn how to insert a new node in the beginning of the list. Add the following method to the Linked List class: def push(self, new_data): new_node = Node(new_data) new_node.next = self.head self.head = new_node

Modify the cell that creates the linked list: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third; llist.push(0) llist.printList()

Run the cell and see the output. Similarly, we can write a method that can add a node at the end (append operation). Add the following code to the linked list class: def append(self, new_data): new_node = Node(new_data) if self.head is None: self.head = new_node return last = self.head while (last.next): last = last.next last.next =

new_node

Modify the last cell as follows, llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3)

● 72

Chapter 5 • Data Structures

llist.head.next = second; second.next = third; llist.push(0) llist.append(4) llist.printList()

Now, let's write a method so that it accepts arguments. If the argument matches with the data part of any node in the linked list, it removes that node. The limitation is that if multiple nodes have the same element in the data part, the method removes the first one only. The following is the method: def deleteNode(self, key): temp = self.head if (temp is not None): if (temp.data == key): self.head = temp.next temp = None return while(temp is not None): if temp.data == key: break prev = temp temp = temp.next if(temp == None): return prev.next = temp.next temp = None

We can check if it works by making the following modifications to the driver code: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third; llist.push(0) llist.append(4) llist.printList() llist.deleteNode(2) llist.printList()

● 73

Kickstart to Python 3

We can delete the entire linked list by deleting all nodes one by one: def deleteList(self): current = self.head while current: prev = current.next del current.data current = prev

Let's modify the driver code: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third; llist.push(0) llist.append(4) llist.printList() llist.deleteNode(2) llist.printList() llist.deleteList() llist.printList()

The last line throws an error as the list has already been deleted. We can also traverse the list and increase a counter variable. In the end, we can find the length of the linked list. The code is simple: def findLength(self): temp = self.head count = 0 while (temp): count += 1 temp = temp.next return count

Modify the driver code: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third;

● 74

Chapter 5 • Data Structures

llist.push(0) llist.append(4) #llist.printList() print(llist.findLength())

It will print the length of the list. We can even have a recursive method: def findLengthRec(self, node): if (not node): return 0 else: return 1 + self.findLengthRec(node.next)

Change the driver code: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third; llist.push(0) llist.append(4) #llist.printList() print(llist.findLength()) print(llist.findLengthRec(llist.head))

We can also write a method to search for an element in the list. The method returns True if the element is found in the list else it returns False: def search(self, x): current = self.head while current != None: if current.data == x: return True current = current.next return False

The driver code can be modified as follows: llist = LinkedList() llist.head = Node(1) second = Node(2) third = Node(3) llist.head.next = second; second.next = third;

● 75

Kickstart to Python 3

llist.push(0) llist.append(4) #llist.printList() print(llist.search(3)) print(llist.search(6))

The data structure we just learned is known as a Singly Linked List. This is because it has only one link (or pointer) that points forward.

5.2.1 Doubly Linked List Now we are comfortable with the concept of a singly linked list. Let's see an advanced data structure (or rather an improvement on the Singly Linked list). We can have a linked list in such a way that it has two links (pointers). We can thus traverse the list both ways. Let's define the node data structure: class Node: def __init__(self, data): self.data = data self.next = None self.prev = None

We can define a class as a doubly-linked list as follows: class DoublyLinkedList: def __init__(self): self.head = None

We can add a method to push an element to the start of the list: def push(self, new_data): new_node = Node(new_data) new_node.next = self.head if self.head is not None: self.head.prev = new_node self.head = new_node

We can also write a method to append the list: def append(self, new_data): new_node = Node(new_data) if self.head is None: self.head = new_node return last = self.head while last.next:

● 76

Chapter 5 • Data Structures

last = last.next last.next = new_node new_node.prev = last return

We can traverse the list: def printList(self, node): print ("Traversal in forward direction") while node: print(node.data) last = node node = node.next print ("Traversal in reverse direction") while last: print(last.data) last = last.prev

Let's write driver code to demonstrate the usage of these functions: llist = DoublyLinkedList() llist.push(1) llist.append(2) llist.append(3) llist.printList(llist.head)

The above code creates a doubly linked list as shown below:

Figure 5-2: Doubly Linked List

5.3 Stack The Stack is a linear data structure that is open at one end and closed at the other. This means that programmatically access it from one end only. We can insert and pop the element from the same end. The operation of inserting an element to a stack is called Push and the operation that removes an element from a stack is known as Pop. There are many ways we can implement a stack in Python and we will explore many of them.

● 77

Kickstart to Python 3

Let's see how to create a stack with a list in Python: stack = [] stack.append('a')

This creates an empty stack and pushes an element into it. Let's print the contents of the stack and see the output: print(stack)

Let's push a few more elements: stack.append('b') stack.append('c') print(stack)

We can pop the elements of a stack as follows: print(stack.pop()) print(stack.pop()) print(stack.pop())

Let's print the stack: print(stack)

It will show an empty list. If we try to pop an element from the empty stack, it will throw an exception: print(stack.pop())

Let's implement a stack with Deque module in Python: from queue import LifoQueue stack = LifoQueue(maxsize=5) print("Current number of element: ", stack.qsize()) for i in range(0, 5): stack.put(i) print("Element

Inserted : " + str(i))

print("\nCurrent number of element: ", stack.qsize()) print("\nFull: ", stack.full()) print("Empty: ", stack.empty())

● 78

Chapter 5 • Data Structures

print('\nElements popped from the stack') for i in range(0, 5): print(stack.get()) print("\nEmpty: ", stack.empty()) print("Full: ", stack.full()) The output is as follows, Current number of element: Element

Inserted : 0

Element

Inserted : 1

Element

Inserted : 2

Element

Inserted : 3

Element

Inserted : 4

Current number of element:

0

Full: Empty:

5

True False

Elements popped from the stack 4 3 2 1 0 Empty:

True

Full:

False

We can also define a stack in a more Pythonic way using the Deque module as follows: from collections import deque class Stack: def __init__(self): self.stack = deque() def isEmpty(self): if len(self.stack) == 0: return True else: return False def length(self):

● 79

Kickstart to Python 3

return len(self.stack) def top(self): return self.stack[-1] def push(self, x): self.x = x self.stack.append(x) def pop(self): self.stack.pop()

Let's write some driver code and learn to reverse a string with the stack. We read and push the characters in a string into a stack and then pop them and create a new string with the popped items as follows: str1 = "Test_string" n = len(str1) stack = Stack() for i in range(0, n): stack.push(str1[i]) reverse = "" while not stack.isEmpty(): reverse = reverse + stack.pop() print(reverse)

We can also implement a stack using a linked list. Complete this as an exercise.

5.4 Queue Queues are the linear data structures that allow insertion from one end and removal from the other. This is unlike stacks that are Last In First Out (LIFO). Queues are First in First Out (FIFO). Adding an element to a queue is known as enqueue and removing an item is known as dequeue. We can implement queues in various ways. Let's implement them with lists in python. Let's see the code: queue = [] queue.append('a') print(queue) Let's add more elements to the queue: queue.append('b') queue.append('c') print(queue)

● 80

Chapter 5 • Data Structures

We can remove a few items: print(queue.pop(0)) print(queue.pop(0)) print(queue)

The queue is now empty and trying to remove one more item from it will raise an exception: print(queue.pop(0))

We can also implement queues with the synchronized queue class in python: from queue import Queue q = Queue(maxsize=3) q.qsize()

When executed, the above code prints the current number of items in the queue. We can add a few items as follows: q.put('a') q.put('b') q.put('c') print(q.full())

We can remove items as follows: print(q.get()) print(q.get()) print(q.get()) print(q.empty())

We can also implement it using a deque: from collections import deque q = deque()

Let's add a few elements to this: q.append('a') q.append('b') q.append('c') print("Contents of the queue") print(q)

● 81

Kickstart to Python 3

The output is as follows, Contents of the queue deque(['a', 'b', 'c']) We can remove the elements: print(q.popleft()) print(q.popleft()) print(q.popleft()) print("\nAn empty Queue: ") print(q)

The output is as follows, a b c An empty Queue: deque([]) In the earlier section, I asked you to implement a stack with a linked list. We can do the same for queues too. Here, we will learn to implement it. If you have written a program for a stack with a linked list, you will see we can modify the same program to suit our purpose. Let's write the code: class Node: def __init__(self, data): self.data = data self.next = None

Let's define a queue using this node: class Queue: def __init__(self): self.front = self.rear = None def isEmpty(self): return self.front == None # Method to add an item to the queue def EnQueue(self, item):

● 82

Chapter 5 • Data Structures

temp = Node(item) if self.rear == None: self.front = self.rear = temp return self.rear.next = temp self.rear = temp def DeQueue(self): if self.isEmpty(): return temp = self.front self.front = temp.next if(self.front == None): self.rear = None

Finally, we can write a driver code: q = Queue() q.EnQueue(1) q.EnQueue(2) q.DeQueue() q.DeQueue() q.EnQueue(3) q.EnQueue(4) q.EnQueue(5) q.DeQueue()

Let's print the elements in the front and the rear of the queue: print("\nThe front of the Queue : " + str(q.front.data)) print("\nThe rear of the Queue : " + str(q.rear.data))

It produces the following output: The front of the Queue : 4 The rear of the Queue : 5

5.4.1 Double Ended Queues We have already used the deque class to implement a simple one-sided queue. It is a two-sided queue where we can insert and remove from both ends. Let's use it the way it was intended to be and write a sample program to demonstrate its double-ended capability:

● 83

Kickstart to Python 3

import collections deq = collections.deque([10, 20, 30]) print (deq)

We can also add a few items from one end: deq.append(40) print (deq)

We can add the item from the other end: deq.appendleft(0) print (deq)

We can remove the item from an end: deq.pop() print (deq)

We can also remove the item from the other end: deq.popleft() print (deq)

5.4.2 Circular Queue A circular queue is a data structure that is also known by the names circular buffer, cyclic buffer, or ring buffer. It is represented as a connected ring. It has start and end pointers. But the real-life memory is never organized in a ring (not physically at least). So, we use linear data structures to demonstrate it. The following is a conceptual representation of a circular buffer:

Figure 5-3: A circular buffer

● 84

Chapter 5 • Data Structures

Let's define a circular buffer and associated operations: class CircularQueue(): def __init__(self, size): self.size = size self.queue = [None for i in range(size)] self.front = self.rear = -1 def enqueue(self, data): if ((self.rear + 1) % self.size == self.front): print("The Circular Queue is full...") elif (self.front == -1): self.front = 0 self.rear = 0 self.queue[self.rear] = data else: self.rear = (self.rear + 1) % self.size self.queue[self.rear] = data def dequeue(self): if (self.front == -1): print ("The Circular Queue is empty...") elif (self.front == self.rear): temp=self.queue[self.front] self.front = -1 self.rear = -1 return temp else: temp = self.queue[self.front] self.front = (self.front + 1) % self.size return temp def show(self): if(self.front == -1): print("The Circular Queue is Empty...") elif (self.rear >= self.front): print("Elements in the circular queue are: ") for i in range(self.front, self.rear + 1): print(self.queue[i]) print () else: print ("Elements in the Circular Queue are: ") for i in range(self.front, self.size):

● 85

Kickstart to Python 3

print(self.queue[i]) for i in range(0, self.rear + 1): print(self.queue[i]) if ((self.rear + 1) % self.size == self.front): print("The Circular Queue is full...")

Let's write a driver program for using this: cq = CircularQueue(5) cq.enqueue(1) cq.enqueue(2) cq.enqueue(3) cq.enqueue(4) cq.show() print ("Dequed value = ", cq.dequeue()) cq.show() cq.enqueue(5) cq.enqueue(6) cq.enqueue(7) print ("Dequed value = ", cq.dequeue()) cq.show()

The output is as follows: Elements in the circular queue are: 1 2 3 4 Dequed value = 1

Elements in the circular queue are: 2 3 4 The Circular Queue is full... Dequed value = 2 Elements in the Circular Queue are: 3 4 ● 86

Chapter 5 • Data Structures

5 6 We can also define a circular queue with a linked list: class Node: def __init__(self): self.data = None self.link = None

Let's define a class for a circular linked queue: class Queue: def __init__(self): front = None rear = None

We can write a method outside the class definition to add an item to this circular queue: def enQueue(q, value): temp = Node() temp.data = value if (q.front == None): q.front = temp else: q.rear.link = temp q.rear = temp q.rear.link = q.front

We can write another method that retrieves an item from the queue: def deQueue(q): if (q.front == None): print("The circular queue is empty") return -999999999999 value = None if (q.front == q.rear): value = q.front.data q.front = None q.rear = None else: temp = q.front value = temp.data q.front = q.front.link q.rear.link = q.front

● 87

Kickstart to Python 3

return value

The following function shows the elements of the queue: def show(q): temp = q.front print("The elements in the Circular Queue are: ") while (temp.link != q.front): print(temp.data) temp = temp.link print(temp.data)

The driver code to demonstrate all the above functionality is as follows: q = Queue() q.front = q.rear = None enQueue(q, 1) enQueue(q, 2) enQueue(q, 3) show(q) print("Dequed value = ", deQueue(q)) print("Dequed value = ", deQueue(q)) show(q) enQueue(q, 4) enQueue(q, 5) show(q)

Here is the output of the code: The elements in the Circular Queue are: 1 2 3 Dequed value = 1 Dequed value = 2 The elements in the Circular Queue are: 3

● 88

Chapter 5 • Data Structures

The elements in the Circular Queue are: 3 4 5

Summary In this chapter, we explored traditional linear data structures. We are now comfortable with stacks, queues, and linked lists. The next chapter will be very interesting and exciting: We will learn Turtle graphics and learn many Turtle recipes. If you are creative and are looking for some adventure in the area of graphics, you will love the next chapter.

● 89

Kickstart to Python 3

Chapter 6 • Turtle Graphics In the previous chapter, we explored various data structures and demonstrated them with Python programming. In this chapter, we will use the built-in turtle library in Python to draw attractive graphical shapes. The following list is the topics we will be cover in this chapter: • History of Turtle • Getting Started • Recipes with Turtle • Visualizing Recursion • Multiple turtles After this chapter, we will be comfortable drawing shapes using turtle.

6.1 History of Turtle Turtles are a class of educational robots designed in the late 1940s under the guidance of researcher William Grey Walter. They are used in the teaching of Computer Science and Mechanical Engineering. These robots are designed to be low to the ground. They have a very small turning radius for finer direction control. They are also sometimes equipped with sensors. Logo is an educational programming language designed by Wally Feurzeig, Seymour Papert, and Cynthia Solomon. Turtle graphics is one of the features of the Logo programming language. It uses either on-screen or a physical turtle to draw on a screen or piece of paper respectively. The Python programming language also has a library for Turtle graphics. In this chapter, we will explore this library in detail.

6.2 Getting Started We can get started by installing the necessary libraries. Install Tkinter library using the following command: pi@pi-desktop:~$ sudo apt -y install python3-tk

Turtle library needs Tkinter. Turtle comes pre-installed with most of the Python distributions. If your Python setup does not have Turtle, it can be installed with the following command: pi@pi-desktop:~$ pip3 install PythonTurtle

● 90

Chapter 6 • Turtle Graphics

Let's launch Python in interactive mode and import turtle: >>> import turtle as Turtle

This imports the module with an alias Turtle. Now, run the following statement: >>> dir(Turtle)

It returns the following list: ['Canvas', 'Pen', 'RawPen', 'RawTurtle', 'Screen', 'ScrolledCanvas', 'Shape', 'TK', 'TNavigator', 'TPen', 'Tbuffer', 'Terminator', 'Turtle', 'TurtleGraphicsError', 'TurtleScreen', 'TurtleScreenBase', 'Vec2D', '_CFG', '_LANGUAGE', '_Root', '_Screen', '_TurtleImage', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__forwardmethods', '__func_body', '__ loader__', '__methodDict', '__methods', '__name__', '__package__', '__spec__', '__stringBody', '_alias_list', '_make_global_funcs', '_screen_docrevise', '_ tg_classes', '_tg_screen_functions', '_tg_turtle_functions', '_tg_utilities', '_turtle_docrevise', '_ver', 'addshape', 'back', 'backward', 'begin_fill', 'begin_poly', 'bgcolor', 'bgpic', 'bk', 'bye', 'circle', 'clear', 'clearscreen', 'clearstamp', 'clearstamps', 'clone', 'color', 'colormode', 'config_dict', 'deepcopy', 'degrees', 'delay', 'distance', 'done', 'dot', 'down', 'end_ fill', 'end_poly', 'exitonclick', 'fd', 'fillcolor', 'filling', 'forward', 'get_poly', 'get_shapepoly', 'getcanvas', 'getmethparlist', 'getpen', 'getscreen', 'getshapes', 'getturtle', 'goto', 'heading', 'hideturtle', 'home', 'ht', 'inspect', 'isdown', 'isfile', 'isvisible', 'join', 'left', 'listen', 'lt', 'mainloop', 'math', 'mode', 'numinput', 'onclick', 'ondrag', 'onkey', 'onkeypress', 'onkeyrelease', 'onrelease', 'onscreenclick', 'ontimer', 'pd', 'pen', 'pencolor', 'pendown', 'pensize', 'penup', 'pos', 'position', 'pu', 'radians', 'read_docstrings', 'readconfig', 'register_shape', 'reset', 'resetscreen', 'resizemode', 'right', 'rt', 'screensize', 'seth', 'setheading', 'setpos', 'setposition', 'settiltangle', 'setundobuffer', 'setup', 'setworldcoordinates', 'setx', 'sety', 'shape', 'shapesize', 'shapetransform', 'shearfactor', 'showturtle', 'simpledialog', 'speed', 'split', 'st', 'stamp', 'sys', 'textinput', 'tilt', 'tiltangle', 'time', 'title', 'towards', 'tracer', 'turtles', 'turtlesize', 'types', 'undo', 'undobufferentries', 'up', 'update', 'width', 'window_height', 'window_width', 'write', 'write_docstringdict', 'xcor', 'ycor']

These are the attributes and methods available in the turtle library. Since we imported the library with the alias Turtle, we can use Turtle to call these in an object-oriented manner. If we wish to know more about any one of these attributes or methods, we can use the routine help(). >>> help(Turtle.fd)

● 91

Kickstart to Python 3

It will display a UNIX-style help page which can be terminated by pressing the q key on the keyboard.

6.3 Exploring Turtle methods Let's explore turtle methods. We know the position of the turtle as follows: >>> Turtle.position()

It shows the following output: (0.00,0.00) This means the Turtle is at the origin of the canvas (0,0). The execution of the statement also opens a graphical window with the Turtle represented as an arrowhead pointing the right direction from our perspective. The following (Figure 6-1) shows this,

Figure 6-1: Turtle at the origin (0,0) We can move the turtle forward with either forward() or fd() method. >>> Turtle.forward(25)

Or >>> Turtle.fd(25)

● 92

Chapter 6 • Turtle Graphics

If we run the position() statement again, we can see the current coordinates of the Turtle as follows: >>> Turtle.position()

The output is as follows, (25.00,0.00) Similarly, we can traverse backward with any one of the following methods: >>> Turtle.backward(10) >>> Turtle.bk (10) >>> Turtle.back (10)

The methods left() or lt() and right() or rt() are used to turn the turtle left and right by a specified amount of angle. By default, the angle is in degrees but we can also set it to radians with radians(). We can set it back to degrees with the routine degrees(). By default, we can see an arrow on the screen. But we can change it to other shapes with the routine shape(). The allowed arguments are "arrow", "turtle", "circle", "square", "triangle", and "classic". Let's change the shape as follows, >>> Turtle.shape("turtle")

Now, let's further learn by creating some small recipes. We will learn the rest of the functions as we need to use them. Until now, we worked with interactive mode. Let's nowww start creating a script file.

6.4 Recipes with Turtle Let's draw a square with the Turtle. Open IDLE and create a file. Write the following code in it: prog00.py import turtle as Turtle import time Turtle.forward(150) Turtle.left(90) Turtle.forward(150) Turtle.left(90) Turtle.forward(150) Turtle.left(90) Turtle.forward(150) Turtle.left(90) time.sleep(10) Turtle.bye()

● 93

Kickstart to Python 3

The above program will draw a square and wait 10 seconds before closing the window. We can write it in a bit more of a Pythonic way as follows: prog01.py import turtle as Turtle import time for i in range(4): Turtle.forward(150) Turtle.left(90) time.sleep(10) Turtle.bye()

Run the program and see the output. Let's add more features to it: prog02.py import turtle as Turtle import time def square(length): for i in range(4): Turtle.forward(length) Turtle.left(90) if __name__ == "__main__": square(150) time.sleep(10) Turtle.bye()

We can draw circles: prog03.py import turtle as Turtle Turtle.circle(50) Turtle.circle(-50)

The output is shown in Figure 6-2.

● 94

Chapter 6 • Turtle Graphics

Figure 6-2: Drawing circles The argument passed to the routine circle() decides the radius and direction of the circle. Let's use this routine in combination with other routines to create a beautiful diagram: prog04.py import turtle as Turtle Turtle.color("green") for angle in range(0, 360, 10): Turtle.seth(angle) Turtle.circle(100)

In the program (prog04.py) above, we use the routine color() to set the color of the drawing. We are also using the routine seth() to set the direction of the Turtle. The code takes quite a lot of time to execute and when it finishes, it creates a beautiful pattern shown in Figure 6-3.

● 95

Kickstart to Python 3

Figure 6-3: Drawing a beautiful pattern with circles We can also draw a circle without using the built-in routine circle(). The logic is that we move the Turtle forward by one position and then take the turn with a one-degree angle. We do it 360 times and get a circle. Here is the code: prog05.py import turtle as Turtle count = 0 while(count < 360): Turtle.forward(1) Turtle.left(1) count = count + 1

Run the program and see the output. We can also set the background color. Let's write a program for a random walk. The background is set as black in this example. prog06.py import turtle as Turtle import random Turtle.speed(10) Turtle.bgcolor('Black')

● 96

Chapter 6 • Turtle Graphics

turns = 1000 distance = 20 for x in range(turns): right=Turtle.right(random.randint(0, 360)) left=Turtle.left(random.randint(0, 360)) Turtle.color(random.choice(['Blue', 'Red', 'Green', 'Cyan', 'Magenta', 'Pink', 'Violet'])) random.choice([right,left]) Turtle.fd(distance)

It produces the following (Figure 6-4) output,

Figure 6-4: Drawing with black background Sometimes programs take a lot of time to execute. We can manage this by changing the speed of the turtle. We can use the routine speed() for this. The speed can vary from 0 to 10. The following is the mapping of the speed strings against the values. The default is "normal". • "fastest" corresponds to 0 • "fast" corresponds to 10 • "normal" corresponds to 6 • "slow" corresponds to 3 • "slowest" corresponds to 1

● 97

Kickstart to Python 3

Let's make some more interesting drawings. We have used circles (prog04.py) to draw a beautiful pattern (Figure 6-3). We can use circles to draw even more intricate patterns: prog07.py import turtle as Turtle Turtle.speed(10) Turtle.bgcolor('Black') colors=['Red', 'Yellow', 'Purple', 'Cyan', 'Orange', 'Pink'] for x in range(100): Turtle.circle(x) Turtle.color(colors[x%6]) Turtle.left(60)

The output is shown in Figure 6-5.

Figure 6-5: Floral pattern We can draw lines that form a colorful square pattern: prog08.py import turtle as Turtle Turtle.speed(0) Turtle.bgcolor('Black') colors=['Red','Yellow','Pink','Orange']

● 98

Chapter 6 • Turtle Graphics

for x in range(300): Turtle.color(colors[x%4]) Turtle.forward(x) Turtle.left(90)

The output is shown in Figure 6-6.

Figure 6-6: Square pattern If you have not noticed, the program runs faster. This is because we have increased the speed of the Turtle and made it fastest. We can also add more colors to the visualization: prog09.py import turtle as Turtle Turtle.speed(0) Turtle.bgcolor('Black') colors=['Red', 'Yellow', 'Pink', 'Orange', 'Blue', 'Green', 'Cyan', 'White'] for x in range(300): Turtle.color(colors[x%8]) Turtle.forward(x) Turtle.left(90)

We can also have the lines drawn in random color from the list.

● 99

Kickstart to Python 3

prog10.py import turtle as Turtle import random Turtle.speed(0) Turtle.bgcolor('Black') colors=['Red', 'Yellow', 'Pink', 'Orange', 'Blue', 'Green', 'Cyan', 'White'] for x in range(300): Turtle.color(colors[random.randint(0, 7)]) Turtle.forward(x) Turtle.left(90)

Run both programs and see the output. We can also draw a beautiful hexagonal pattern drawing: prog11.py import turtle as Turtle Turtle.bgcolor("black") colors=["Red","White","Cyan","Yellow","Green","Orange"] for x in range(300): Turtle.color(colors[x%6]) Turtle.fd(x) Turtle.left(59)

The output is shown in Figure 6-7.

Figure 6-7: Hexagonal pattern drawing

● 100

Chapter 6 • Turtle Graphics

We can also fill shapes with colors. Let's see a couple of examples before ending this section. We will use the routines fillcolor(), begin_fill(), and end_fill(). The following is a simple example. prog12.py Turtle.fillcolor('red') Turtle.begin_fill() Turtle.forward(100) Turtle.left(120) Turtle.forward(100) Turtle.left(120) Turtle.forward(100) Turtle.left(120) Turtle.end_fill()

It will draw a triangle and fill it with red color. Let's see another simple example: prog13.py import turtle as Turtle Turtle.fillcolor('Orange') Turtle.begin_fill() for count in range(4): Turtle.forward(100) Turtle.left(90) Turtle.end_fill()

And the above program fills a square with orange color.

6.5 Visualizing Recursion We learned about the recursion in the third chapter. We know that calling a function from within itself is known as direct recursion. We know that there are two important parts of a recursive function. The first is the recursion termination criteria. The second is the recursive call. In our earlier recursive functions, we printed the output on the console. Now instead of the text-based output, we will have graphical output. We will start with a simple program: prog00.py import turtle as Turtle def zigzag(size, length): if size > 0: Turtle.left(45) Turtle.forward(length) Turtle.right(90) Turtle.forward(2*length) Turtle.left(90) Turtle.forward(length) Turtle.right(45)

● 101

Kickstart to Python 3

zigzag(size-1, length) if __name__ == "__main__": zigzag(5, 50)

We can see in the prog00.py that we have the termination criteria that checks if one of the arguments passed is greater than zero. We have only one recursive call that passes a decremented argument to one of the parameters. There doesn't need to be only one recursive call. There could be multiple recursive calls too. We will explore those possibilities further down the road. But for now, we will explore a few shapes that call the recursive function only once. The Following (Figure 6-8) shows us the output:

Figure 6-8: Output of Zigzag function call Let's make some changes to the part where we call the recursive function from the main part of the program: zigzag(3, 70)

We can observe the changed output as follows:

Figure 6-9: Output of Zigzag function call with changed arguments This was one of the simplest recursive figures we can draw. Let us draw a more complicated figure: a spiral. Let's first see the code. I will then explain it in detail. prog01.py import turtle as Turtle def spiral(sideLen, angle, scaleFactor, minLength): if sideLen >= minLength: Turtle.forward(sideLen) Turtle.left(angle) spiral(sideLen*scaleFactor, angle, scaleFactor, minLength) if __name__ == "__main__": Turtle.speed(0) spiral(200, 120, 0.9, 20)

This program (prog01.py) draws various spiral shapes based on the arguments we pass to the recursive function. The criteria for termination is that the length of the side of the spiral must be greater than the minimum length. If so we move the Turtle forward and turn it to

● 102

Chapter 6 • Turtle Graphics

the left. We then make the recursive call such that the subsequent part of the spiral is the scaled-down version of the earlier part. Let's see the output:

Figure 6-10: A triangular spiral We can make any regular shaped spiral with this. For example, we can change the angle to 90 to modify the recursive call as follows: spiral(200, 90, 0.9, 20)

This is the output:

Figure 6-11: A Square spiral We can modify the main call for a pentagonal spiral: spiral(200, 72, 0.9, 20)

This is the output:

Figure 6-12: A Pentagonal spiral

● 103

Kickstart to Python 3

Finally, we can make a hexagonal spiral using the following code: spiral(200, 60, 0.9, 20)

The output is as follows:

Figure 6-13: A Hexagonal spiral Run the following calls from the main section to see more action: spiral(200, 45, 0.9, 20) spiral(200, 40, 0.9, 20) spiral(200, 36, 0.9, 20) spiral(200, 30, 0.9, 20) spiral(200, 24, 0.9, 20) spiral(200, 20, 0.9, 20) spiral(200, 18, 0.9, 20)

Earlier in the third chapter, we learned how to write a program for Fibonacci Series. We printed the Fibonacci numbers on the console. Here, we will learn to visually represent the Fibonacci Tree. In place of printing, we will draw with the Turtle: prog02.py import turtle as Turtle def drawfib(n, len_ang): Turtle.forward(2 * len_ang) if n == 0 or n == 1: pass else: Turtle.left(len_ang) drawfib(n - 1, len_ang) Turtle.right(2 * len_ang) drawfib(n - 2, len_ang) Turtle.left(len_ang) Turtle.backward(2 * len_ang) if __name__ == "__main__":

● 104

Chapter 6 • Turtle Graphics

Turtle.left(90) Turtle.speed(0) drawfib(7, 20)

As we can see, the code is the same and we are only replacing the print() statements with the Turtle movement. The fibonacci tree is an example of the recursion where we call the function recursively on the same level more than once (twice exactly). It produces the following (Figure 6-14):

Figure 6-14: A Fibonacci Tree We can change the arguments to the function call in the main section to obtain different outputs. The following are a few examples:

Figure 6-15: Fibonacci Trees with different arguments to the function call in the main section As an exercise, call the function with a different set of arguments. We can also draw a part of the Koch snowflake: prog03.py import turtle as Turtle def koch_snowflake(length, depth): if depth == 0: Turtle.forward(length) else: length = length / 3

● 105

Kickstart to Python 3

depth = depth - 1 Turtle.color('Blue') koch_snowflake(length, depth) Turtle.right(60) Turtle.color('Orange') koch_snowflake(length, depth) Turtle.left(120) Turtle.color('Red') koch_snowflake(length, depth) Turtle.right(60) Turtle.color('Green') koch_snowflake(length, depth) Turtle.speed(10) koch_snowflake(500, 4)

We have three recursive function calls in the code (prog03.py). This is the output:

Figure 6-16: A part of Koch Snowflake We can also draw the entire snowflake by modifying the code above (prog03.py). We have to add the function call in the main section in a loop: prog04.py import turtle as Turtle import random def koch_snowflake(length, depth): if depth == 0: Turtle.forward(length) else: length = length / 3 depth = depth - 1 Turtle.color(colors[random.randint(0, 8)]) koch_snowflake(length, depth) Turtle.right(60) Turtle.color(colors[random.randint(0, 8)]) koch_snowflake(length, depth) Turtle.left(120)

● 106

Chapter 6 • Turtle Graphics

Turtle.color(colors[random.randint(0, 8)]) koch_snowflake(length, depth) Turtle.right(60) Turtle.color(colors[random.randint(0, 8)]) koch_snowflake(length, depth) Turtle.speed(10) colors = ['Blue', 'Red', 'Orange', 'Green', 'Magenta', 'Purple', 'Cyan', 'Violet', 'Black'] for i in range(3): koch_snowflake(200, 3) Turtle.left(120)

The output is a complete snowflake as shown in Figure 6-17:

Figure 6-17: Koch Snowflake We can draw a more complicated figure. In the last examples (prog03.py and prog04. py), we call the function recursively thrice. In the next example, we will call the function recursively four times. Have a look at the following code. We are using many new routines in it. prog05.py import turtle as Turtle def draw_line(pos1, pos2): Turtle.penup() Turtle.goto(pos1[0], pos1[1]) Turtle.pendown() Turtle.goto(pos2[0], pos2[1]) def recursive_draw(x, y, width, height, count): draw_line([x + width * 0.25, height // 2 + y], [x + width * 0.75, height // 2 + y]) draw_line([x + width * 0.25, (height * 0.5) // 2 + y], [x + width * 0.25, (height * 1.5) // 2 + y]) draw_line([x + width * 0.75, (height * 0.5) // 2 + y],

● 107

Kickstart to Python 3

[x + width * 0.75, (height * 1.5) // 2 + y]) if count 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((640, 480)) pygame.quit() sys.exit(0)

Let's decode the program line-by-line. The first line imports all the libraries we will use in the program. Then we use the routine init() to initialize the Pygame library in the current program. We are storing the return value into a variable and checking if it returns an error.

● 112

Chapter 7 • Programming animations and games

Then we set the resolution of the output window using the routine set_mode(). Finally, we use the routine quit() to close the pygame session. Run the program. The output is not very conspicuous. It creates a pygame window of the given dimensions. It has a black background. It flashes the window momentarily before closing it. Congrats, we are getting started with the Pygame library! Also, do not forget to check the console output. It will be as follows: pygame 2.0.1 (SDL 2.0.14, Python 3.9.7) Hello from the pygame community. https://www.pygame.org/ contribute.html Pygame initialized successfully! Let's add an event loop to this. The event loop records all the events of the mouse and keyboard and terminates the program on clicking the close button. Following is the enhanced code, prog01.py import pygame, sys result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((640, 480)) running = True while running: for event in pygame.event.get(): print(event) if event.type == pygame.QUIT: running = False pygame.quit() sys.exit(0)

Run the above program (prog01.py) and observe the output in the terminal. You will notice that the program prints all the actions performed by the user (keyboard and mouse events). Let's create a small application that changes the background color on MOUSEBUTTONDOWN. This is the full program: prog02.py import pygame, sys, random result = pygame.init()

● 113

Kickstart to Python 3

if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((640, 480)) BLACK = (0, 0, 0) GRAY = (127, 127, 127) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) YELLOW = (255, 255, 0) CYAN = (0, 255, 255) MAGENTA = (255, 0, 255) bgcolor = [BLACK, GRAY, WHITE, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA] background = BLACK running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.MOUSEBUTTONDOWN: background = bgcolor[random.randint(0, 8)] screen.fill(background) pygame.display.flip() pygame.quit() sys.exit(0)

Let's take a look at the new code we added to the file. We learned tuples in an earlier chapter. We are defining colors with tuples that have a combination of Red, Green, and Blue values. These values can vary from 0 to 255. We have defined 9 different tuples for colors. Then we are adding those tuples of colors to a list. In the loop for the event, if we detect a MOUSEBUTTONDOWN event, we are choosing from the list of colors randomly and assigning that color to the background. Finally, we are setting the background with the routine fill() and updating the display with the routine flip(). The routine flip() is used to update the contents of the entire display. We will be using it frequently from now onward. Run the program and see the background change on the MOUSEBUTTONDOWN event.

● 114

Chapter 7 • Programming animations and games

7.2 Recursion with Pygame We have studied the concept of recursion in the third chapter. In the previous chapter, we explored it visually. Let's explore it again visually. But this time, we will use the Pygame library. Let's start with something simple. We will draw a simple recursive tree. The visualization will have a black background and white branches. The last few branches and leaves will be green. Let's see the code: prog03.py import pygame, math, random import time, sys width, height = 1366, 768 result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') window = pygame.display.set_mode((width, height)) pygame.display.set_caption('Fractal Tree') screen = pygame.display.get_surface() def Fractal_Tree(x1, y1, theta, depth): if depth: rand_length = random.randint(1, 10) rand_angle = random.randint(10, 20) x2 = x1 + int(math.cos(math.radians(theta)) * depth * rand_length) y2 = y1 + int(math.sin(math.radians(theta)) * depth * rand_length) if depth < 5: clr = (0, 255, 0) else: clr = ( 255, 255, 255) pygame.draw.line(screen, clr, (x1, y1), (x2, y2), 2) Fractal_Tree(x2, y2, theta - rand_angle, depth-1) Fractal_Tree(x2, y2, theta + rand_angle, depth-1) Fractal_Tree( (width/2), (height-10), -90, 14) pygame.display.flip() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit(0)

● 115

Kickstart to Python 3

As we can see, we have used a new routine set_caption() to set the title of the window. We are also using the routine draw.line() to draw a line. An important thing to consider is that the origin (0, 0) lies in the top left corner in Pygame. We are familiar with most of the code. The recursive function accepts the coordinates of a point, angle, and depth as arguments. We also randomly generate the length of a branch. Using the passed coordinates, and randomly generated angle and length, we compute the endpoint of the branch. If the branches are near the leaves, then we color them green else with white. In the end, we draw the line segment and pass the endpoint of that segment to the recursive function call. We also randomly generate another value for angle and add and subtract it to the passed angle and use these new values as arguments to the recursive call. Let's see the results:

Figure 7-1: Recursive tree with Pygame Let's draw the Sierpinski triangle. It is a bit more complicated than a recursive tree. We have to call the function recursively three times. Here is the code: prog04.py import pygame, math, random import time, sys width, height = 800, 800 result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') window = pygame.display.set_mode((width, height)) pygame.display.set_caption('Fibonacci Tree')

● 116

Chapter 7 • Programming animations and games

screen = pygame.display.get_surface() shift = 20 A_x = 0 + shift A_y = 649 + shift B_x = 750 + shift B_y = 649 + shift C_x = 375 + shift C_y = 0 + shift RGB = [(0, 0, 255), (0, 255, 0), (255, 0, 0)] def draw_triangle(A_x, A_y, B_x, B_y, C_x, C_y, i): pygame.draw.line(screen, RGB[i%3], (A_x, A_y), (B_x, B_y), 1) pygame.draw.line(screen, RGB[i%3], (C_x, C_y), (B_x, B_y), 1) pygame.draw.line(screen, RGB[i%3], (A_x, A_y), (C_x, C_y), 1) pygame.display.flip() def draw_fractal(A_x, A_y, B_x, B_y, C_x, C_y, depth): if depth > 0: draw_fractal((A_x), (A_y), (A_x+B_x)/2, (A_y+B_y)/2, (A_x+C_x)/2, (A_y+C_y)/2, depth-1) draw_fractal((B_x), (B_y), (A_x+B_x)/2, (A_y+B_y)/2, (B_x+C_x)/2, (B_y+C_y)/2, depth-1) draw_fractal((C_x), (C_y), (C_x+B_x)/2, (C_y+B_y)/2, (A_x+C_x)/2, (A_y+C_y)/2, depth-1) draw_triangle((A_x), (A_y), (A_x+B_x)/2, (A_y+B_y)/2, (A_x+C_x)/2, (A_y+C_y)/2, depth) draw_triangle((B_x), (B_y), (A_x+B_x)/2, (A_y+B_y)/2, (B_x+C_x)/2, (B_y+C_y)/2, depth) draw_triangle((C_x), (C_y), (C_x+B_x)/2, (C_y+B_y)/2, (A_x+C_x)/2, (A_y+C_y)/2, depth) draw_fractal(A_x, A_y, B_x, B_y, C_x, C_y, 1) pygame.display.flip() running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit(0)

As we can see, we have a separate custom function that draws the actual shape (draw_ triangle()). We are calling this three times into the recursive function before calling the recursive function three times. In the main section, we are calling the function with depth as 1. It produces the following output:

● 117

Kickstart to Python 3

Figure 7-2: Sierpinski Triangle of depth 1 Let's modify the call in the main section for a better understanding: draw_fractal(A_x, A_y, B_x, B_y, C_x, C_y, 2)

The output is as follows:

Figure 7-3: Sierpinski Triangle of depth 2 Let's go further and increase the depth to 5 and see the results: draw_fractal(A_x, A_y, B_x, B_y, C_x, C_y, 5)

● 118

Chapter 7 • Programming animations and games

The following is the output:

Figure 7-4: Sierpinski Triangle of depth 5 Let's increase the depth to 10: draw_fractal(A_x, A_y, B_x, B_y, C_x, C_y, 10)

The output is as follows:

Figure 7-5: Sierpinski Triangle of depth 10

● 119

Kickstart to Python 3

7.3 Sierpinski Triangle by Chaos Game We can create the Sierpinski Triangle using Chaos Game. The fractal is created by iteratively creating a sequence of points. We choose an initial random point ((400, 400) in this example). Each point in the sequence is given a half of the distance between the previous point and one of the vertices of the polygon chosen randomly in each iteration. When we repeat this iterative process a large number of times while selecting a vertex of a triangle at random on each iteration most often (but not always) results in the Sierpinski Triangle. For better results, we can decide not to plot a few initial points. Here is the code: prog05.py import pygame, math, random import time, sys width, height = 800, 800 result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') surface = pygame.display.set_mode((width, height)) pygame.display.set_caption('Fibonacci Tree') screen = pygame.display.get_surface() def draw_pixel(x, y): surface.fill(pygame.Color(0, 255, 0), ((x, y), (1, 1))) pygame.display.flip() shift = 20 A_x = 0 + shift A_y = 649 + shift B_x = 750 + shift B_y = 649 + shift C_x = 375 + shift C_y = 0 + shift x, y = 400, 400 for i in range( 1, 50000): choice = random.randint(1, 3) if choice == 1: x = (x+A_x)/2 y = (y+A_y)/2 elif choice == 2: x = (x+B_x)/2 y = (y+B_y)/2 elif choice == 3: x = (x+C_x)/2 y = (y+C_y)/2 if i < 10: pass

● 120

Chapter 7 • Programming animations and games

else: draw_pixel(x, y) running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False pygame.quit() sys.exit(0)

We are now familiar with most of the code. The logic for choosing a random vertex of the triangle and then choosing a new point halfway between the current point and chosen vertex is within the for loop. This is the output:

Figure 7-6: Sierpinski Triangle with Chaos Game

7.4 Simple animation with Pygame Let's create a simple animation of a bouncing ball with the Pygame library. We will learn a lot of new things in this demonstration. Let's write code step by step. Create a new file and save it as prog06.py. Let's import the required libraries as follows: import pygame from pygame.locals import *

Let's initialize Pygame and create an object for screen: size = 720, 480 width, height = size

● 121

Kickstart to Python 3

result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((size))

We are already familiar with this entire block of code, so I won't explain it to you. Let's see some new code. Add the following code to the file: BLUE = (150, 150, 255) RED = (255, 0, 0) ball = pygame.image.load('ball_transparent.gif') rect = ball.get_rect() speed = [2, 2]

We are defining tuples for Blue and Red colors. Then we are using the routine image. load() to load an image into a variable. We can get a rectangle of the size of the image with the routine get_rect(). Finally, we are defining a list that stores the values for the speed along both axes. Let's write the code for the loop: running = True while running: for event in pygame.event.get(): if event.type == QUIT: running = False rect = rect.move(speed) if rect.left < 0 or rect.right > width: speed[0] = -speed[0] if rect.top < 0 or rect.bottom > height: speed[1] = -speed[1] screen.fill(BLUE) screen.blit(ball, rect) pygame.time.Clock().tick(240) pygame.display.flip()

We are familiar with the loop for events. So, let's discuss the next part. We are using the routine move() to move the object. We have to pass it the variable that stores values for the speed. In the if statements, we are checking if the rectangle encompassing the ball touches the borders. If it does, we reverse the speed of the ball. Then, we are filling the screen with blue. We then use the routine blit() to show the ball. The routine time.Clock().tick(240) is used to define the framerate of the animation.

● 122

Chapter 7 • Programming animations and games

Finally, we are using the routine flip() to show everything on the screen. We wrap up everything with the routine quit() as follows, pygame.quit()

The entire code is as follows: prog06.py import pygame from pygame.locals import * size = 720, 480 width, height = size result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((size))

BLUE = (150, 150, 255) RED = (255, 0, 0) ball = pygame.image.load('ball_transparent.gif') rect = ball.get_rect() speed = [2, 2] running = True while running: for event in pygame.event.get(): if event.type == QUIT: running = False rect = rect.move(speed) if rect.left < 0 or rect.right > width: speed[0] = -speed[0] if rect.top < 0 or rect.bottom > height: speed[1] = -speed[1] screen.fill(BLUE) screen.blit(ball, rect) pygame.time.Clock().tick(240) pygame.display.flip() pygame.quit()

● 123

Kickstart to Python 3

This creates a wonderful animation of a bouncing ball. The following is a screenshot of the animation:

Figure 7-7: A bouncing ball We can write a complex program using the same concept. We can have multiple bouncing balls with different speeds. This time, we will create an object for the ball. Let's see the code step-by-step: import pygame import random

This imports all the libraries. Let's define colors: BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) colors = [WHITE, RED, BLUE, GREEN]

Let's define ball size and screen resolution, size = 720, 480 width, height = size BALL_SIZE = 25

Let's define a class for balls,

● 124

Chapter 7 • Programming animations and games

class Ball: def __init__(self): self.x = 0 self.y = 0 self.change_x = 0 self.change_y = 0 self.color = colors[random.randint(0, 3)]

We are defining the co-ordinates, speed in both the dimensions, and the color (which is random). Let's define a function to create a ball and assign random values for position and speed: def make_ball(): ball = Ball() ball.x = random.randrange(BALL_SIZE, width - BALL_SIZE) ball.y = random.randrange(BALL_SIZE, height - BALL_SIZE) ball.change_x = random.randint(1, 3) ball.change_y = random.randint(1, 3) return ball

Let's initialize pygame: result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((size)) pygame.display.set_caption("Bouncing Balls")

Let's define fps (frame per second): fps = 30

Let's define a list to hold the ball objects: ball_list = []

Let's create a ball and add it to the list: ball = make_ball() ball_list.append(ball)

● 125

Kickstart to Python 3

Let's write the main loop: running = False while not running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: if len(ball_list) < 5: ball = make_ball() ball_list.append(ball) else: print("Screen already has five balls!") elif event.key == pygame.K_BACKSPACE: if len(ball_list) == 0: print("Ball list is empty!") else: ball_list.pop() elif event.key == pygame.K_q: if fps == 30: print("Minimum FPS") else: fps = fps - 30 print("Current FPS = " + str(fps)) elif event.key == pygame.K_e: if fps == 300: print("Maximum FPS") else: fps = fps + 30 print("Current FPS = " + str(fps)) elif event.key == pygame.K_r: for ball in ball_list: ball.change_x = random.randint(-2, 3) ball.change_y = random.randint(-2, 3) ball.color = colors[random.randint(0, 3)]

This is a big block and looks intimidating. However, it is simple. If we press the spacebar, it creates a new ball and adds it to the list. If there are already five balls, it does not create a new ball. If we press the backspace key, if the list of balls is not empty, it removes the last created ball. If we press the E key, it increases frames per second by 30 (which, in turn, increases the speed of the animation). If speed = 300, it does not increase it. Similarly, pressing q decreases the speed by 30. If speed is already 30, it does not decrease it any further. Pressing the R key changes the speed and color of balls randomly. We are not yet done with this block of code:

● 126

Chapter 7 • Programming animations and games

for ball in ball_list: ball.x = ball.x + ball.change_x ball.y = ball.y + ball.change_y if ball.y > height - BALL_SIZE or ball.y < BALL_SIZE: ball.change_y = -ball.change_y if ball.x > width - BALL_SIZE or ball.x < BALL_SIZE: ball.change_x = -ball.change_x

Here, in this block of code, we are changing the position of the balls and checking if they are colliding with the edges of the screen. If so, we are changing the direction. Finally, we are drawing every frame. screen.fill(BLACK) for ball in ball_list: pygame.draw.circle(screen, ball.color, [ball.x, ball.y], BALL_SIZE) pygame.time.Clock().tick(fps) pygame.display.flip()

We are filling the entire screen with black. We are then drawing the current positions of balls. Finally, we are displaying everything by using the routine flip(). pygame.quit()

In the end, we wrap up with the routine quit(). Run the program. We can use the keys Q, E, and R. We can also use space and backspace. Let's put the entire program together: prog07.py import pygame import random BLACK = (0, 0, 0) WHITE = (255, 255, 255) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) colors = [WHITE, RED, BLUE, GREEN] size = 720, 480 width, height = size BALL_SIZE = 25 class Ball: def __init__(self):

● 127

Kickstart to Python 3

self.x = 0 self.y = 0 self.change_x = 0 self.change_y = 0 self.color = colors[random.randint(0, 3)]

def make_ball(): ball = Ball() ball.x = random.randrange(BALL_SIZE, width - BALL_SIZE) ball.y = random.randrange(BALL_SIZE, height - BALL_SIZE) ball.change_x = random.randint(1, 3) ball.change_y = random.randint(1, 3) return ball result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') screen = pygame.display.set_mode((size)) pygame.display.set_caption("Bouncing Balls") ball_list = [] ball = make_ball() ball_list.append(ball) fps = 30 running = False while not running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = True elif event.type == pygame.KEYDOWN: if event.key == pygame.K_SPACE: if len(ball_list) < 5: ball = make_ball() ball_list.append(ball) else: print("Screen already has five balls!") elif event.key == pygame.K_BACKSPACE: if len(ball_list) == 0: print("Ball list is empty!") else: ball_list.pop()

● 128

Chapter 7 • Programming animations and games

elif event.key == pygame.K_q: if fps == 30: print("Minimum FPS") else: fps = fps - 30 print("Current FPS = " + str(fps)) elif event.key == pygame.K_e: if fps == 300: print("Maximum FPS") else: fps = fps + 30 print("Current FPS = " + str(fps)) elif event.key == pygame.K_r: for ball in ball_list: ball.change_x = random.randint(-2, 3) ball.change_y = random.randint(-2, 3) ball.color = colors[random.randint(0, 3)] for ball in ball_list: ball.x = ball.x + ball.change_x ball.y = ball.y + ball.change_y if ball.y > height - BALL_SIZE or ball.y < BALL_SIZE: ball.change_y = -ball.change_y if ball.x > width - BALL_SIZE or ball.x < BALL_SIZE: ball.change_x = -ball.change_x screen.fill(BLACK) for ball in ball_list: pygame.draw.circle(screen, ball.color, [ball.x, ball.y], BALL_SIZE) pygame.time.Clock().tick(fps) pygame.display.flip() pygame.quit()

● 129

Kickstart to Python 3

The following is the output:

Figure 7-8: Many bouncing balls We can modify this program to show an image in place of creating circles. Try this as an exercise.

7.5 Snake Game After learning the tricks of the trade, let's start our work with a complex game project. In this project, we will write the code for the classic game of snake. In the beginning, there is a snake composed of two squares of size 10x10 pixels. In the top left corner, we can see difficulty and score. A random 10x10 square is spawned and when the snake consumes it, it is absorbed by the snake and the snake grows in size. Once the square is consumed, another square is spawned in another random location. The snake is in a state of perpetual motion. We can move it by using WSAD keys but cannot directly reverse its direction. Let's see the code block by block: import pygame, sys, time, random

This imports all the needed libraries. Let's set the initial difficulty: difficulty = 5

Let's initialize pygame and create a window: window_size_x = 720 window_size_y = 480 result = pygame.init() if result[1] > 0: print('Error initializing Pygame : ' + str(result[1]))

● 130

Chapter 7 • Programming animations and games

sys.exit(1) else: print('Pygame initialized successfully!') pygame.display.set_caption('Snake') game_window = pygame.display.set_mode((window_size_x, window_size_y))

Let's define a few colors, black = pygame.Color(0, 0, 0) white = pygame.Color(255, 255, 255) green = pygame.Color(0, 255, 0)

Let's define initial snake segments and direction: snake_pos = [100, 100] snake_body = [[100, 100], [100-10, 100]] direction = 'DOWN' change_to = direction

and define a random position for food square: food_pos = [random.randrange(1, (window_size_x//10)) * 10, random.randrange(1, (window_size_y//10)) * 10] food_spawn = True

Let's define a few game related variables: score = 0 difficulty_counter = 0 difficulty = 5

Let's define a routine (a function) to show score: def show_score(choice, color, font, size): score_font = pygame.font.SysFont(font, size) score_surface = score_font.render('Score : ' + str(score) + ' Difficulty : ' + str(difficulty), True, color) score_rect = score_surface.get_rect() if choice == 1: score_rect.midtop = (window_size_x/10 + 30, 15) else: score_rect.midtop = (window_size_x/2, window_size_y/1.25) game_window.blit(score_surface, score_rect)

● 131

Kickstart to Python 3

It has a provision for displaying the score either in the top left in small fonts or the middle in big fonts. We display the score in the top left during the normal execution of the game and in the middle in the big fonts when the game is over. Let's define a function to be called when the game is over: def game_over(): game_over_font = pygame.font.SysFont('Times New Roman', 90) game_over_surface = game_over_font.render('Game Over', True, green) game_over_rect = game_over_surface.get_rect() game_over_rect.midtop = (window_size_x/2, window_size_y/4) game_window.fill(black) game_window.blit(game_over_surface, game_over_rect) show_score(0, green, 'Times New Roman', 20) pygame.display.flip() time.sleep(3) pygame.quit() sys.exit()

This function calls the routine show_score() that we defined earlier. Let's define the main game loop: while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0)

We are already familiar with this code. All code segments below are part of this main game loop so be careful with the indentation if you are manually typing block by block. For your convenience, I will list all code after explaining how the blocks work. Let's write the logic for capturing the keypress: elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP or event.key == ord('w'): change_to = 'UP' if event.key == pygame.K_DOWN or event.key == ord('s'): change_to = 'DOWN' if event.key == pygame.K_LEFT or event.key == ord('a'): change_to = 'LEFT' if event.key == pygame.K_RIGHT or event.key == ord('d'): change_to = 'RIGHT'

● 132

Chapter 7 • Programming animations and games

if event.key == pygame.K_ESCAPE: pygame.event.post(pygame.event.Event(pygame.QUIT))

Based on the keypress, we are setting the variable that tells us what direction we should change the snake to. Now let us see the code block that sets the variable for the direction: if change_to == 'UP' and direction != 'DOWN': direction = 'UP' if change_to == 'DOWN' and direction != 'UP': direction = 'DOWN' if change_to == 'LEFT' and direction != 'RIGHT': direction = 'LEFT' if change_to == 'RIGHT' and direction != 'LEFT': direction = 'RIGHT'

We can see if the snake is going in the UP direction, we cannot directly set it to the DOWN direction. This is true for all the other directions as well. This way we make sure the snake is not reversing the direction. Now, let's set the position of the snake: if direction == 'UP': snake_pos[1] -= 10 if direction == 'DOWN': snake_pos[1] += 10 if direction == 'LEFT': snake_pos[0] -= 10 if direction == 'RIGHT': snake_pos[0] += 10

Let's write the block that checks if the snake has consumed the food: snake_body.insert(0, list(snake_pos)) if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]: score = score + 1 difficulty_counter = difficulty_counter + 1 print(difficulty_counter) if difficulty_counter == 10: difficulty_counter = 0 difficulty = difficulty + 5

We are increasing the score and the length of the snake's body. We are also increasing the difficulty (it's the FPS, just makes the game run faster) every time by 5 after the score increases by 10. The following is the logic for spawning food:

● 133

Kickstart to Python 3

food_spawn = False else: snake_body.pop() if not food_spawn: food_pos = [random.randrange(1, (window_size_x//10)) * 10, random.randrange(1, (window_size_y//10)) * 10] food_spawn = True

Let's draw food and the snake body, game_window.fill(black) for pos in snake_body: pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 10, 10)) pygame.draw.rect(game_window, white, pygame.Rect(food_pos[0], food_pos[1], 10, 10))

Let's call the routine game_over() if snake touches borders or it's own body. if snake_pos[0] < 0 or snake_pos[0] > window_size_x-10: game_over() if snake_pos[1] < 0 or snake_pos[1] > window_size_y-10: game_over() for block in snake_body[1:]: if snake_pos[0] == block[0] and snake_pos[1] == block[1]: game_over()

Let's show the score in the top left and update the display and the FPS of the game. show_score(1, white, 'Times New Roman', 20) pygame.display.update() pygame.time.Clock().tick(difficulty)

Let's put it together in a single file as follows: Snake_Game.py import pygame, sys, time, random difficulty = 5 window_size_x = 720 window_size_y = 480 result = pygame.init() if result[1] > 0:

● 134

Chapter 7 • Programming animations and games

print('Error initializing Pygame : ' + str(result[1])) sys.exit(1) else: print('Pygame initialized successfully!') pygame.display.set_caption('Snake') game_window = pygame.display.set_mode((window_size_x, window_size_y)) black = pygame.Color(0, 0, 0) white = pygame.Color(255, 255, 255) green = pygame.Color(0, 255, 0) snake_pos = [100, 100] snake_body = [[100, 100], [100-10, 100]] direction = 'DOWN' change_to = direction food_pos = [random.randrange(1, (window_size_x//10)) * 10, random.randrange(1, (window_size_y//10)) * 10] food_spawn = True score = 0 difficulty_counter = 0 difficulty = 5 def game_over(): game_over_font = pygame.font.SysFont('Times New Roman', 90) game_over_surface = game_over_font.render('Game Over', True, green) game_over_rect = game_over_surface.get_rect() game_over_rect.midtop = (window_size_x/2, window_size_y/4) game_window.fill(black) game_window.blit(game_over_surface, game_over_rect) show_score(0, green, 'Times New Roman', 20) pygame.display.flip() time.sleep(3) pygame.quit() sys.exit() def show_score(choice, color, font, size): score_font = pygame.font.SysFont(font, size) score_surface = score_font.render('Score : ' + str(score) + ' Difficulty : ' + str(difficulty), True, color) score_rect = score_surface.get_rect()

● 135

Kickstart to Python 3

if choice == 1: score_rect.midtop = (window_size_x/10 + 30, 15) else: score_rect.midtop = (window_size_x/2, window_size_y/1.25) game_window.blit(score_surface, score_rect)

while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit(0) elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP or event.key == ord('w'): change_to = 'UP' if event.key == pygame.K_DOWN or event.key == ord('s'): change_to = 'DOWN' if event.key == pygame.K_LEFT or event.key == ord('a'): change_to = 'LEFT' if event.key == pygame.K_RIGHT or event.key == ord('d'): change_to = 'RIGHT' if event.key == pygame.K_ESCAPE: pygame.event.post(pygame.event.Event(pygame.QUIT)) if change_to == 'UP' and direction != 'DOWN': direction = 'UP' if change_to == 'DOWN' and direction != 'UP': direction = 'DOWN' if change_to == 'LEFT' and direction != 'RIGHT': direction = 'LEFT' if change_to == 'RIGHT' and direction != 'LEFT': direction = 'RIGHT' if direction == 'UP': snake_pos[1] -= 10 if direction == 'DOWN': snake_pos[1] += 10 if direction == 'LEFT': snake_pos[0] -= 10 if direction == 'RIGHT': snake_pos[0] += 10 snake_body.insert(0, list(snake_pos))

● 136

Chapter 7 • Programming animations and games

if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]: score = score + 1 difficulty_counter = difficulty_counter + 1 print(difficulty_counter) if difficulty_counter == 10: difficulty_counter = 0 difficulty = difficulty + 5 food_spawn = False else: snake_body.pop() if not food_spawn: food_pos = [random.randrange(1, (window_size_x//10)) * 10, random.randrange(1, (window_size_y//10)) * 10] food_spawn = True game_window.fill(black) for pos in snake_body: pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 10, 10)) pygame.draw.rect(game_window, white, pygame.Rect(food_pos[0], food_pos[1], 10, 10)) if snake_pos[0] < 0 or snake_pos[0] > window_size_x-10: game_over() if snake_pos[1] < 0 or snake_pos[1] > window_size_y-10: game_over() for block in snake_body[1:]: if snake_pos[0] == block[0] and snake_pos[1] == block[1]: game_over() show_score(1, white, 'Times New Roman', 20) pygame.display.update() pygame.time.Clock().tick(difficulty)

● 137

Kickstart to Python 3

Let's execute this code. The following is a screenshot of the game:

Figure 7-9: Snake game in action

Summary In this chapter, we explored the Pygame library for graphics, games, and animation in detail. We are comfortable creating small graphics, animations, and games with Pygame. In the next chapter, we will learn how to work with files of various formats. We will learn how to read and modify files programmatically.

● 138

Chapter 8 • Working with files

Chapter 8 • Working with files In the previous chapter, we learned how to work with the Pygame library. We produced demonstrations for recursion, games, animations, and simulations. In this chapter, we will explore the important topic of working with files. We store data into various file formats. We will learn how to work with the files in various formats. We will explore the following topics in detail, • Handling plaintext file • CSV Files • Handling Spreadsheets This is a detailed chapter using a considerable amount of hands-on and advanced concepts. After following this chapter, we will be comfortable with MISSING

8.1 Handling plaintext file Python comes with a provision to read data directly from a plaintext file. We do not have to import a library for this. Let's see the use of the routine open() to open a file and assign it to a file object. Create a new Jupyter notebook for the demonstrations in this chapter. Write the following code: file1 = open('test.txt', mode='rt', encoding='utf-8')

When we execute this code, it returns an error because the file does not exist. Let's modify the code to handle this: try: file1 = open('test.txt', mode='rt', encoding='utf-8') except Exception as e: print(e) It prints the following message on execution: [Errno 2] No such file or directory: 'test.txt'

Let's understand the use of the routine open() in detail. First, let's create an empty file named test.txt in the directory where we saved the notebook. Run the code again. It will run without any problems. Let's add more code. try: file1 = open('test.txt', mode='rt', encoding='utf-8') except Exception as e: print(e) finally: file1.close()

● 139

Kickstart to Python 3

Let's understand the meaning of the arguments passed to the parameter of the routine open(). The first argument is, obviously, the name of the file we need to work with. The second is the mode in which the file is opened. The third is encoding. There are various modes in which we can open a file and the following is a list of them: Mode

Meaning

r

Opens a file for reading. (default) Opens a file for writing. Creates a new file if it does not exist or truncates the file if it

w

exists.

x

Opens a file for exclusive creation. If the file already exists, the operation fails. Opens a file for appending at the end of the file without truncating it. Creates a new file

a

if it does not exist.

t

Opens in text mode. (default)

b

Opens in binary mode.

+

Opens a file for updating (reading and writing)

As we can see, by default, a file is opened in read and text mode. What we wrote earlier is equivalent to the following code: try: file1 = open('test.txt') except Exception as e: print(e) finally: file1.close()

We can also have combinations of multiple modes. The following table lists the meaning of all the combinations I have worked with up until now: Mode

Meaning Opens a file for reading only in binary format. The file pointer is placed at the begin-

rb

ning of the file. This is the default mode. Opens a file for both reading and writing. The file pointer placed at the beginning of

r+

the file. Opens a file for both reading and writing in binary format. The file pointer placed at

rb+

the beginning of the file. Opens a file for writing only in binary format. Overwrites the file if the file exists. If

wb

the file does not exist, it creates a new file for writing. Opens a file for both writing and reading. Overwrites the existing file if the file exists.

w+

● 140

If the file does not exist, it creates a new file for reading and writing.

Chapter 8 • Working with files

Opens a file for both writing and reading in binary format. Overwrites the existing file if the file exists. If the file does not exist, it creates a new file for reading and wb+

writing. Opens a file for appending in binary format. The file pointer is at the end of the file if the file exists. That is, the file is in the append mode. If the file does not exist, it

ab

creates a new file for writing. Opens a file for both appending and reading. The file pointer is at the end of the file if the file exists. The file opens in the append mode. If the file does not exist, it

a+

creates a new file for reading and writing. Opens a file for both appending and reading in binary format. The file pointer is at the end of the file if the file exists. The file opens in the append mode. If the file does not exist, it creates a new file for reading and writing.

ab+

Let's add some text (manually) to the file we created and then modify the code: try: file1 = open('test.txt', mode='rt', encoding='utf-8') for each in file1: print (each) except Exception as e: print(e) finally: file1.close()

This will read the content of a file line by line and print it. We can also read the contents of a file in a single line of code as shown below: try: file1 = open('test.txt', mode='rt', encoding='utf-8') print(file1.read()) except Exception as e: print(e) finally: file1.close()

We can read a certain number of characters by passing the number as an argument to the routine read(). try: file1 = open('test.txt', mode='rt', encoding='utf-8') print(file1.read(20)) except Exception as e: print(e) finally: file1.close()

● 141

Kickstart to Python 3

Let's see how to open a file in write mode and write data into it. try: file1 = open('test1.txt', mode='w', encoding='utf-8') file1.write('That fought with us upon Saint Crispin's day.') except Exception as e: print(e) finally: file1.close()

As we know that the mode write creates a new file or truncates an existing file with the same name, the above program creates the specified file and adds the text mentioned in the routine write(). If we run the code above multiple times, it simply truncates the old file and creates a new file with the same name and contents. Let's see how to open a file in append mode. If we use this mode, it creates a new file if the specified file does not exist. If the file exists, it will append the file with the given string. Take a look at the following code: try: file1 = open('test2.txt', mode='a', encoding='utf-8') file1.write('That fought with us upon Saint Crispin's day.') except Exception as e: print(e) finally: file1.close()

Run this code multiple times. You will find the specified file with multiple lines of the same string specified in the code. We can also use the keyword with to work files: try: with open("test.txt", "w") as f: f.write("Hello, World!") except Exception as e: print(e) finally: file1.close() We can delete a file using the following code: import os if os.path.exists("test.txt"): os.remove("test.txt") else: print("The file does not exist")

● 142

Chapter 8 • Working with files

8.2 CSV Files Let's understand how to work with CSV files. We can start by understanding what CSV files are. CSV means Comma Separated Value. This means a delimited text file that uses a comma as a separator for values. Each line of the CSV file is a data record. Before modern Relational databases, this format was (and is still used) to store records in tabular format. Many CSV files have the first row as the header row that stores the names of the fields. CSV files are closely related to DSV (delimiter separated files) file format where we use delimiters like colon and space to delimit fields. CSVs are a subgroup of DSVs. Now, let's create a CSV file manually in the same directory where we are running our program. Let's save it with the name test.csv. Add the following or similar data to the CSV file: Name,Salary Ashwin,100000 Thor,200000 Jane,300000 Cpt America,30000 Iron Man,4000000

As we can see, this is payroll-related data. You can have any data of your choice. I prefer to keep it simple for beginners. In this section, we will learn how to read the data from this and other CSV files. We have to import the built-in library CSV for this. This library comes with python as the part of batteries-included philosophy and we do not have to install it separately. Let's get started with the programming part: import csv

Let's open the file in text and read mode: file = open('test.csv')

We must treat this file as a CSV (because while it is a plaintext file, we know that there is CSV data in this). Let's do it, csvreader = csv.reader(file)

Now, we have the object that treats the file as a CSV. Let's read the first row. The first row is the header row that has the name of the columns: header = [] header = next(csvreader) header

● 143

Kickstart to Python 3

It prints the following in the output area of the Jupyter notebook, ['Name', 'Salary']

Now, we have the column names, let's extract and print the data. Let's define an empty list: rows = []

Let's extract the data and print it. After every row, we are printing a visual marker to separate the rows. Simultaneously, we are upending the list variable rows with a row from the CSV. Let' see: for row in csvreader: for data in row: print(data) print('---') rows.append(row)

This is the output: Ashwin 100000 --Thor 200000 --Jane 300000 --Cpt America 30000 --Iron Man 4000000 --We can see the data of the list: rows

This is the output: [['Ashwin', '100000'], ['Thor', '200000'], ['Jane', '300000'], ['Cpt America', '30000'],

● 144

Chapter 8 • Working with files

['Iron Man', '4000000']] This is how we extract and process the data from a CSV.

8.3 Handling Spreadsheets We can also read the data stored in spreadsheets with the extension *.xls or *.xlsx format. A spreadsheet application stores data in tabular form. It is not a plaintext format like CSV and we need specialized software to read the data stored in spreadsheets. We can also use Excel or free and open source software like LibreOffice and Apache OpenOffice. We can also write programs in python to read the data stored in the spreadsheets. Create a spreadsheet in the current directory and save it as test.xlsx. Add the following data: Food Item Color Weight Banana Yellow 250 Orange Orange 200 Grapes Green 400 Tomoto Red 100 Spinach Green 40 Potatoes Grey 400 Rice White 300 Rice Brown 400 Wheat Brown 500 Barley Yellow 500 As we can see, the data is organized in three columns. Let's read it with python. There are many libraries in Python that can read data from spreadsheets. We will use one such library, openpyxl. Let's install it. Let's first upgrade the pip utility: !python -m pip install --upgrade pip

Install the library with the following command: !pip3 install openpyxl

and import the library: import openpyxl

Open the spreadsheet file with the following code: wb = openpyxl.load_workbook('test.xlsx') print(wb) print(type(wb))

● 145

Kickstart to Python 3

It prints the following output:

Let's print the names of all the worksheets (any spreadsheet is organized as a collection of worksheets): print(wb.sheetnames)

Here is the output: ['Sheet1', 'Sheet2', 'Sheet3'] Choose a sheet for processing as follows: currSheet = wb['Sheet1'] print(currSheet) print(type(currSheet))

The output is as follows,

We can also choose the current sheet: currSheet = wb[wb.sheetnames[1]] print(currSheet) print(type(currSheet)) Print the title of the sheet: currSheet = wb[wb.sheetnames[0]] print(currSheet) print(type(currSheet)) print(currSheet.title)

Here is the output:

Sheet1 We can choose a cell and print its value: var1 = currSheet['A1'] print(var1.value)

● 146

Chapter 8 • Working with files

Here's another way: print(currSheet['B1'].value)

Yet another way is as follows: var2 = currSheet.cell(row=2, column=2) print(var2.value)

We can print the maximum number of rows and columns: print(currSheet.max_row) print(currSheet.max_column)

Using the above technique, we can retrieve and print all rows and columns. for i in range(currSheet.max_row): print('---Beginning of Row---') for j in range(currSheet.max_column): var = currSheet.cell(row=i+1, column=j+1) print(var.value) print('---End of Row---')

Here is the output: ---Beginning of Row--Food Item Color Weight ---End of Row-----Beginning of Row--Banana Yellow 250 ---End of Row-----Beginning of Row--Orange Orange 200 ---End of Row-----Beginning of Row--Grapes Green 400 ---End of Row-----Beginning of Row---

● 147

Kickstart to Python 3

Tomoto Red 100 ---End of Row-----Beginning of Row--Spinach Green 40 ---End of Row-----Beginning of Row--Potatoes Grey 400 ---End of Row-----Beginning of Row--Rice White 300 ---End of Row-----Beginning of Row--Rice Brown 400 ---End of Row-----Beginning of Row--Wheat Brown 500 ---End of Row-----Beginning of Row--Barley Yellow 500 ---End of Row--This is how we extract and process spreadsheets using Python

Summary In this chapter, we learned to read and manipulate data stored in files of different formats. In the next chapter, we will explore the domain of image processing in detail. It will be a large and detailed chapter with many code demonstrations. If you are a maker, you will find this chapter particularly interesting.

● 148

Chapter 9 • Image Processing with Python

Chapter 9 • Image Processing with Python In the previous chapter, we learned how to handle files of various types. In this chapter, we will explore another area where Python is commonly used as the preferred choice of programming language: Image Processing. For this, we will explore an image processing library in Python in great depth. The name of this library is Wand. We will explore the following topics in detail: • Digital Image Processing and Wand Library • Getting Started • Image Effects • Special Effects • Transformations • Statistical Operations • Color Enhancement • Image Quantization • Threshold • Distortions This chapter is full of advanced concepts, their in-depth description, and hands-on demonstrations. After following this chapter, we will be at ease with Image processing using Python.

9.1 Digital Image Processing and Wand Library Image Processing is the use of algorithms to process the images. In the days of analog films and motion pictures, there were techniques to improve the quality of images and frames (in a motion picture) using manual techniques like the use of chemical compounds. This was a precursor to the modern idea of image processing. Nowadays, most images are digital. Of course, digital is yet to catch up with the vibrant colors and clarity of analog (chemical film-based imaging). However, since it is cheaper, a vast majority of people and organizations (film producing and processing organizations) use digital imaging in the production of images and videos. Modern computers are also fast enough to be used for processing digital images. We usually use modern programming languages like C, C++, Java, Python, MATLAB, and GNU Octave for processing images and videos. It is very easy to process images with Python as there are many third-party libraries for this. ImageMagick is software for the manipulation of images. It comes with APIs for various programming languages. We can use the library Wand that provides a pythonic interface to ImageMagick. Let's set up the needed software to get started. We first need to install ImageMagick for our OSes. We can install the ImageMagick on macOS with the following commands: brew install ghostscript brew install imagemagick

● 149

Kickstart to Python 3

These two commands should install ImageMagick to your macOS. If not then we must install it manually. This is easy. Download the zip file located at https://download.imagemagick.org/ ImageMagick/download/binaries/ImageMagick-x86_64-apple-darwin20.1.0.tar.gz. Copy it to the home directory of your user on macOS. Extract it with the following command: tar xvzf ImageMagick-x86_64-apple-darwin20.1.0.tar.gz

Now we need to add a few entries to the file .bash_profile located in the home directory of your user on macOS. # Settings for ImageMagik export MAGICK_HOME="$HOME/ImageMagick-7.0.10" export PATH="$MAGICK_HOME/bin:$PATH" export DYLD_LIBRARY_PATH="$MAGICK_HOME/lib/"

Exit and relaunch the command prompt and run the following commands one by one: magick logo: logo.gif identify logo.gif display logo.gif

It will display the logo of the ImageMagick project. Installation on Windows is easy. There are binary executable installable files for all flavors of desktop Windows (32/64 bit). Of all the options, we need to choose the one with the description Win64/Win32 dynamic at 16 bits-per-pixel component with High-dynamic-range imaging enabled. For 64-bit systems, use https://download.imagemagick.org/ImageMagick/download/binaries/ImageMagick-7.1.010-Q16-HDRI-x64-dll.exe For 32-bit Windows use, https://download.imagemagick.org/ImageMagick/download/binaries/ImageMagick-7.1.010-Q16-HDRI-x86-dll.exe Use these files to install the ImageMagick on Windows. Let's install it on Linux. Download the source with the following command, wget https://www.imagemagick.org/download/ImageMagick.tar.gz

Let's check where it has extracted all the files: ls ImageMagick*

● 150

Chapter 9 • Image Processing with Python

It shows us the name of the directory. ImageMagick-7.1.0-10

Go to the directory. cd ImageMagick-7.1.0-10

Run the following commands one after another (if you are familiar with the Linux, you will recognize that this is a standard set of commands to install any new program on Linux distros): ./configure make sudo make install sudo ldconfig /usr/local/lib

After we successfully install the ImageMagick program, we can install the wand library on any platform with the following command: pip3 install wand

This is how we can install ImageMagick and Wand on any operating system.

9.2 Getting Started Please create a new Jupyter notebook for all demonstrations in this chapter. All the code from now on should be saved and executed in the notebook. Let's import the required libraries. from __future__ import print_function from wand.image import Image

These statements import the required modules. Let's read an image and print its dimensions as follows, img = Image(filename='D:/Dataset/4.2.03.tiff') print('width =', img.width) print('height =', img.height)

The output is as follows, width = 512 height = 512

We can also see the type of the image:

● 151

Kickstart to Python 3

type(img)

This produces the following output: wand.image.Image

We can show the image in the notebook as output just by typing in the name of the variable that stores the image. img

Thus creates the following output:

Figure 9-1: Image displayed in the Jupyter notebook I am using the image dataset provided by http://www.imageprocessingplace.com/root_files_V3/image_databases.htm. All images are standard test images frequently used in image processing. I am not using the standard test image, Lena, as I believe the origin of the image is controversial and it is disrespectful and derogatory towards women in general. We can also clone an image, change its file format, and save it to the disk as follows: img1 = img.clone() img1.format = 'png' img1.save(filename='D:/Dataset/output.png')

If you have not already noticed, I am using a Windows computer for this demo. If you are using any Unix-like OS, you have to modify the location accordingly. For example, I use the following code to save in a Raspberry Pi OS (Debian Linux variant) computer to save the output file:

● 152

Chapter 9 • Image Processing with Python

img1.save(filename='/home/pi/Dataset/output.png')

We can also create a custom image with uniform color. from wand.color import Color bg = Color('black') img = Image(width=256, height=256, background=bg) img.save(filename='D:/Dataset/output.png') Let's see how to resize an image. There are two ways: img = Image(filename='D:/Dataset/4.2.03.tiff') img1 = img.clone() img1.resize(60, 60) img1.size

The second way is as follows: img1 = img.clone() img1.sample(60, 60) img1.size

The routines resize() and sample() resize the image to specified dimensions. We can also crop a part of an image. img1 = img.clone() img1.crop(10, 10, 60, 60) img1.size

9.3 Image Effects We can have a variety of image effects. Let's start by blurring an image. img1 = img.clone() img1.blur(radius=6, sigma=3) img1

The output is as shown in Figure 9-2.

● 153

Kickstart to Python 3

Figure 9-2: A blurred image Let's apply the adaptive blur: img1 = img.clone() img1.adaptive_blur(radius=12, sigma=6) img1

We can apply the Gaussian blur: img1 = img.clone() img1.gaussian_blur(sigma=8) img1

Here is the output:

Figure 9-3: Adaptive Blur

● 154

Chapter 9 • Image Processing with Python

We can have motion blur: img1 = img.clone() img1.motion_blur(radius=20, sigma=10, angle=-30) img1

We are mentioning the angle of motion while calling the routine. Here is the output:

Figure 9-4: Motion Blur with 30-degree angle We can also have rotational blur: img1 = img.clone() img1.rotational_blur(angle=25) img1

Here is the output:

Figure 9-5: Rotational Blur

● 155

Kickstart to Python 3

We can also have selective blur: img1 = img.clone() img1.selective_blur(radius=10, sigma=5, threshold=0.50 * img.quantum_range) img1

Here is the output:

Figure 9-6: Selective Blur We can also despeckle (reduce noise) an image img1 = img.clone() img1.despeckle() img1

and detect edges: img = Image(filename='D:/Dataset/4.1.07.tiff') img1 = img.clone() img1.edge(radius=1) img1

● 156

Chapter 9 • Image Processing with Python

Here is the output:

Figure 9-7: Edge Detection We can generate a 3D emboss effect: img1 = img.clone() img1.emboss(radius=4.5, sigma=3) img1

Here is the output:

Figure 9-8: Emboss

● 157

Kickstart to Python 3

We can also change the image to greyscale and apply an image effect: img1 = img.clone() img1.transform_colorspace('gray') img1.emboss(radius=4.5, sigma=3) img1

Here is the output:

Figure 9-9: Emboss on greyscale image We can apply a smoothing filter to reduce noise: img1 = img.clone() img1.kuwahara(radius=4, sigma=2) img1

The edges are preserved in the output:

Figure 9-10: Smoothing Filter

● 158

Chapter 9 • Image Processing with Python

We can also create the shade effect: img1 = img.clone() img1.shade(gray=True, azimuth=30.0, elevation=30.0) img1

Here is the output:

Figure 9-11: Shade Filter We can sharpen an image: img1 = img.clone() img1.sharpen(radius=12, sigma=4) img1 We can apply adaptive sharpen algorithm: img1 = img.clone() img1.adaptive_sharpen(radius=12, sigma=6) img1 We can use unsharp mask: img1 = img.clone() img1.unsharp_mask(radius=20, sigma=5, amount=2, threshold=0) img1

We can also spread the pixels randomly in the specified radius: img1 = img.clone() img1.spread(radius=15.0) img1

● 159

Kickstart to Python 3

Here is the output:

Figure 9-12: Spread

9.4 Special Effects Let's study how to apply special effects to an image. The first effect is Noise. There are various types of noise. Let's see how to introduce Gaussian noise. img1 = img.clone() img1.noise("gaussian", attenuate=1.0) img1

Here is the output:

Figure 9-13: Gaussian Noise

● 160

Chapter 9 • Image Processing with Python

The following is a list of all valid character strings that can be used as names of noises: 'gaussian' 'impulse' 'laplacian' 'multiplicative_gaussian' 'poisson' 'random' 'uniform'

We can blue shift an image as follows: img1 = img.clone() img1.blue_shift(factor=0.5) img1

The output as follows,

Figure 9-14: Blue Shift We can also create the charcoal drawing effect: img1 = img.clone() img1.charcoal(radius=2, sigma=1) img1

● 161

Kickstart to Python 3

Here is the output:

Figure 9-15: Charcoal Effect

We can also apply a color matrix: img1 = img.clone() matrix = [[0, 0, 1], [0, 1, 0], [1, 0, 0]] img1.color_matrix(matrix) img1

A color matrix can have a maximum size of 6x6. In a color matrix, each column maps to a color channel to reference, and each row represents a color channel to effect. For RGB images, these are red, green, blue, n/a, alpha, and a constant (offset). For CMYK images, they are cyan, yellow, magenta, black, alpha, and a constant. In this example, we have created a 3x3 matrix. Here is the output:

● 162

Chapter 9 • Image Processing with Python

Figure 9-16: Color Matrix We can blend an image with a constant color: img1 = img.clone() img1.colorize(color="green", alpha="rgb(10%, 0%, 20%)") img1

Here is the output:

Figure 9-17: Blending with a constant color We can make an image implode: img1 = img.clone() img1.implode(amount=0.5) img1

● 163

Kickstart to Python 3

The output is as follows:

Figure 9-18: Imploding an image We can also have polaroid effect: img1 = img.clone() img1.polaroid() img1

Here is the output:

Figure 9-19: Polaroid effect

● 164

Chapter 9 • Image Processing with Python

Let's apply the sepia tone (threshold based) to the image as follows: img1 = img.clone() img1.sepia_tone(threshold=0.3) img1

Here is the output:

Figure 9-20: Sepia tone We can convert an image to a sketch: img1 = img.clone() img1.sketch(0.5, 0.0, 98.0) img1

The output is as follows:

Figure 9-21: Sketch

● 165

Kickstart to Python 3

Let's create burned effect: img1 = img.clone() img1.solarize(threshold=0.2 * img.quantum_range) img1

Here is the output:

Figure 9-22: Solarize We can Swirl an image: img1 = img.clone() img1.swirl(degree=90) img1

Here is the output:

Figure 9-23: Swirl

● 166

Chapter 9 • Image Processing with Python

We can tint an image: img1 = img.clone() img1.tint(color="green", alpha="rgb(40%, 60%, 80%)") img1

The output is as follows:

Figure 9-24: A tinted image

We can also create vignette effect: img1 = img.clone() img1.vignette(sigma=3, x=10, y=10) img1

● 167

Kickstart to Python 3

Here is the output:

Figure 9-25: Vignette Effect We can add the wave effect: img1 = img.clone() img1.wave(amplitude=img.height / 32, wave_length=img.width / 4) img1

Here is the output:

Figure 9-26: Wave Effect

● 168

Chapter 9 • Image Processing with Python

We can also have a wavelet denoise of an image: img1 = img.clone() img1.wavelet_denoise(threshold=0.05 * img.quantum_range, softness=0.0) img1

The output is as follows:

Figure 9-27: Wavelet Denoise Effect

9.5 Transformations We can apply transformations to images. Read a new image as follows: img = Image(filename='D:/Dataset/4.1.04.tiff') img

We can flip an image: img1 = img.clone() img1.flip() img1

Here is the output:

● 169

Kickstart to Python 3

Figure 9-28: Flip Effect We can also have the flop effect: img1 = img.clone() img1.flop() img1

This is the output:

Figure 9-29: Flop Effect We can also rotate the image, img1 = img.clone() img1.rotate(45, background=Color('rgb(127, 127, 127)')) img1

● 170

Chapter 9 • Image Processing with Python

The output is as follows:

Figure 9-30: Rotated image

9.6 Statistical Operations We can perform statistical operations on images. These can be performed using the routine statistic(). Let's see a few examples. Let's read and display a new image for this part: img = Image(filename='D:/Dataset/4.1.01.tiff') img

Now, let's compute median: img1 = img.clone() img1.statistic("median", width=8, height=8) img1

● 171

Kickstart to Python 3

The output is as follows:

Figure 9-31: Median In this example, we computed the median of every pixel based on the 8x8 neighborhood (passed as arguments). We can compute other statistical operations too. Let's compute the gradient: img1 = img.clone() img1.statistic("gradient", width=8, height=8) img1

The output is:

Figure 9-32: Gradient

● 172

Chapter 9 • Image Processing with Python

We can compute maximum: img1 = img.clone() img1.statistic("maximum", width=8, height=8) img1

Here's the output:

Figure 9-33: Maximum We can compute mean: img1 = img.clone() img1.statistic("mean", width=8, height=8) img1

The output is as follows:

Figure 9-34: Mean

● 173

Kickstart to Python 3

We can compute minimum: img1 = img.clone() img1.statistic("minimum", width=8, height=8) img1

The output is:

Figure 9-35: Minimum We can compute mode: img1 = img.clone() img1.statistic("mode", width=8, height=8) img1

Here's the output:

Figure 9-36: Mode

● 174

Chapter 9 • Image Processing with Python

We can compute non-peak: img1 = img.clone() img1.statistic("nonpeak", width=8, height=8) img1

The output is as follows:

Figure 9-37: Non-peak We can also compute Root Mean Square. img1 = img.clone() img1.statistic("root_mean_square", width=8, height=8) img1

● 175

Kickstart to Python 3

This is the output:

Figure 9-38: Root Mean Square Let's compute standard deviation img1 = img.clone() img1.statistic("standard_deviation", width=8, height=8) img1

Here's the output:

Figure 9-39: Standard Deviation

9.7 Color Enhancement Let's study a few routines to enhance the colors of an image. We have to use the routine evaluate() to apply various operations. We can find the list of operations at

● 176

Chapter 9 • Image Processing with Python

https://docs.wand-py.org/en/0.6.7/wand/image.html#wand.image.EVALUATE_OPS. Let's read and display a new image: img = Image(filename='D:/Dataset/4.1.03.tiff') img

We can rightshift the values of a channel by a certain bits. img1 = img.clone() img1.evaluate(operator='rightshift', value=2, channel='green') img1

The output is as follows:

Figure 9-40: Rightshift We can also apply the leftshift operation: img1 = img.clone() img1.evaluate(operator='leftshift', value=2, channel='blue') img1

● 177

Kickstart to Python 3

Here's the output:

Figure 9-41: Leftshift We have even more operations listed at https://docs.wand-py.org/en/0.6.7/wand/image. html#wand.image.FUNCTION_TYPES. We can apply these on an image using the routine function(): img1 = img.clone() frequency = 3 phase_shift = -90 amplitude = 0.2 bias = 0.7 img1.function('sinusoid', [frequency, phase_shift, amplitude, bias]) img1

Here is the output:

Figure 9-42: Sinusoidal

● 178

Chapter 9 • Image Processing with Python

Let's read a new image for the next two demonstrations: img = Image(filename='D:/Dataset/4.1.06.tiff') img

We can adjust the gamma of an image: img1 = img.clone() img1.gamma(1.66) img1

The output is as follows:

Figure 9-43: Gamma We can adjust black and white boundaries: img1 = img.clone() img1.level(black=0.2, white=0.9, gamma=1.66) img1

● 179

Kickstart to Python 3

The enhanced output is as follows:

Figure 9-44: Level

9.8 Image Quantization Image Quantization is a lossy compression technique. We can achieve it by compressing a range of color values to a single-color value. The extent of information loss depends on the total number of colors in the final output. More colors generally correspond to the retention of more information. This technique makes it possible to reduce the number of bytes required to store and transmit images. This is a very useful image processing technique. Many algorithms can perform image quantization. Let's see a few of them. First, let's choose an image, img = Image(filename='D:/Dataset/4.1.05.tiff') img

Let's apply K-Means clustering algorithm: img1 = img.clone() img1.kmeans(number_colors=4, max_iterations=100, tolerance=0.01) img1

Here is the output:

● 180

Chapter 9 • Image Processing with Python

Figure 9-45: KMeans Clustering We can also use the routine posterize() to quantize an image. We can pass different arguments for the dithering method. Let's see them one by one. Let's use the Floyd Steinberg method: img1 = img.clone() img1.posterize(levels=4, dither='floyd_steinberg') img1

Here's another method for dithering: img1 = img.clone() img1.posterize(levels=4, dither='riemersma') img1

We can also avoid using dithering: img1 = img.clone() img1.posterize(levels=4, dither='no') img1

We can also use the routine quantize() for the same purpose: img1 = img.clone() img1.quantize(number_colors=8, colorspace_type='srgb', treedepth=1, dither=True, measure_error=False) img1

● 181

Kickstart to Python 3

Here's the output:

Figure 9-46: Quantization with 8 colors

9.9 Threshold In thresholding, based on the value of channels for a pixel, we make some decisions. Let's say, we define a function that takes an argument and returns 0 if the value passed is less than 127 then the function is a thresholding function and 127 is the threshold value. We can manually define such a function in Python. The Wand library has plenty of such functions. Let's explore them one by one. Let's look at Local Adaptive Threshold. This is also known as Local Threshold or Adaptive Threshold. In this method, every pixel is adjusted as per the values of the surrounding pixel. If the pixel has a greater value than the average of its surrounding pixels, it is set to white, otherwise to black. img1 = img.clone() img1.transform_colorspace('gray') img1.adaptive_threshold(width=16, height=16, offset=-0.08 * img.quantum_range) img1

We are turning the image to greyscale and then thresholding it. This way, we can see the results. The output is as follows:

● 182

Chapter 9 • Image Processing with Python

Figure 9-47: Local Adaptive Threshold As we can see, the thresholding operation on a greyscale image results in a binary (blackand-white) image. This is known as binarization which is the simplest form of image segmentation. Let's see how we can automatically threshold an image without passing any threshold value. There are three methods. The first is the Kapur method, img1 = img.clone() img1.transform_colorspace('gray') img1.auto_threshold(method='kapur') img1

The second is the Otsu method: img1 = img.clone() img1.transform_colorspace('gray') img1.auto_threshold(method='otsu') img1

The final method is the Triangle: img1 = img.clone() img1.transform_colorspace('gray') img1.auto_threshold(method='triangle') img1

We can also omit the step where we convert the image to greyscale and apply the thresholding algorithm directly to the color image. In such cases, the algorithm is applied to all the channels of the color image. Let's see the black thresholding where we set all the pixels below the threshold value to the black color,

● 183

Kickstart to Python 3

img1 = img.clone() img1.black_threshold(threshold='#960') img1

Here's the output:

Figure 9-48: Black Threshold We can even have a color threshold where the values between the start and stop are set to white and rest are set to black: img1 = img.clone() img1.color_threshold(start='#321', stop='#aaa') img1

The Wand library has a method to apply predefined threshold maps to create dithered images. The following is a table of the map values and their meanings:

● 184

Map

Description

threshold

Threshold 1x1 (non-dither)

checks

Checkerboard 2x1 (dither)

o2x2

Ordered 2x2 (dispersed)

o3x3

Ordered 3x3 (dispersed)

o4x4

Ordered 4x4 (dispersed)

o8x8

Ordered 8x8 (dispersed)

h4x4a

Halftone 4x4 (angled)

h6x6a

Halftone 6x6 (angled)

h8x8a

Halftone 8x8 (angled)

Chapter 9 • Image Processing with Python

h4x4o

Halftone 4x4 (orthogonal)

h6x6o

Halftone 6x6 (orthogonal)

h8x8o

Halftone 8x8 (orthogonal)

h16x16o

Halftone 16x16 (orthogonal)

c5x5b

Circles 5x5 (black)

c5x5w

Circles 5x5 (white)

c6x6b

Circles 6x6 (black)

c6x6w

Circles 6x6 (white)

c7x7b

Circles 7x7 (black)

c7x7w

Circles 7x7 (white)

Let's see a simple example of the same with the last entry in the table: img1 = img.clone() img1.ordered_dither('c7x7w') img1

This produces the following output:

Figure 9-49: Dithering We can apply a random threshold between two given values: img1 = img.clone() img1.random_threshold(low=0.3 * img1.quantum_range, high=0.6 * img1.quantum_range) img1

● 185

Kickstart to Python 3

Here is the output:

Figure 9-50: Random Threshold We can also have a white threshold, which is the exact reverse of black. Set all the pixels above the given threshold to white as follows: img1 = img.clone() img1.white_threshold(threshold='#ace') img1

This is the output:

Figure 9-51: White Threshold This is how we can threshold images.

● 186

Chapter 9 • Image Processing with Python

9.10 Distortions Distortions are geometric transformations we apply to images. Geometric transformations are mathematical functions. Let's apply a simple geometric transformation to an image: img1 = img.clone() img1.distort('arc', (angle, )) img1

Here is the output:

Figure 9-52: Arc transformation Observe the output. We can see the transformation creates extra pixels that are filled by extending the edge of the original image. This is the default behavior. We can customize it with virtual pixels. So before proceeding further with more transformations, we will learn more about various methods for virtual pixels. We can fill these extra pixels with a constant color: img1 = img.clone() img1.background_color = Color('rgb(127, 127, 127)') img1.virtual_pixel = 'background' angle = 60 img1.distort('arc', (angle, )) img1

● 187

Kickstart to Python 3

This is the output:

Figure 9-53: Grey background We can see the list of all methods for virtual pixels at https://docs.wand-py.org/en/0.6.7/wand/image.html#wand.image.VIRTUAL_PIXEL_METHOD.

Let's see their demonstrations one by one. We can have a white background: img1 = img.clone() img1.virtual_pixel = 'white' angle = 60 img1.distort('arc', (angle, )) img1

We can also have a black background: img1 = img.clone() img1.virtual_pixel = 'black' angle = 60 img1.distort('arc', (angle, )) img1

We can have a transparent background: img1 = img.clone() img1.virtual_pixel = 'transparent' angle = 60 img1.distort('arc', (angle, ))

● 188

Chapter 9 • Image Processing with Python

img1

Let's use dithering for virtual pixels: img1 = img.clone() img1.virtual_pixel = 'dither' angle = 60 img1.distort('arc', (angle, )) img1

This is the output:

Figure 9-54: Dither Extended edges are the default way. We have already seen this. We can also set virtual pixels to this explicitly: img1 = img.clone() img1.virtual_pixel = 'edge' angle = 60 img1.distort('arc', (angle, )) img1

We can also use the mirror method: img1 = img.clone() img1.virtual_pixel = 'mirror' angle = 60 img1.distort('arc', (angle, )) img1

● 189

Kickstart to Python 3

Here is the output:

Figure 9-55: Mirror We can have random pixels: img1 = img.clone() img1.virtual_pixel = 'random' angle = 60 img1.distort('arc', (angle, )) img1

The output is as follows:

Figure 9-56: Random

● 190

Chapter 9 • Image Processing with Python

We can have a tile effect: img1 = img.clone() img1.virtual_pixel = 'tile' angle = 60 img1.distort('arc', (angle, )) img1

Here is the output:

Figure 9-57: Tile

9.11 Affine Transformations and Projections We can apply an affine transformation to an image. We have to provide three points and their mappings: img1 = img.clone() img1.resize(140, 70) img1.background_color = Color('rgb(127, 127, 127)') img1.virtual_pixel = 'background' args = (10, 10, 15, 15,

# Point 1: (10, 10) => (15,

15)

139, 0, 100, 20, # Point 2: (139, 0) => (100, 20) 0, 70, 50, 70

# Point 3: (0,

70) => (50,

70)

) img1.distort('affine', args) img1

● 191

Kickstart to Python 3

The result is as follows:

Figure 9-58: Affine Transformation We can also apply affine projections by providing scale, rotate, and translation factors: from collections import namedtuple Point = namedtuple('Point', ['x', 'y']) img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('skyblue') img1.virtual_pixel = 'background' rotate = Point(0.1, 0) scale = Point(0.7, 0.6) translate = Point(5, 5) args = (scale.x, rotate.x, rotate.y, scale.y, translate.x, translate.y) img1.distort('affine_projection', args) img1

This is the output:

Figure 9-59: Affine Projection

9.11.1 Arc We have already seen this transformation. Let's see it further in detail. We have to provide angles for arc and rotation: img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' args = (270, 45,

# ArcAngle # RotateAngle

) img1.distort('arc', args) img1

● 192

Chapter 9 • Image Processing with Python

Here is the output:

Figure 9-60: Arc

9.11.2 Barrel and Barrel Inverse We can have Barrel and Barrel Inverse. We need to specify four data points. The mathematical equation for barrel is as follows: Rsrc = r * ( A * r3 + B * r2 + C * r + D ) The r is the radius of the destination. Let's see the demonstration: img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' args = ( 0.2,

# A

0.0,

# B

0.0,

# C

1.0,

# D

) img1.distort('barrel', args) img1

This is the output:

Figure 9-61: Barrel

● 193

Kickstart to Python 3

The inverse barrel equation is as follows, Rsrc = r / ( A * r3 + B * r2 + C * r + D ) Let's demonstrate it, img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' args = ( 0.0,

# A

0.0,

# B

-0.5,

# C

1.5,

# D

) img1.distort('barrel_inverse', args) img1

Here is the output:

Figure 9-62: Inverse Barrel

9.11.3 Bilinear Transformation In this, we have to specify four source and destination points: from itertools import chain img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' source_points = ( (0, 0), (140, 0), (0, 92), (140, 92)) destination_points = ( (14, 4.6), (126.9, 9.2), (0, 92),

● 194

Chapter 9 • Image Processing with Python

(140, 92)) order = chain.from_iterable(zip(source_points, destination_points)) arguments = list(chain.from_iterable(order)) img1.distort('bilinear_forward', arguments) img1

The output is as follows:

Figure 9-63: Bilinear We can have reverse bilinear: order = chain.from_iterable(zip(destination_points, source_points)) arguments = list(chain.from_iterable(order)) img1.distort('bilinear_reverse', arguments) img1

Here is the output:

Figure 9-64: Reverse Bilinear

9.11.4 Cylinder and Planar We can convert a planar image to a cylinder as follows: import math img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' lens = 60 film = 35 args = (lens/film * 180/math.pi,) img1.distort('plane_2_cylinder', args) img1

● 195

Kickstart to Python 3

This is the output:

Figure 9-65: We can convert cylinder to plane: img1.distort('cylinder_2_plane', args) img1

Here's the output:

Figure 9-66: Cylinder to Plane

9.11.5 Polar and Depolar We can convert an image to Polar: img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' img1.distort('polar', (0,)) img1

The output is as follows:

Figure 9-67: Polar

● 196

Chapter 9 • Image Processing with Python

We can also depolar an image: img1.distort('depolar', (-1,)) img1

The output is:

Figure 9-68: Polar

9.11.6 Polynomial We can apply a polynomial: Point = namedtuple('Point', ['x', 'y', 'i', 'j']) img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' order = 1.5 alpha = Point(0, 0, 26, 0) beta = Point(139, 0, 114, 23) gamma = Point(139, 91, 139, 80) delta = Point(0, 92, 0, 78) args = (order, alpha.x, alpha.y, alpha.i, alpha.j, beta.x, beta.y, beta.i, beta.j, gamma.x, gamma.y, gamma.i, gamma.j, delta.x, delta.y, delta.i, delta.j) img1.distort('polynomial', args) img1

This is the output:

Figure 9-69: Polynomial

● 197

Kickstart to Python 3

9.11.7 Shepards We can apply a shepards transformation: img1 = img.clone() img1.resize(140, 92) img1.background_color = Color('black') img1.virtual_pixel = 'background' alpha = Point(0, 0, 30, 15) beta = Point(70, 46, 60, 70) args = (*alpha, *beta) img1.distort('shepards', args) img1

Here's the output:

Figure 9-70: Shepards

Summary In this chapter, we explored the domain of image processing. We have looked at many routines from the Wand library for processing digital images. We will continue our journey of exploring Python in the next chapter. We will look at a few more useful topics in detail.

● 198

Chapter 10 • A few useful topics in Python

Chapter 10 • A few useful topics in Python In the previous chapter, we learned how to work with images and apply various image processing techniques to enhance their quality. This chapter covers an array of topics that I could not add to other chapters Following is the list of topics we will be learning in this chapter: • Command Line Arguments • Word Cloud This chapter will hopefully make us comfortable with the concepts mentioned above.

10.1 Command Line Arguments We learned in the very first chapter how to launch a Python script or program from the command line. We can also process command line arguments. Let's see how this is done. Take a look at the following program, prog00.py #!/usr/bin/python3 import sys n = len(sys.argv) print("Total arguments passed: ", n) print("\nArguments passed: \n") for i in range(0, n): print(sys.argv[i], end = "\n")

We are using the built-in sys module to process the command line arguments. If we run this program from any IDE, it will print the following result: Total arguments passed: 1 Arguments passed: C:/Users/Ashwin/Google Chaptet10/prog00.py

Drive/Elektor/Python

Book

Project/Code/

The first argument is always the name of the script. Launch it from the command line (cmd / powershell in windows or terminal emulator of Unix like operating system) as follows: python prog00.py 1 "test 123" test

● 199

Kickstart to Python 3

Here is the output: Arguments passed: prog00.py 1 test 123 test This is how we can process command line arguments in Python. It is a very useful technique that can be used in programming text-based utilities.

10.2 Worldcloud Word clouds are also known as tag clouds. They are visual representations of the frequency of the keywords in the source document so that the most frequently occurring word has the biggest size. This applies to all the other keywords in the document and we get a cloud-like visual shape. There are many tools available to generate word clouds. We can also generate them programmatically. Let's get started with the programming part. Create a new notebook for this section. Run the following command to install the required libraries: !pip3 install matplotlib pandas wordcloud pillow

Matplotlib is the visualization library in the Scientific Python ecosystem. Pandas is the data analysis library in Python. Pillow is an image processing library. Wordcloud is a library for creating word clouds. Let's run the magic command that enables visualizations in the notebook: %matplotlib inline

Import all the required modules and libraries: from wordcloud import WordCloud, STOPWORDS import matplotlib.pyplot as plt import pandas as pd from PIL import Image

I have downloaded a zip file containing the CSV file from https://archive.ics.uci.edu/ml/machine-learning-databases/00380/YouTubeSpam-Collection-v1.zip We can then extract the CSV file from the compressed file and use it in our program. We can read the CSV file with a routine from the pandas library.

● 200

Chapter 10 • A few useful topics in Python

df = pd.read_csv(r"Youtube05-Shakira.csv", encoding = "latin-1")

Let's define two variables: key and stop words, comment_words = ' ' stopwords = set(STOPWORDS) stopwords

The variable for the keywords is an empty string. We can add keywords to the list as follows: for val in df.CONTENT: val = str(val) tokens = val.split() for i in range(len(tokens)): tokens[i] = tokens[i].lower() for words in tokens: comment_words = comment_words + words + ' ' comment_words

Let's generate the word cloud as follows, wordcloud = WordCloud(width= 1920, height=1080, background_color='white', stopwords = stopwords, min_font_size = 10).generate(comment_words)

Let's visualize it with the Matplotlib library. plt.imshow(wordcloud) plt.axis('off') plt.tight_layout(pad=0) plt.show()

● 201

Kickstart to Python 3

The output is as follows,

Figure 10-1: Word cloud with the default settings We can generate a word cloud from a text string. Let's create a string: text=("Python is an interpreted, high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python's design philosophy emphasizes code readability through use of significant whitespace. Its language constructs and object-oriented approach aim to help programmers write clear, logical code for small and large-scale projects.")

Let's generate a word cloud: # Create the wordcloud object wordcloud = WordCloud(width= 1280, height=720, margin = 0).generate(text) # Display the generated image: plt.imshow(wordcloud, interpolation='bilinear') plt.axis("off") plt.margins(x=0, y=0) plt.show()

● 202

Chapter 10 • A few useful topics in Python

Here is the output:

Figure 10-2: Word cloud of a text string as the source We can also generate the word cloud in such a way that it only shows a set number of words. These words are the most frequently occurring ones in the source: wordcloud = WordCloud(width= 1280, height=720, max_words = 3).generate(text) plt.figure() plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.margins(x=0, y=0) plt.show()

Here is the output:

Figure 10-3: Word cloud with a set number of words

● 203

Kickstart to Python 3

If you run all cells containing the code samples again, you will see that similar but different images are generated. Since the position and color are randomly assigned to the words in the cloud, we get slightly different images for the same data. We can remove some words from the final output as follows: wordcloud = WordCloud(width= 1280, height=720, stopwords = ['Python', 'code']).generate(text) plt.figure() plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.margins(x=0, y=0) plt.show()

Here is the output:

Figure 10-4: Word cloud with a few keywords omitted We can also change the background color: wordcloud = WordCloud(width= 1280, height=720, background_color='skyblue').generate(text) plt.figure() plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.margins(x=0, y=0) plt.show()

● 204

Chapter 10 • A few useful topics in Python

This is the output:

Figure 10-5: Word cloud with a custom color for the background We can also change the color of the words: wordcloud = WordCloud(width= 1280, height=720, colormap='Blues').generate(text) plt.figure() plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.margins(x=0, y=0) plt.show()

The output is as follows:

Figure 10-6: Word cloud with acustom colormap for the words

● 205

Kickstart to Python 3

Another color for the text can be set using this technique: wordcloud = WordCloud(width= 1280, height=720, colormap='autumn').generate(text) plt.figure() plt.imshow(wordcloud, interpolation="bilinear") plt.axis("off") plt.margins(x=0, y=0) plt.show()

Here is the output:

Figure 10-7: Word cloud with another custom colormap for the words

Summary In this chapter, we explored some important and useful topics in Python. We learned how to accept and process command line arguments. We also learned how to create graphical word clouds.

Conclusion With this chapter, our journey of exploring Python comes to an end. Remember the ocean of Python Programming language is vast and deep. In this book, we have had a generalized overview of Python programming. We can use Python programming language in various domains like automation, system programming, automation of tests, graphics, computer vision, machine learning, and AI. Armed with the basic knowledge of Python, you now can dive deeper into any (or all, if you wish) of the above areas.

● 206

Index

Index @abstractmethod 58

Flip Effect Floral pattern Floyd Steinberg method fps (frame per second) Fractal

170 98 181 125 108

G Gaussian noise Geany get_rect() GNU Octave

160 17 122 149

H Hexagonal spiral H-Fractal

104 108

I IDLE ImageMagick Inheritance IPython issubclass()

13 150 51 19 52

K key:value Koch Snowflake

29 107

80 10 183

A Affine Transformation Animation Animations Append mode Arc Argument

191 121 112 142 192 37

B Barrel Barrel Inverse Basic Inheritance blit() Bouncing ball Chaos Game Character Circular buffer Classes CMYK images CPython CSV Files Customize installation Cylinder

193 193 52 122 124 120 34 84 44 162 17 143 11 195

D Data Structures Debian Deque Digital Image Processing Dithering draw.line() Drawing circles

69 13 78 149 185 116 95

L LIFO Linux Local Adaptive Threshold M Modularity Multiline Docstrings

47 46

E Eclipse Marketplace Edge Detection Exceptions exit()

17 157 62 15

N Nano Notepad

17 17

F False Fibonacci Trees

33 104

O Object-Oriented OOP open()

42 42 139

● 207

Kickstart to Python 3

openpyxl Ordered data structure

145 21

P Pip Planar Polymorphism Polynomial Pop print() Pygame Pygame library Python Documentation Strings Python IDE Python interpreter Python Package Index Python Setup Python Software Foundation Raspberry Pi OS

18 195 59 197 77 32 112 121 44 17 15 18 11 9 10

R Recursion remove() Root Mean Square Rotational blur Running

29 28 175 155 70

S Sierpinski Triangle Smoothing filter Snake speed() Spreadsheets Stack statistic() Strings super() Syntax Errors

120 158 130 97 145 77 171 32 54 62

T Thonny Threshold Tkinter Tuples Turtle TypeError

17 182 90 26 90 64

● 208

W Wand Wand Library Wave Effect Worldcloud WSAD keys

149 149 168 200 130

Z Zigzag function

102

books books

Kickstart to Python 3 An Ultra-Rapid Programming Course This book serves as the very first step to for novices to learn Python programming. The book is divided into ten chapters. In the first chapter, readers are introduced to the basics of Python. It has the detailed instructions for installation on various platforms such as macOS, Windows, FreeBSD, and Linux. It also covers the other aspects of Python programming such as IDEs and Package Manager. The second chapter is where the readers get an opportunity to have a detailed hands-on with Python programming. It covers a group of built-in data structures popularly known as Python Collections. The third chapter covers the important concepts of strings, functions, and recursion. The fourth chapter focuses on the Object-Oriented Programming with Python. The fifth chapter discusses most commonly used custom data structures such as stack and queue. The sixth chapter spurs the creativity of the readers with Python’s Turtle graphics library. The seventh chapter explores animations and game development using the Pygame library. The eighth chapter covers handling data stored in a variety of file formats. The ninth chapter covers the area of Image processing with Wand library in Python. The tenth and the final chapter presents an array of assorted handy topics in Python.

Ashwin Pajankar holds a Master of Technology degree from IIIT Hyderabad, and has over 25 years of programming experience. He started his journey into programming and electronics with the BASIC programming language and is now proficient in assemblylanguage programming, C, C++, Java, Shell Scripting, and Python. Further technical experience includes single-board computers such as the Raspberry Pi, Banana Pro, and Arduino.

The entire book follows a step-by-step approach. The explanation of the topic is always followed by a detailed code example. The code examples are also explained in suitable detail and they are followed by the output in the form of text or screenshot wherever possible. Readers will become comfortable with Python programming language by closely following the concepts and the code examples in this book. The book also has references to external resources for readers to explore further. A download of the software code, and links to tutorial videos can be found on the Elektor website.

Elektor International Media BV www.elektor.com