Python Functions - Unit 2 PDF
Document Details
Uploaded by SafeCherryTree6638
OSTF MCAS
Tags
Summary
This document details Python functions, including syntax, components, docstrings, returns, and examples. It covers variable scope and lifetime within functions, with illustrative examples.
Full Transcript
Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Python Functions In Python, function is a group of related statements that perform a specific task. Functions help break our program into smaller and modular chucks. As our program grows larger and larger, functions ma...
Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Python Functions In Python, function is a group of related statements that perform a specific task. Functions help break our program into smaller and modular chucks. As our program grows larger and larger, functions make it more organized and manageable. Furthermore, it avoids repetition and makes code reusable. Syntax of Function def function_name(parameters): """docstring""" statement(s) Above shown is a function definition which consists of following components. 1. Keyword def marks the start of function header. 2. A function name to uniquely identify it. Function naming follows the samerules of writing identifiers in Python. 3. Parameters (arguments) through which we pass values to a function. They are optional. 4. A colon (:) to mark the end of function header. 5. Optional documentation string (docstring) to describe what the function does. 6. One or more valid python statements that make up the function body. Statements must have same indentation level (usually 4 spaces). 7. An optional return statement to return a value from the function. Example of a function def greet(name): """This function greets to the person passed in as parameter""" print("Hello, " + name + ". Good morning!") Function Call Once we have defined a function, we can call it from another function, program or even the Python prompt. To call a function we simply type the function name with appropriate parameters. >>> greet('Paul') Hello, Paul. Good morning! Page 1 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Docstring The first string after the function header is called the docstring and is short for documentation string. It is used to explain in brief, what a function does. Although optional, documentation is a good programming practice. Unless you can remember what you had for dinner last week, always document your code. In the above example, we have a docstring immediately below the function header. We generally use triple quotes so that docstring can extend up to multiple lines. This string is available to us as __doc__ attribute of the function. For example: >>> print(greet.__doc__) This function greets to the person passed into the name parameter The return statement The return statement is used to exit a function and go back to the place from where it was called. Syntax of return return [expression_list] This statement can contain expression which gets evaluated and the value is returned. If there is no expression in the statement or the return statement itself is not present inside a function, then the function will return the None object. For example: >>> print(greet("May")) Hello, May. Good morning! None Here, None is the returned value. Example of return def absolute_value(num): """This function returns the absolute value of the entered number""" if num >= 0: return num else: return -num print(absolute_value(2)) print(absolute_value(-4)) Page 2 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Output 2 4 Scope and Lifetime of variables Scope of a variable is the portion of a program where the variable is recognized. Parameters and variables defined inside a function is not visible from outside. Hence, they have a local scope. Lifetime of a variable is the period throughout which the variable exits in the memory. The lifetime of variables inside a function is as long as the function executes. They are destroyed once we return from the function. Hence, a function does not remember the value of a variable from its previous calls. Here is an example to illustrate the scope of a variable inside a function. def my_func(): x = 10 print("Value inside function:",x) x = 20 my_func() print("Value outside function:",x) Output Value inside function: 10 Value outside function: 20 Here, we can see that the value of x is 20 initially. Even though the functionmy_func() changed the value of x to 10, it did not effect the value outside the function. This is because the variable x inside the function is different (local to the function) from the one outside. Although they have same names, they are two different variables with different scope. On the other hand, variables outside of the function are visible from inside. They have a global scope. We can read these values from inside the function but cannot change (write) them. In order to modify the value of variables outside the function, they must be declared as global variables using the keyword global. Types of Functions Basically, we can divide functions into the following two types: 1. Built-in functions - Functions that are built into Python. Page 3 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 2. User-defined functions - Functions defined by the users themselves. Python Programming Built-in Functions Functions that come built into the Python language itself are called built-in functions and are readily available to us. Functions like print(), input(), eval() etc. that we have been using, are some examples of the built-in function. There are 68 built-in functions (may change with version) in Python. They are listed below alphabetically along with a brief description. Python built-in functions Built-in Description Function abs() Return the absolute value of a number. Return True if all elements of the iterable all() are true (or if the iterable is empty). Return True if any element of the iterable is any() true. If the iterable is empty, return False. Return a string containing a printable ascii() representation of an object, but escape the non-ASCII characters. bin() Convert an integer number to a binary string. bool() Convert a value to a Boolean. bytearray() Return a new array of bytes. bytes() Return a new "bytes" object. Return True if the object argument appears callable() callable,False if not. chr() Return the string representing a character. classmethod() Return a class method for the function. Page 4 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 compile() Compile the source into a code or AST object. Create a complex number or convert a string complex() or number to a complex number. delattr() Deletes the named attribute of an object. dict() Create a new dictionary. Return the list of names in the current local dir() scope. Return a pair of numbers consisting of divmod() quotient and remainder when using integer division. enumerate() Return an enumerate object. The argument is parsed and evaluated as a eval() Python expression. exec() Dynamic execution of Python code. Construct an iterator from elements of filter() iterable for which function returns true. Convert a string or a number to floating float() point. Convert a value to a "formatted" format() representation. frozenset() Return a new frozenset object. Return the value of the named attribute of an getattr() object. Return a dictionary representing the current globals() global symbol table. Return True if the name is one of the hasattr() object's attributes. Page 5 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 hash() Return the hash value of the object. help() Invoke the built-in help system. Convert an integer number to a hexadecimal hex() string. id() Return the "identity" of an object. Reads a line from input, converts it to a input() string (stripping a trailing newline), and returns that. int() Convert a number or string to an integer. Return True if the object argument is an isinstance() instance. issubclass() Return True if class is a subclass. iter() Return an iterator object. Return the length (the number of items) of an len() object. list() Return a list. Update and return a dictionary representing locals() the current local symbol table. Return an iterator that applies function to map() every item of iterable, yielding the results. max() Return the largest item in an iterable. Return a "memory view" object created from memoryview() the given argument. min() Return the smallest item in an iterable. next() Retrieve the next item from the iterator. object() Return a new featureless object. Page 6 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 oct() Convert an integer number to an octal string. Open file and return a corresponding file open() object. ord() Return an integer representing the Unicode. pow() Return power raised to a number. print() Print objects to the stream. property() Return a property attribute. range() Return an iterable sequence. Return a string containing a printable repr() representation of an object. reversed() Return a reverse iterator. round() Return the rounded floating point value. set() Return a new set object. setattr() Assigns the value to the attribute. slice() Return a slice object. sorted() Return a new sorted list. staticmethod() Return a static method for function. str() Return a str version of object. Sums the items of an iterable from left to sum() right and returns the total. Return a proxy object that delegates method super() calls to a parent or sibling class. tuple() Return a tuple type() Return the type of an object. Page 7 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Return the __dict__ attribute for a module, vars() class, instance, or any other object. Make an iterator that aggregates elements zip() from each of the iterables. This function is invoked by __import__() the import statement. Python Programming User-defined Functions Functions that we define ourselves to do certain specific task are referred as user-defined functions. The way in which we define and call functions in Pythonare already discussed. Functions that readily come with Python are called built-in functions. If we use functions written by others in the form of library, it can be termed as library functions. All the other functions that we write on our own fall under user-defined functions. So, our user-defined function could be a library function to someone else. Advantages of user-defined functions 1. User-defined functions help to decompose a large program into small segments which makes program easy to understand, maintain and debug. 2. If repeated code occurs in a program. Function can be used to include those codes and execute when needed by calling that function. 3. Programmars working on large project can divide the workload by making different functions. Example of a user-defined function # Program to illustrate # the use of user-defined functions def my_addition(x,y): """This function adds two numbers and return the result""" sum = x + y return sum num1 = float(input("Enter a number: ")) num2 = float(input("Enter another number: ")) print("The sum is", my_addition(num1,num2)) Output Page 8 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Enter a number: 2.4 Enter another number: 6.5 The sum is 8.9 Explanation Here, we have defined the function my_addition() which adds two numbers and returns the result. This is our user-defined function. We could have multiplied the two numbers inside our function (it's all up to us). But this operation would not be consistent with the name of the function. It would create ambiguity. It is always a good idea to name functions according to the task they perform. In the above example, input(), print() and float() are built-in functions of the Python programming language. Python Function Arguments In user-defined function topic, we learned about defining a function and calling it. Otherwise, the function call will result into an error. Here is an example. def greet(name,msg): """This function greets to the person with the provided message""" print("Hello",name + ', ' + msg) greet("Monica","Good morning!") Output Hello Monica, Good morning! Here, the function greet() has two parameters. Since, we have called this function with two arguments, it runs smoothly and we do not get any error. But if we call it with different number of arguments, the interpreter will complain. Below is a call to this function with one and no arguments along with their respective error messages. >>> greet("Monica") # only one argument TypeError: greet() missing 1 required positional argument: 'msg' >>> greet() # no arguments Page 9 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 TypeError: greet() missing 2 required positional arguments: 'name' and 'msg' Variable Function Arguments Up until now functions had fixed number of arguments. In Python there are other ways to define a function which can take variable number of arguments. Three different forms of this type are described below. Default Arguments Function arguments can have default values in Python. We can provide a default value to an argument by using the assignment operator (=). Here is an example. def greet(name, msg = "Good morning!"): """This function greets to the person with the provided message. If message is not provided, it defaults to "Good morning!" """ print("Hello",name + ', ' + msg) In this function, the parameter name does not have a default value and is required (mandatory) during a call. On the other hand, the parameter msg has a default value of "Good morning!". So, it is optional during a call. If a value is provided, it will overwrite the default value. Here are some valid calls to this function. >>> greet("Kate") Hello Kate, Good morning! >>> greet("Bruce","How do you do?") Hello Bruce, How do you do? Any number of arguments in a function can have a default value. But once we have a default argument, all the arguments to its right must also have default values. This means to say, non - default arguments cannot follow default arguments. For example, if we had defined the function header above as: def greet(msg = "Good morning!", name): We would get an error as: Page 10 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 SyntaxError: non-default argument follows default argument Keyword Arguments When we call a function with some values, these values get assigned to the arguments according to their position. For example, in the above function greet(), when we called it as greet("Bruce","How do you do?"), the value "Bruce" gets assigned to the argument name and similarly "How do you do?" to msg. Python allows functions to be called using keyword arguments. When we call functions in this way, the order (position) of the arguments can be changed. Following calls to the above function are all valid and produce the same result. greet(name = "Bruce",msg = "How do you do?") # 2 keyword arguments greet(msg = "How do you do?",name = "Bruce") # 2 keyword arguments (out of order) greet("Bruce",msg = "How do you do?") # 1 positional, 1 keyword argument As we can see, we can mix positional arguments with keyword arguments during a function call. But we must keep in mind that keyword arguments must follow positional arguments. Having a positional argument after keyword arguments will result into errors. For example the function call as follows: greet(name="Bruce","How do you do?") Will result into error as: SyntaxError: non-keyword arg after keyword arg Arbitrary Arguments Sometimes, we do not know in advance the number of arguments that will be passed into a function. Python allows us to handle this kind of situation through function calls with arbitrary number of arguments. In the function definition we use an asterisk (*) before the parameter name to denote this kind of argument. Here is an example. def greet(*names): Page 11 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 """This function greets all the person in the names tuple.""" # names is a tuple with arguments for name in names: print("Hello",name) greet("Monica","Luke","Steve","John") Output Hello Monica Hello Luke Hello Steve Hello John Here, we have called the function with multiple arguments. These arguments get wrapped up into a tuple before being passed into the function. Inside the function, we use a for loop to retrieve all the arguments back. Global and local Variables in Functions In the following example, we want to demonstrate, how global values can be used inside the body of a function: def f(): print(s) s = "I love Paris in the summer!" f() The variable s is defined as the string "I love Paris in the summer!", before calling the function f(). The body of f() consists solely of the "print(s)" statement. As there is no local variable s, i.e. no assignment to s, the value from the global variable s will be used. So the output will be the string "I love Paris in the summer!". The question is, what will happen, if we change the value of s inside of the function f()? Will it affect the global variable as well? We test this in the following piece of code: def f(): s = "I love London!" print(s) s = "I love Paris!" f() print(s) The output looks like this: Page 12 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 I love London! I love Paris! What if we combine the first example with the second one, i.e. first access s with a print() function, hoping to get the global value, and then assigning a new value to it? Assigning a value to it, would mean creating a local variable s. So, we would have s both as a global and a local variable in the same scope, i.e. the body of the function. Python doesn't allow this ambiguity. So, it will throw an error, as we can see in the following example: >>> def f():... print(s)... s = "I love London!"... print(s)... >>> s = "I love Paris!" >>> f() Traceback (most recent call last): File "", line 1, in File "", line 2, in f UnboundLocalError: local variable 's' referenced before assignment >>> A variable can't be both local and global inside of a function. So Python decides that we want a local variable due to the assignment to s inside of f(), so the first print statement before the definition of s throws the error message above. Any variable which is changed or created inside of a function is local, if it hasn't been declared as a global variable. To tell Python, that we want to use the global variable, we have to explicitly state this by using the keyword "global", as can be seen in the following example: def f(): global s print(s) s = "Only in spring, but London is great as well!" print(s) s = "I am looking for a course in Paris!" f() print(s) We have solved our problem. There is no ambiguity left. The output of this small script looks like this: I am looking for a course in Paris! Only in spring, but London is great as well! Only in spring, but London is great as well! Page 13 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Local variables of functions can't be accessed from outside, when the function call has finished: def f(): s = "I am globally not known" print(s) f() print(s) Starting this script gives us the following output with an error message: I am globally not known Traceback (most recent call last): File "ex.py", line 6, in print(s) NameError: name 's' is not defined The following example shows a wild combination of local and global variables and function parameters: def foo(x, y): global a a = 42 x,y = y,x b = 33 b = 17 c = 100 print(a,b,x,y) a,b,x,y = 1,15,3,4 foo(17,4) print(a,b,x,y) The output looks like this: 42 17 4 17 42 15 3 4 Python Recursion Recursion is the process of defining something in terms of itself. A physical world example would be to place two parallel mirrors facing each other. Any object in between them would be reflected recursively. Python Recursive Function We know that in Python, a function can call other functions. It is even possible for the function to call itself. These type of construct are termed as recursive functions. Following is an example of recursive function to find the factorial of an integer. Factorial of a number is the product of Page 14 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 all the integers from 1 to that number. For example, the factorial of 6 (denoted as 6!) is 1*2*3*4*5*6 = 720. Example of recursive function # An example of a recursive function to # find the factorial of a number def recur_fact(x): """This is a recursive function to find the factorial of an integer""" if x == 1: return 1 else: return (x * recur_fact(x-1)) num = int(input("Enter a number: ")) if num >= 1: print("The factorial of", num, "is", recur_fact(num)) Output Enter a number: 4 The factorial of 4 is 24 Explanation In the above example, recur_fact() is a recursive functions as it calls itself. When we call this function with a positive integer, it will recursively call itself by decreasing the number. Each function call multiples the number with the factorial of number-1 until the number is equal to one. This recursive call can be explained in the following steps. recur_fact(4) # 1st call with 4 4 * recur_fact(3) # 2nd call with 3 4 * 3 * recur_fact(2) # 3rd call with 2 4 * 3 * 2 * recur_fact(1) # 4th call with 1 4 * 3 * 2 * 1 # retrun from 4th call as number=1 4 * 3 * 2 # return from 3rd call 4 * 6 # return from 2nd call 24 # return from 1st call Page 15 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Our recursion ends when the number reduces to 1. This is called the base condition. Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely. We must avoid infinite recursion. Advantages of recursion 1. Recursive functions make the code look clean and elegant. 2. A complex task can be broken down into simpler sub -problems using recursion. 3. Sequence generation is easier with recursion than using some nested iteration. Disadvantages of recursion 1. Sometimes the logic behind recursion is hard to follow through. 2. Recursive calls are expensive (inefficient) as they take up a lot of memory and time. 3. Recursive functions are hard to debug. Python Modules Modules refer to a file containing Python statements and definitions. A file containing Python code, for e.g.: example.py, is called a module and its module name would be example. We use modules to break down large programs into small manageable and organized files. Furthermore, modules provide reusability of code. We can define our most used functions in a module and import it, instead of copying their definitions into different programs. Let us create a module. Type the following and save it as example.py. # Python Module example def add(a, b): """This program adds two numbers and return the result""" result = a + b return result Here, we have defined a function add() inside a module named example. The function takes in two numbers and returns their sum. Importing modules We can import the definitions inside a module to another module or the interactive interpreter in Python. We use Page 16 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 the import keyword to do this. To import our previously defined module example we type the following in the Python prompt. >>> import example This does not enter the names of the functions def ined in example directly in the current symbol table. It only enters the module name example there. Using the module name we can access the function using dot (.) operation. For example: >>> example.add(4,5.5) 9.5 Python has a ton of standard modules available. You can check out the full list of Python standard modules and what they are for. These files are in the Lib directory inside the location where you installed Python. Standard modules can be imported the same way as we import our user-defined modules. There are various ways to import modules. They are listed as follows. The import statement We can import a module using import statement and access the definitions inside it using the dot operator as described above. Here is an example. # import statement example # to import standard module math import math print("The value of pi is", math.pi) Output The value of pi is 3.141592653589793 Import with renaming We can import a module by renaming it as follows. # import module by renaming it import math as m print("The value of pi is", m.pi) The output of this is same as above. We have renamed the math module as m. This can save us typing time in some cases. Note that the name math is not recognized in our scope. Hence, math.pi is invalid, m.pi is the correct implementation. Page 17 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 The from...import statement We can import specific names form a module without importing th e module as a whole. Here is an example. # import only pi from math module from math import pi print("The value of pi is", pi) The output of this is same as above. We imported only the attribute pi form the module. In such case we don't use the dot operator. We could have imported multiple attributes as follows. >>> from math import pi, e >>> pi 3.141592653589793 >>> e 2.718281828459045 Import all names We can import all names(definitions) form a module using the following construct. # import all names form # the standard module math from math import * print("The value of pi is", pi) The output of this is same as above. We imported all the definitions from the math module. This makes all names except those beginnig with an underscore, visible in our scope. Importing everything with the asterisk (*) symbol is not a good programming practice. This can lead to duplicate definitions for an identifier. It also hampers the readability of our code. Python Module Search Path While importing a module, Python looks at several places. Interpreter first looks for a built-in module then (if not found) into a list of directories defined insys.path. The search is in this order. The current directory. PYTHONPATH (an environment variable with a list of directory). The installation-dependent default directory. >>> import sys >>> sys.path Page 18 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 ['', 'C:\\Python33\\Lib\\idlelib', 'C:\\Windows\\system32\\python33.zip', 'C:\\Python33\\DLLs', 'C:\\Python33\\lib', 'C:\\Python33', 'C:\\Python33\\lib\\site-packages'] We can add modify this list to add our own path. Reloading a module The Python interpreter imports a module only once during a session. This makes things more efficient. Here is an example to show how this works. Suppose we have the following code in a module named my_module. # This module shows the effect of # multiple imports and reload print("This code got executed") Now we see the effect of multiple imports. >>> import my_module This code got executed >>> import my_module >>> import my_module We can see that our code got executed only once. This goes to say that our module was imported only once. Now if our module changed during the course of the program, we would have to reload it. One way to do this is to restart the interpreter. But this does not help much. Python provides a neat way of doing this. We can use the reload()function inside the imp module to reload a module. This is how its done. >>> import imp >>> import my_module This code got executed >>> import my_module >>> imp.reload(my_module) This code got executed Page 19 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 The dir() built-in function We can use the dir() function to find out names that are defined inside a module. For example, we have defined a function add() in the module example that we had in the beginning. >>> dir(example) ['__builtins__', '__cached__', '__doc__', '__file__', '__initializing__', '__loader__', '__name__', '__package__', 'add'] Here, we can see a sorted list of names (along with add). All other names that begin with an underscore are default Python attributes associated with the module (we did not define them ourself). For example, the __name__ attribute contains the name of the module. >>> import example >>> example.__name__ 'example' All the names defined in our current namespace can be found out using the dir()function without any arguments. >>> a = 1 >>> b = "hello" >>> import math >>> dir() ['__builtins__', '__doc__', '__name__', 'a', 'b', 'math', 'pyscripter'] Python Package We don't usually store all of our files in our computer in the same location. We use a well-organized hierarchy of directories for easier access. Similar files are kept in the same directory, for example, we may keep all the songs in the "music" directo ry. Analogous to this, Python has packages for directories and modules for files. As our application program grows larger in size with a lot of modules, we place similar modules in one package and different modules in different packages. This makes a project (program) Page 20 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 easy to manage and conceptually clear. Similar, as a directory can contain sub-directories and files, a Python package can have sub-packages and modules. A directory must contain a file named __init__.py in order for Python to consider it as a package. This file can be left empty but we generally place the initialization code for that package in this file. Here is an example. Suppose we are developing a game, one possible organization of packages and modules could be as shown in the figure below. Importing module from a package We can import modules from packages using the dot (.) operator. For example, if want to import the start module in the above example, it is done as follows. import Game.Level.start Now if this module contains a function named select_difficulty(), we must use the full name to reference it. Game.Level.start.select_difficulty(2) If this construct seems lengthy, we can import the module without the package prefix as follows. from Game.Level import start We can now call the function simply as follows. Page 21 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 start.select_difficulty(2) Yet another way of importing just the required function (or class or variable) form a module within a package would be as follows. from Game.Level.start import select_difficulty Now we can directly call this function. select_difficulty(2) Although easier, this method is not recommended. Using the full namespace avoids confusion and prevents two same identifier names from colliding. While importing packages, Python looks in the list of directories defined in sys.path, similar as for module search path. Python File I/O File is a named location on disk to store related information. It is used to permanently store data in a non-volatile memory (e.g. hard disk). Since, random access memory (RAM) is volatile which loses its data when computer is turned off, we use files for future use of the data. When we want to read from or write to a file we need to open it first. When we are done, it needs to be closed, so that resources that are tied with the file are freed. Hence, in Python, a file operation takes place in the following order. 1. Open a file 2. Read or write (perform operation) 3. Close the file Opening a File Python has a built-in function open() to open a file. This function returns a file object, also called a handle, as it is used to read or modify the file accordingly. >>> f = open("test.txt") # open file in current directory >>> f = open("C:/Python33/README.txt") # specifying full path We can specify the mode while opening a file. In mode, we specify whether we want to read 'r', write 'w' or append 'a' to the file. We also specify if we want to open the file in text mode or binary mode. The default is reading in text mode. In this mode, we get strings when reading from the file. On the other hand, binary mode returns bytes and this is the mode to be used when dealing with non-text files like image or exe files. Page 22 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Python File Modes Mode Description 'r' Open a file for reading. (default) Open a file for writing. Creates a new file if it 'w' does not exist or truncates the file if it exists. Open a file for exclusive creation. If the file 'x' already exists, the operation fails. Open for appending at the end of the file without 'a' truncating it. Creates a new file if it does not exist. 't' Open in text mode. (default) 'b' Open in binary mode. '+' Open a file for updating (reading and writing) f = open("test.txt") # equivalent to 'r' or 'rt' f = open("test.txt",'w') # write in text mode f = open("img.bmp",'r+b') # read and write in binary mode Since the version 3.x, Python has made a clear distinction between str (text) andbytes (8-bits). Unlike other languages, the character 'a' does not imply the number 97 until it is encoded using ASCII (or other equivalent encodings). Hence, when working with files in text mode, it is recommended to specify the encoding type. Files are stored in bytes in the disk, we need to decode them into str when we read into Python. Similarly, encoding is performed while writing texts to the file. The default encoding is platform dependent. In windows, it is 'cp1252' but 'utf-8' in Linux. Hence, we must not rely on the default encoding otherwise, our code will behave differently in different platforms. Thus, this is the preferred way to open a file for reading in text mode. f = open("test.txt",mode = 'r',encoding = 'utf-8') Page 23 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 Closing a File When we are done with operations to the file, we need to properly close it. Python has a garbage collector to clean up unreferenced objects. But we must not rely on it to close the file. Closing a file will free up the resources that were tied with the file and is done using the close() method. f = open("test.txt",encoding = 'utf-8') # perform file operations f.close() This method is not entirely safe. If an exception occurs when we are performing some operation with the file, the code exits without closing the file. A safer way is to use a try...finally block. try: f = open("test.txt",encoding = 'utf-8') # perform file operations finally: f.close() This way, we are guaranteed that the file is properly closed even if an exception is raised, causing program flow to stop. The best way to do this is using the with statement. This ensures that the file is closed when the block inside with is exited. We don't need to explicitly call theclose() method. It is done internally. with open("test.txt",encoding = 'utf-8') as f: # perform file operations Writing to a File In order to write into a file we need to open it in write 'w', append 'a' or exclusive creation 'x' mode. We need to be careful with the 'w' mode as it will overwrite into the file if it already exists. All previous data are erased. Writing a string or sequence of bytes (for binary files) is done using write()method. This method returns the number of characters written to the file. with open("test.txt",'w',encoding = 'utf-8') as f: f.write("my first file\n") f.write("This file\n\n") f.write("contains three lines\n") Page 24 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 This program will create a new file named 'test.txt' if it does not exist. If it does exist, it is overwritten. We must include the newline characters ourselves to distinguish different lines. Reading From a File To read the content of a file, we must open the file in reading mode. There are various methods available for this purpose. We can use the read(size) method to read in size number of data. If size parameter is not specified, it reads and returns up to the end of the file. >>> f = open("test.txt",'r',encoding = 'utf-8') >>> f.read(4) # read the first 4 data 'This' >>> f.read(4) # read the next 4 data ' is ' >>> f.read() # read in the rest till end of file 'my first file\nThis file\ncontains three lines\n' >>> f.read() # further reading returns empty sting '' We can see, that read() method returns newline as '\n'. Once the end of file is reached, we get empty string on further reading. We can change our current file cursor (position) using the seek() method. Similarly, the tell() method returns our current position (in number of bytes). >>> f.tell() # get the current file position 56 >>> f.seek(0) # bring file cursor to initial position 0 >>> print(f.read()) # read the entire file This is my first file This file contains three lines We can read a file line-by-line using a for loop. This is both efficient and fast. Page 25 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 >>> for line in f:... print(line, end = '')... This is my first file This file contains three lines The lines in file itself has a newline character '\n'. Moreover,the print() function also appends a newline by default. Hence, we specify the end parameter to avoid two newlines when printing. Alternately, we can use readline() method to read individual lines of a file. This method reads a file till the newline, including the newline character. >>> f.readline() 'This is my first file\n' >>> f.readline() 'This file\n' >>> f.readline() 'contains three lines\n' >>> f.readline() '' Lastly, the readlines() method returns a list of remaining lines of the entire file. All these reading method return empty values when end of file (EOF) is reached. >>> f.readlines() ['This is my first file\n', 'This file\n', 'contains three lines\n'] Python File Methods There are various methods available with the file object. Some of them have been used in above examples. Here is the complete list of methods in text mode with a brief description. Method Description Close an open file. It has no close() effect if the file is already Page 26 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 closed. Separate the underlying binary detach() buffer from theTextIOBase and return it. Return an integer number (file fileno() descriptor) of the file. Flush the write buffer of the flush() file stream. Return True if the file stream isatty() is interactive. Read atmost n characters form read(n) the file. Reads till end of file if it is negative or None. Returns True if the file stream readable() can be read from. Read and return one line from readline(n=-1) the file. Reads in at most n bytes if specified. Read and return a list of lines from the file. Reads in at readlines(n=-1) most n bytes/characters if specified. Change the file position seek(offset,from=SEEK_SET) to offset bytes, in reference to from (start, current, end). Returns True if the file stream seekable() supports random access. Returns the current file tell() location. Resize the file stream truncate(size=None) to size bytes. If size is not Page 27 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 specified, resize to current location. Returns True if the file stream writable() can be written to. Write string s to the file and write(s) return the number of characters written. Write a list of lines to the writelines(lines) file. Python Directory and Files Management If there are large number of files in Python, we can place related files in different directories to make things more manageable. A directory or folder is a collection of files and sub directories. Python has the os module, which provides us with many useful methods to work with directories (and files as well). Get Current Directory We can get the present working directory using the getcwd() method. This method returns the current working directory in the form of a string. We can also use the getcwdb() method to get it as bytes object. >>> import os >>> os.getcwd() 'C:\\Program Files\\PyScripter' >>> os.getcwdb() b'C:\\Program Files\\PyScripter' The extra backslash implies escape sequence. The print() function will render this properly. >>> print(os.getcwd()) C:\Program Files\PyScripter Changing Directory We can change the current working directory using the chdir() method. The new path that we want to change to must be supplied as a string to this method. We can use both forward Page 28 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 slash (/) or the backward slash (\) to separate path elements. It is safer to use escape sequence when using the backward slash. >>> os.chdir('C:\\Python33') >>> print(os.getcwd()) C:\Python33 List Directories and Files All files and sub directories inside a directory can be known using the listdir()method. This method takes in a path and returns a list of sub directories and files in that path. If no path is specified, it returns from the current working directory. >>> print(os.getcwd()) C:\Python33 >>> os.listdir() ['DLLs', 'Doc', 'include', 'Lib', 'libs', 'LICENSE.txt', 'NEWS.txt', 'python.exe', 'pythonw.exe', 'README.txt', 'Scripts', 'tcl', 'Tools'] >>> os.listdir('G:\\') ['$RECYCLE.BIN', 'Movies', 'Music', 'Photos', 'Series', 'System Volume Information'] Making a New Directory We can make a new directory using the mkdir() method. This method takes in the path of the new directory. If the full path is not specified, the new directory is created in the current working directory. Page 29 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 >>> os.mkdir('test') >>> os.listdir() ['test'] Renaming a Directory or a File The rename() method can rename a directory or a file. The first argument is the old name and the new name must be supplies as the second argument. >>> os.listdir() ['test'] >>> os.rename('test','new_one') >>> os.listdir() ['new_one'] Removing Directory or File A file can be removed (deleted) using the remove() method. Similarly, the rmdir()method removes an empty directory. >>> os.listdir() ['new_one', 'old.txt'] >>> os.remove('old.txt') >>> os.listdir() ['new_one'] >>> os.rmdir('new_one') >>> os.listdir() [] However, note that rmdir() method can only remove empty directories. In order to remove a non-empty directory we can use the rmtree() method inside the shutilmodule. >>> os.listdir() ['test'] >>> os.rmdir('test') Traceback (most recent call last):... OSError: [WinError 145] The directory is not empty: 'test' Page 30 Unit-2 : Functions, Scoping and Abstraction OSTF MCA5 >>> import shutil >>> shutil.rmtree('test') >>> os.listdir() [] Page 31