Python Data Persistence – pyodbc Module

Python Data Persistence – pyodbc Module

ODBC is a language and operating system independent API for accessing relational databases. The product module enables access to any RDBMS for which the respective ODBC driver is available on the operating system. Most of the established relational database products (Oracle, MySQL, PostgreSQL, SQL Server, etc.) have ODBC drivers developed by the vendors themselves or third-party developers.

In this section, we access ‘mydb’ database deployed on the MySQL server. First of all, verify if your OS has a corresponding ODBC driver installed. If not, download MYSQL/ODBC connector compatible with your OS, MySQL version, and hardware architecture from MySQL’s official download page: https://dev.mysql.com/downloads/connector/odbc/ and perform the installation as per instructions.

The following discussion pertains to MySQL ODBC on Windows OS. You need to open the ODBC Data Sources app in the Administrative Tools section of the control panel, add a newly installed MySQL driver, if it doesn’t have the same already, and configure it to identify by a DSN (Data Source Name) with the help of MySQL sever’s user ID and password, pointing towards ‘mydb’ database.(figure 8.1)

Python Data Presistence - pyodbc Module chapter 8 img 1

This ‘MySQLDSN’ is now available for use in any application including our Python interpreter. You need to install pyodbc module for that purpose.

Start the Python interpreter and import this module. Its connect () function takes the DSN and other login credentials as arguments.

Example

>>> con=pyodbc.connect("DSN=MYSQLDSN;UID=root")

Once we obtain the connection object, the rest of the operations are exactly similar to that described with reference to the sqlite3 module. You can try creating Customers and Invoices tables in mydb database using their earlier structure and sample data.
In conclusion, we can say that the DB-API specification has made database handling very easy and more importantly uniform. However, data in SQL tables is stored basically in primary data types only which are mapped to corresponding built-in data types of Python. Python’s user-defined objects can’t be persistently stored and retrieved to/from SQL tables. The next chapter deals with the mapping of Python classes to SQL tables.

Python Data Persistence SQLAIchemy

Python Data Persistence – Python – SQLAIchemy

The concluding paragraph of the previous chapter briefly talked about the disparity between type systems of SQL and object-oriented programming languages such as Python. Apart from Python’s Number (that too int and float only, not complex) and string types (which are generally called scalar types), SQL doesn’t have an equivalent data type for others such as diet, tuple, list, or any user-defined class.

If you have to store such an object in a relational database, it must be deconstructed into SQL data types first, before performing INSERT operation. On the other hand, a Python object of the desired type will have to be constructed by using data retrieved from a SQL table, before a Python script is able to process it.

Let’s take the case of ‘Products’ table in the SQLite database used in the previous chapter. Its structure is as follows:

Example

CREATE TABLE Products (
ProductID    INTEGER     PRIMARY KEY AUTOINCREMENT,
Name     TEXT (20),
Price       INTEGER
) ;

On the other side, Python script has a Products class and its object is populated with data as below:

Example

class Product 
def __init__(self, id, name, price):
           self.id=id
           self.name=name
           self.price=price
p1=Product(1, Laptop 1,25000)

Following sqlite3 module syntax, the following statement will insert pi object in the Products table:

Example

cur.execute("insert into products values 
(?,?,?);",(self.id, self.name, self.price))

Similarly, following statements will store retrieved data in an object of Products class.

Example

cur.execute('select * from products where name=?',
(1 Laptop',)) row=cur.fetchone()
p1=Products(row[0], row[1],row[2])

As you can see, this involves a tedious and explicit packing and unpacking of Python objects in order to be compatible with SQL data types. This is where Object Relational Mappers are useful.

WhatisORM?

An Object Relation Mapper (ORM) library provides a seamless interface between a class and a SQL table. A class is mapped to a certain table in the database, so that cumbersome to and fro conversion between object and SQL types are automated. The products class in Python code can be mapped to the Products table in the database. As a result, all CRUD operations are done with the help of objects only, not requiring hard-coded SQL queries to be used in Python script.

ORMs thus provides an abstraction layer over the raw SQL queries, thus enabling rapid application development. Such ORM libraries are available for most programming languages including Python. SQLAlchemy is a popular database toolkit widely used by Python developers. SQL ALchemy’s ORM system transparently synchronizes all changes in the state of an object of a user-defined class with its related row in the database table.

SQLAlchemy interacts with a certain type of database in association with the respective DB-API compliant module. Its dialect system is able to establish interaction with a database through the latter’s DB-API driver. That means you should have a corresponding DB-API module also installed along with SQLAlchemy to be able to use a particular type of RDBMS.

As a matter of fact, SQLALchemy library also contains, in addition to ORM API, the SQL Expression Language (SQLAlchemy Core) that executes primitive constructs of the relational database directly. While our focus in this chapter is on SQLALChemy ORM, we shall also briefly SQL Expression language in the end. (figure 9.1)

Python Data Presistence - Python - SQLAIchemy chapter 8 img 1

In most cases, SQLAlchemy is installed with the help of a pip utility. As explained in —, a virtual environment with SQLAlchemy installed will be used for this chapter. We need to activate it and start a Python interpreter.

Example

E:\SQLAlchemyEnv>scripts\activate 
(SQLAlchemyEnv) E:\SQLAlchemyEnv>python 
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 23:09:28) 
[MSC v.1916 64 bit (AMD64)] on Win32 
Type "help", "copyright", "credits" or "license" for more information.
>>>

ORM – Session object

Now that we have created the Products table in the database, the next step is to start the transaction session. A session object is a handle used to interact with the database. We define a Session class that will serve as a factory for new Session objects with the help of the session maker () function.

from sqlalchemy.orm import sessionmaker 
Session = sessionmaker(bind=engine)

Here the engine is the Engine object that represents a connection with our database. Whenever you need to have a conversation with the database, you instantiate a Session:

session  = Session ( )

The session remains in force till changes to the database are committed and/or the close () method is called on a session object.

Python Data Persistence – SQLAlchemy ORM

Python Data Persistence – SQLAlchemy ORM

The first step is to connect to a database by using the create_engine () function in sqlalchemy module. This function should be provided with the URL of the database. The easiest way is to connect to an in-memory SQLite database.

Example

>>> from sqlalchemy import create_engine
>>> engine=create_engine('sqlite:///:memory:')

To connect to a SQLite database file use URL similar to following: engine =create_engine(‘sqlite:///mydb.sqlite’)

As you know, the Python library has in-built support for SQLite in the form of a DB-API compatible sqlite3 module. However, for other databases, their respective module needs to be installed. In order to connect to a different database (other than SQLite), its corresponding connection string includes the dialect and module. The general format of use of the create_engine () function is as follows:

dialect+driver://username:password®host:port/ database

Hence, to connect to a MySQL database using pymysql module, we need to use the following statement:

engine = create_engine('mysql+pymydsql://root@ localhost/mydb')

This assumes that the MySQL server’s username is ‘roof with no password set. The create_engine () function returns Engine object. It represents the interface to the database. The ORM doesn’t use the Engine directly once created but is used behind the scenes. This function can accept the optional ‘echo’ argument which is False by default. If set to True, it causes the generated SQL to be displayed by the Python interpreter.

>>> engine=create_
engine(1sqlite:///:memory:',echo=True)

 

Python Data Persistence – ORM – Table Object and Mapped Class

Python Data Persistence – ORM – Table Object and Mapped Class

The next step is to describe the database tables and define the mapping classes. An object of a metaclass, called Declarative Base class that stores a catalog of user-defined classes and mapped tables is first obtained. This Declarative Base class is defined in sqlalchemy. ext.declarative sub-module.

>>> from sqlalchemy.ext.declarative import 
declarative_base
>>> base=declarative_base( )

Use this ‘base’ class to define mapped classes in terms of it. We define the Products class and map it to the Products table in the database. Its table name property defines this mapping. Other attributes are column names in the table.

Example

#myclasses.py
from sqlalchemy.ext.declarative import declarative_ 
base
from sqlalchemy import Column, Integer, String base=declarative_base( ) 
class Product(Base):
tablename = 'Products'

ProductID = Column(Integer, primary_key=True) 
name = Column(String) 
price = Column(Integer)

The column is a SQL Alchemy schema object that represents column in the database table. Its constructor defines name, data type, and constraint parameters. The Column data type can be any of the following generic data types that specify the type in which Python data can be read, written, and stored. SQLAlchemy will choose the best database column type available on the target database when issuing a CREATE TABLE statement.

  • Biglnteger
  • Boolean
  • Date
  • DateTime
  • Float
  • Integer
  • Numeric
  • Smalllnteger
  • String
  • Text
  • Time

Even though this class defines mapping, it’s a normal Python class, in which there may be other ordinary attributes and methods as may be required by the application.

The Table object is created as per the specifications in the class and is associated with the class by constructing a Mapper object which remains behind the scene and we normally don’t need to deal with it directly.

The Table object created in the Declarative system is a member of the MetaData attribute of the declarative base class. The create_all ( ) method is called on metadata, passing in our Engine as a source of database connectivity. It will emit CREATE TABLE statements to the database for all tables that don’t yet exist.

base.metadata.create_all(engine)

Complete process explained above is stored as a script (addproducts.py) in the root folder of our virtual environment.

Example

from sqlalchemy import Column, Integer, String 
from sqlalchemy.ext.declarative import declarative_ base
from sqlalchemy import create_engine
from myclasses import Product, base
engine = create_engine('sqlite:///mydb.sqlite',echo=True)
base.metadata.create_all(engine)

We run this script from the command prompt (from within our virtual environment of course). The command window will show, apart from other logging information, the equivalent CREATE TABLE statement emitted by SQLALchemy. (figure 9.1)

(SQLAlchemyEnv) E:\SQLAlchemyEnv>python class-table-mapping . py
PRAGMA table_info("Products")
( )
CREATE TABLE "Products" (
"ProductID" INTEGER NOT NULL, 
name VARCHAR, 
price INTEGER,
PRIMARY KEY ("ProductID")
)
( )
COMMIT

Python Data Persistence – ORM – Add Data

Python Data Persistence – ORM – Add Data

To add data in the ‘Products’ table, first initialize an object of its mapped Products class, add it to the session and commit the changes.

Example

p1 = Products(name='Laptop 1, price = 25000) 
sessionobj.add(p1) 
sessionobj.commit( )

Add above code snippets to addproducts.py. It now looks like this:

from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
from myclasses import Products,base
engine = create_engine('sqlite:///mydb.sqlite', echo=True)
base.metadata.create_all(engine) 
from sqlalchemy.orm import sessionmaker 
Session = sessionmaker(bind=engine) 
sessionobj = Session()
p1 = Product(name='Laptop', price=25000) 
sessionobj.add(p1) 
sessionobj.commit( )

Run the above script from the command prompt. SQLAlchemy will emit equivalent parameterized INSERT query that will be echoed on the terminal as shown below in figure 9.2:

(SQLAlchemyEnv) E:\SQLAlchemyEnv>addproducts.py 
PRAGMA table_info("Products")
( )
BEGIN (implicit)
INSERT INTO "Products" (name, price) VALUES (?, ?) ('Laptop', 25000)
COMMIT

If you want to confirm, open the database in SQLite console and view’ rows in Products table, (figure 9.3)

sqlite> .head on
sqlite> .mode column
sqlite> .open mydb.sqlite
sqlite> select * from products;
ProductID        name          price
———-         ——-         ——
1               Laptop         25000

To add multiple records at once, call the add_all() method on the session object. It requires a list of objects to be added.

Example

p2=Products(name='TV',price=40000) 
p3=Products(name=1 Router',price = 2 000) 
p4 = Products(name=1 Scanner 1,price = 5000) 
p5 = Products(name='Printer' ,price = 9000) 
p6=Products(name='Mobile',price=15000) 
sessionobj.add_all( [p2,p3,p4,p5,p6]) 
sessionobj.commit( )

Go ahead and add the ‘Customers’ class mapped to the ‘Customers’ table. Add data as per sample data given. (We shall add ‘Invoices’ class and ‘Invoices’ table a little later)

Example

class Customer(base):
table name ='Customers'
CustID=Column(Integer, primary_key=True) 
name=Column(String)
GSTIN=Column(String)

We have to add this table in the database schema by executing the following statement again:

base.metadata.create_all(engine)

Python Data Presistence – Row Object

Python Data Presistence – Row Object

By default, each row in the query result set is a tuple of values belonging to the column list in the SELECT statement. In the above example, the row object returns a tuple.

Example

>>> row=cur. f etchone ( )
> > > row
(2, 'TV', 40000)
>>> type(row)
<class 'tuple'>

The order of columns in the tuple cannot be ascertained from the object itself. The connection object has a useful ‘row_£actory’ property with which row in the result set can be converted into some meaningful representation. This can be done either by assigning a row factory to a user-defined function that will return a custom object or by setting it to the constructor of the Row class.

Row class has been defined in the sqlite3 module, whose primary purpose is to be used as a row factory. As a result, the row of the result set is returned as a Row object. Row class defines a keys () method that returns column names used in the SELECT statement. Values are accessible using the index as well as by name.

Example

>>> r=cur.fetchone( )
>>> type(r)
<class 'sqlite3.Row'>
>>> r.keysO
t'ProductID', 'Name', 'Price']
>>> fields=r .keys ( )
>>> r[1]
'TV'
> > > r['name']
'TV'
>>> for nm in fields:
print (nm, r[nm])
ProductID 2
Name TV
Price 40000

 

Python Data Presistence – while Statement

Python Data Presistence – while Statement

The while keyword constructs a conditional loop. Python interpreter evaluates the boolean expression and keeps on executing the subsequent
uniformly indented block of statements as long as it holds true. The moment it is no longer true, the repetition stops, and the program flow proceeds to the next statement in the script. A syntactical representation of usage of while loop is as follows:

Example

#using while statement
while expression==True:
#while block
. . .
end of while block
#rest of the statements

One way to control repetition is to keep its count and allow the next round of execution of block till the count exceeds the desired limit. In the following code snippet, the block executes repeatedly till the count is <= 5.

Example

#while-1.py
count=0
while count<5:
#count repetitions
count=count+1
print ("This is count number",count)
print ("end of 5 repetitions")

Output

E:\python 3 7>python whi1e- 1.py 
This is count number 1 
This is count number 2 
This is count number 3 
This is count number 4 
This is count number 5 
end of 5 repetitions 

E:\python37>

The expression in while statement is executed before each round of repetition (also called iteration). Here is another example to consolidate your understanding of the while loop.

The following code generates the list of numbers in the Fibonacci series. First, the two numbers in the list are 0 and 1. Each subsequent number is the sum of the previous two numbers. The while loop in the code adds the next 10 numbers. The iterations are counted with the help of variable ‘i’. the sum of numbers at i* and (Ml)* position is appended to the list till ‘i’ reaches 10.

Example

#fibolist . py
FiboList= [ ]
i = 0
max=1
FiboList.append(i)
FiboList.append(max)
while i<10:
#next number is sum of previous two
max=FiboList[i]+FiboList[i+1]
FiboList.append(max)
i = i + l
print ('Fibonacci series:1,FiboList)

Output

E : \py thon3 7 >py thon fibolist.py 
Fibonacci series: [ 0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89] 

E:\python37>

Python Data Presistence – Nested Loops

Python Data Presistence – Nested Loops

Nesting is a very popular term in programming parlance. It indicates the existence of a certain entity inside another that is of the same type. If you have a situation where an if statement appears inside another if statement, it is termed as nested if. Similarly, a loop within another loop constitutes nested loops. It is also possible to have nested functions, classes, and so on. We are going to discuss nested loops here.
As mentioned above, a nested loop means the existence of a loop within a loop. The following diagram illustrates the situation (figure 2.19):

Python Data Presistence - Nested Loops chapter 2 img 1

What happens when such nesting of the loop is done? Each repetition of the outer loop encounters the inner loop which has to complete its own repetitions before the next round of the outer loop starts. As a result, if the outer loop is designed to perform m iterations and the inner loop is designed to perform n iterations, the innermost statement will be executed men times.

There are a number of instances around us where looping activities are performed in a nested manner. The clock for example has three loops. Outer loop counting hours has a nested loop for minutes, which in turn has a seconds loop.

Any type of loop (while or for) can appear in any other type. However, in practice, we find nesting for loops. The following example displays all prime numbers between 1 and 100. Outer loop iterates over a range object. Inner loop checks whether each number in the outer range is prime or not.

Example

#nestedfor.py
for num in range ( 1 , 101):
for x in range(2,num):
if num%x==0:
break
x=x+1
else:
print (num,sep=’ ‘, end = ‘ ‘ )

Output:

E:\python37>python nestedfor.py
1 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 93 59 61 67 71 73 79 83 89 97
E:\python37>

 

 

Python Data Persistence – Excel with Pandas

Python Data Persistence – Excel with Pandas

Pandas library is extremely popular with data scientists as it provides easy-to-use tools for data manipulation and analysis. Different types of data structures are available in Pandas. Of which, the data frame is most commonly used. Dataframe in Pandas represents a two-dimensional tabular data structure with labeled columns which may be of different data types.

Before we explore the DataFrame object and its relationship with Excel, we have to ensure that the Pandas package is installed in the current Python environment. If you are using Anaconda distribution, Pandas is already installed in it. Otherwise, you may have to get it using pip utility in the virtual environment we have created for this chapter.

E:\excelenv>scripts\activate
(excelenv) E:\excelenv>scripts\pip3 install pandas

During installation, few more libraries like NumPy and others are also installed as they are internally used by Pandas.
As mentioned earlier, the DataFrame object of Pandas is a two-dimensional table-like structure, with labeled columns that may be of different data types (This is similar to SQL table isn’t it?). It can be constructed using various data objects as inputs such as Python lists or dictionaries. Of particular relevance to us in this chapter is creating a DataFrame object using a list of dictionary items.
Let us first define a list, each item in which is a dictionary object. It has three k-v pairs as shown below:

Example

>>> pricelist=[{1ProductID1 1, 'Name ':'Laptop', 'price': 25000} ,
{'ProductID' : 2 , 'Name':' TV' ,'price' : 40000} ,
{'ProductID' : 3 , 'Name 1 : ' Router', 'price’ : 2 0 0 0 } ,
{'ProductID' :4, 'Name':' Scanner', 'price' : 5000 } ,
{'ProductID1 : 5, 'Name':' Printer 1,'price' : 9000} ]

Use this list object as an argument to the constructor of the DataFrame object. Example 10.15

>>> import pandas as pd
>>> df=pd.DataFrame(pricelist)
>>> df
Name ProductID price
0 Laptop 1 25000
1 TV 2 40000
2 Router 3 2000
3 Scanner 4 5000
4 Printer 5 9000

Example

Incidentally, conversion to/from DataFrame and many other data formats is possible. This includes JSON, CSV, pickle, SQL, and so on. As a quick example, we shall try to read SQLite table data, using the read_sql_ query () function.

Example

>>> import pandas as pd
>>> import sqlite3
>>> con=sqlite3.connect('mydb.sqlite')
>>> df = pd.read_sql_query("SELECT * FROM Products;", con)
>>> df
ProductID Name Price
0 1 Laptop 27500
1 3 Router 3000
2 4 Scanner 5500
3 5 Printer 11000
4 6 Mobile 16500

At the conclusion of this chapter, you must have got a fair idea of how you can use Python to manipulate Excel workbook documents. While openpyxl package is all about automating the functionality of Excel software, data in Excel sheets can be brought in Pandas data frames for high-level manipulations and analysis and exported back.

The next two chapters of this book deal with the exciting world of NOSQL databases and the way Python can interact with two of very popular NOSQL databases – MongoDB, and Cassandra.

Python Data Persistence – User Defined Modules

Python Data Persistence – User Defined Modules

A module is a collection of Python objects such as functions, classes, and so on. Python interpreter is bundled with a standard library consisting of a large number of built-in modules, some of which we got acquainted with. Just as built-in modules, you can create a collection of your own functions and import them in an interactive Python session or in another script.

Any Python script (having .py extension) can be used as a module. The idea behind a user-defined module is the same as that of a built-in module. If the entire programming solution involves quite a large number of functions and classes, in that case putting all definitions in a single script is likely to be troublesome. A better way is to organize them in separate modules. Functions and classes of similar relevance are kept in one module. Such an approach makes the code easier to maintain. First of all, let us put some functions in a script called ‘mymodule.py

Example

‘docstring of mymodule’
def isprime(num):
x=2
for x in range(2,num):
if num%x==0:
return False
else:
return True
def iseven(num):
if num%2==0:
return True
else:
return False
def isleap(num):
if num%4==0:
return True
else:
return False

We can now import any function from this module in an interactive interpreter session, just as we imported the math module.

Example

>>> import mymodule
>>> mymodule . isprime ( 43 )
True
>>> mymodule . isprime ( 72 )
False
>>> mymodule . iseven ( 28 )
True
>>> mymodule . iseven ( 93 )
False
>>> mymodule . isleap (2019)
False
>>> mymodule . isleap ( 1996 )
True
>>>

It is also possible to import this module in another Python script. Here is an example script ‘moduledemo.py’

Example

#moduledemo.py
import mymodule
print (‘calling isprime function from mymodule1)
n=int(input(‘enter a number..’))
retval=mymodule.isprime(n)
if retval==True:
print ( ‘ { } is a prime number ‘ . format ( n ) )
else:
print ( ‘ { } is not a prime number ‘ . format ( n ) )

Output

E:\python37>python moduledemo.py
calling isprime function from mymodule
enter a number . . 39
39 is not a prime number
E:\python37>python moduledemo.py
calling isprime function from mymodule
enter a number . . 97
97 is a prime number

_name_ attribute
As is often said, everything in Python is an object. Likewise, a module – whether built-in or user-defined – is also an object of module class.

Example

>>> #module object – built-in module
. . .
>>> import math
>>> type(math)
<class ‘module’>
>>> #module object – user defined module
. . .
>>> import mymodule
>>> type(mymodule)
<class ‘module’>

Module object is characterized by various attributes. One of the important attributes of a module object is__name___and it has a peculiar behavior. Inside Python’s interactive shell, the__name__attribute returns ‘__main__’. It is the name of the top-level namespace in which the Python interpreter is running. However, the value of an imported module’s__name__ attribute is the name of the module itself (excluding the .py part from the script’s name)

Example

>>> #__name__ attribute of interactive shell
. . .
>>>__name__
‘__main__’
>>> #__name__ attrbute of imported module
. . .
>>> import math
>>> math.__name__
‘ math’
>>> import mymodule
>>> ‘mymodule.__name__
‘mymodule’

This is also the same in the case of a Python script. When a certain script is run from the command line, Python is running in scripting mode. Hence value of__name__in the script is ‘__main__’. So also,__name__attribute of a module imported in the script is its name itself. Run the following code from the command line.

Example

#moduledemo-1.py
import mymodule
print (‘_name_ of top level module: ‘ ,__name__)
print (‘__name__ of imported mymodule : ‘ , mymodule.__name__)

Output

E:\python37>python moduledemo-1.py
__name__of top level module:__main__
__name__ of imported mymodule: mymodule

A script having function definitions may also have certain executable code also in it. What happens if it is imported into another script? Let us see. Open mymodule.py and add statements that call is even( ) function after definitions.

Example

‘docstring of mymodule’
def isprime(num):
x=2
for x in range(2,num):
if num%x==0:
return False
else:
return True
def iseven(num):
if num%2==0:
return True
else:
return False
def isleap(num):
if num%4==0:
return True
else:
return False
##add following statements
n=int(input(‘enter a number..’))
retval=iseven(n)
if retval==True:
print ( ‘ { } is even ‘ . format ( n ) )
else:
print ( ‘ { } is odd ‘ . format ( n ) )

Now if the moduledemo.py is run (it imports my module in it). Look at the result, (figure 3.15)

E:\python37>python moduledemo.py
enter a number..23
23 is odd
calling is prime function from my module
enter a number23
23 is a prime number

You find that the executable part in the imported module also runs. Obviously, we don’t want this to happen. We do want the executable code to run when the module script is invoked but not when it is imported. The peculiar behavior of__name__attribute comes in handy here. As we saw, its value happens to be ‘__main__’ when a module script is run, but__name__attribute takes up its name when imported. So we have to test this attribute and run the executable code only if it is ‘__main__’. Modify the my module code as follows:

Example

‘docstring of mymodule’
def isprime(num):
x=2
for x in range(2,num):
if num%x==0:
return False
else:
return True
def iseven(num):
if num%2==0:
return True
else:
return False
def isleap(num):
if num%4==0:
return True
else:
return False
#modify as follows:
if __name__==’__main__’ :
n=int ( input ( ‘ enter a number . . ‘ ) )
retval=iseven(n)
if retval==True:
print ( ‘ { } is even ‘ . format ( n ) )
else:
print ( ‘ { } is odd ‘ . format ( n ) )

You can run mymodule.py independently but wouldn’t affect the execution of moduledemo.py. (figure 3.16)

E:\python37>python moduledemo.py
calling is a prime function from my module
enter a number..11
11 is a prime number