List and tuple both are sequence in python. The key difference is lists are mutable type and tuples are immutable type. Tuples do not support assignment like Lists. The literal syntax of list is shown by square brackets ' [ ] ' and tuple is shown by parenthesis ' ( ) '.
List
l1 = [1,2,3,4,5]
l2 = ['s', 2, ['sam', (1,2)]]
Tuple
t1 = (1, 'tiger', 4.0)
t2 = ('a', 'e', 'i', 'o', 'u')
Everything in python is an object. The objects which cannot be changed once created is called immutable type and the objects which can be changed after creation are called mutable type.
Mutable data types- list, dict, set, byte array
Immutable data types- int, float, complex, string, tuple, frozen set , bytes
Append and extend both are the methods of list data type. Append add argument as a single element at the end of a list instance. Extend iterates over its argument and add each element to the end of the list.
Append
List1 = ['fan', 'box']
List1.append('ball')
print(list1)
list2 = [1, 2, 3, 4]
list1.append(list2)
print(list1)
Extend
List1 = ['fan', 'box']
list2 = [1, 2, 3, 4]
list1.extend(list2)
print(list1)
list1.extend('ball')
print(list1)
When argument of a function take the reference of an object is called “call by reference” and changing it inside a function will change the object outside.
def change_list(list1):
list1 += [12]
n = [4, 5, 6]
print(id(n))
change_list(n)
print(n)
print(id(n))
When argument of a function take only the value of an object is called “call by value” and changing it inside a function will not affect the object outside.
def change_number(n):
print(id(n))
n += 10
a = 9
print(id(a))
change_number(a)
print(a)
print(id(a))
Variables assigned outside function definition is called global variable. It can be accessed from outside and inside of the functions. If a variable is assigned a value anywhere within the function’s body, it’s assumed to be a local unless explicitly declared as global. Local variables are accessible inside the function in which it is defined. Trying to access local variable outside function will raise error.
x = 5
y = 6 # global variables
def double():
y = 7 # local variable
print(x*2)
print(y*2)
double_x() # 10
# 14
print(x*2) # 10
print(y*2) # 12
x = 5
y = 6 # global variables
def double():
global y = 7 # changing value of y globally
print(x*2)
print(y*2)
double_x() # 10
# 14
print(x*2) # 10
print(y*2) # 14
Variables defined inside a function is local to that function only. In case of nested functions, if we define a variable as nonlocal, then it’s scope expands to its outer function. Changing a variable as nonlocal in inner function will change the value in outer function.
x = 1
def outer():
x = 5
def inner():
x = 6
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
# inner: 6
# outer: 5
# global: 1
Using nonlocal
x = 1
def outer():
x = 5
def inner():
nonlocal x
x = 6
print("inner:", x)
inner()
print("outer:", x)
outer()
print("global:", x)
# inner: 6
# outer: 6
# global: 1
Both the methods returns the index number of the substring when it find the first occurrence of the substring. But Find method returns ‘-1’ when it does not find the substring but index method gives a value error in this scenario.
s1 = 'I love swimming'
print(s1.find('n')) # -1
print(s1.index('n')) # ValueError: substring not found
my_list = [1, 2, 3, 4, 5, 6]
my_list.remove(2)
print(my_list) # [1, 3, 4, 5, 6]
my_list.pop(2) # will remove the element with index no ‘2’
print(list) # [1, 3, 5, 6]
del my_list(1) # will delete the element with index no ‘1’
print(list) # [1, 5, 6]
my_list[1:2] = [] # will remove the element with index no ‘1’
print(list) # [1, 6]
my_list = [1, 2, 3, 4, 5, 6]
another_list = [7, 8 , 9]
# include the second list in the first list after the element ‘3’
Method-1
my_list = mylist[0:3] + another_list + mylist[3:]
print(new_list) # [1, 2, 3, 7, 8, 9, 4, 5, 6]
Method-2
my_list = [1, 2, 3, 4, 5, 6]
my_list[3:4]= another_list
print(my_list) # [1, 2, 3, 7, 8, 9, 4, 5, 6]
Parameters are defined by the names that appear in a function definition, whereas arguments are the values actually passed to a function when calling it. Parameters define what types of arguments a function can accept. For example, given the function definition:
def func(foo, bar=None, **kwargs):
pass
#foo, bar and kwargs are parameters of func. However, when calling func, for example:
func(42, bar=314, extra=somevar)
#the values 42, 314, and somevar are arguments.
Self is merely a conventional name for the first argument of a method inside a class. A method defined as meth(self, a, b, c)
should be called as x.meth(a, b, c)
for some instance x of the class in which the definition occurs; the called method will think it is called asmeth(x, a, b, c)
.
The future module
With in-built future module, we can import python 3.x functions to python 2.x.
Division operator
Integer division ‘/’ gives an integer in python 2 but it gives a float in python 3. Python 3 has a floor division ‘//’ to get integer value.
Print function
Python 2’s print statement has been replaced by the print() function in python 3
Unicode
Python 2 has ASCII str() types, separate unicode(), but no byte type.
In Python 3, we have Unicode (utf-8) strings, and 2 byte classes: byte and bytearrays
Xrange
In Python 3, the range() is like the xrange() function in python 2. xrange() function does not exist in python 3.
The syntax is the somename and **somename. The names args and *kwargs are only by convention but there's no strict requirement to use them.
You would use args when you're not sure how many arguments might be passed to your function. It allows you pass an arbitrary number of arguments to your function.
def addnum(*args):
a = 0
for n in args:
a += num
print(a)
addnum(4, 9) # 13
addnum(10, 7) # 17
addnum(2, 7, 4) # 13
addnum(3, 8, 10, 2) # 23
With **kwargs you can give arbitrary keyword arguments to a function and you can access them as a dictionary.
def func1(**kwargs):
for key, value in kwargs.items():
print(key, value)
func1(name='jack', animal= ‘monkey’ , eyes=2)
# name jack
# animal monkey
# eyes 2
Using args and *kwargs in function call
def func1(x,y,z):
print(x,y,z)
l1 = [5,6,8]
fun1(*l1) # 5 6 8
d1 = {1: ‘first’, ‘s’: ‘second’, ‘m’: ‘third’}
fun1(*t1) # first second third
It is a built-in function of python. It takes an iterable object as an argument and returns an enumerate object.
Syntax: enumerate(iterable, start=0)
Example:
We can get a list of tuples from Enumerate object by built-in function list().
fruits = ['apple', 'orange', 'banana', 'grapes']
print(list(enumerate(fruits)))
[(0, 'apple'), (1, 'orange'), (2, 'banana'), (3, 'grapes')]
By default indexing starts with “0”, but we can specify the starting index.
fruits = ['apple', 'orange', 'banana', 'grapes']
print( list(enumerate(fruits, start = 10)))
[(10, 'apple'), (11, 'orange'), (12, 'banana'), (13, 'grapes')]
Using of Enumerate with for loop
fruits = ['apple', 'orange', 'banana', 'grapes']
for index, value in enumerate(fruits, 1):
print(index, value)
# 1 apple
# 2 orange
# 3 banana
# 4 grapes
The keyword break
, breaks out of the innermost enclosing loop.
for n in range(2, 7):
for x in range(2, n):
if n % x == 0:
print(n, 'equals', x, '*', n//x)
break
else:
# loop fell through without finding a factor
print(n, 'is a prime number')
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
The keyword continue, moves the control to the next iteration of the loop.
for num in range(2, 8):
if num % 2 == 0:
print("Found an even number", num)
continue
print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Small anonymous functions can be created with the lambda keyword. Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression. Semantically, they are just syntactic sugar for a normal function definition. Like nested function definitions, lambda functions can reference variables from the containing scope
These functions return the sum of its two arguments
func1 = lambda a, b: a+b
def func2(a,b):
return a+b
func1(2,3) # 5
func2(3,4) # 7
Keywords are the reserved words in Python. In side python these words have its own meaning and usability. You can always get the list of keywords in your current version by typing the following.
import keyword
print(keyword.kwlist)
['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def',
'del', 'elif','else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is',
'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield']
Remember these key words are specific to a particular version of python. Some keywords may be different in different versions.
The exceptions which can be generated by the interpreter or built-in functions are called as built-in exceptions.
Examples:
Identifies are the names given by user to represent objects in python like variable, class, function, module etc.
List comprehension is a concise way of generating a list from an iterator.
Square of numbers
Using for loop
list1 = []
for i in range(1,5):
list1.append(i*i)
print(list1)
Using list comprehension
Print([i*i for i in range(1,5) ])
Find odd numbers
Using for loop
list1 = []
for i in range(1,6):
if i % 2 !=0:
list1.append(i)
print(list1)
Using list comprehension
Print([i for i in range(1,5) if i % 2 !=0 ])
Like list comprehension dictionary can be formed from a iterator in one line.
dict1 = {x: x**2 for x in (2, 4, 6)}
print(dict1) #{2: 4, 4: 16, 6: 36}
A set is an unordered collection with no duplicate elements. Basic uses include membership testing and eliminating duplicate entries. Set objects also support mathematical operations like union, intersection, difference, and symmetric difference.
Curly braces or the set() function can be used to create sets.To create an empty set you have to use set(), not {}.
basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
print(basket) # {'orange', 'banana', 'pear', 'apple'}
a = set('abracadabra')
print(a) #{'a', 'r', 'b', 'c', 'd'}
The list methods make it very easy to use a list as a stack, where the last element added is the first element retrieved (“last-in, first-out”). To add an item to the top of the stack, use append(). To retrieve an item from the top of the stack, use pop() without an explicit index.
stack = [3, 4, 5]
stack.append(6)
stack.append(7)
print(stack) # [3, 4, 5, 6, 7]
stack.pop()
print(stack) # [3, 4, 5, 6]
stack.pop() # 6 removed
stack.pop() # 5 removed
print(stack) # [3, 4]
It is possible to use a list as a queue, where the first element added is the first element retrieved (“first-in, first-out”); However, lists are not efficient for this purpose. While appends and pops from the end of list are fast, doing inserts or pops from the beginning of a list is slow (because all of the other elements have to be shifted by one).
To implement a queue, use collections.deque which was designed to have fast appends and pops from both ends.
from collections import deque
queue = deque(["Eric", "John", "Michael"])
queue.append("Terry") # Terry arrives
queue.append("Graham") # Graham arrives
queue.popleft() # The first to arrive now leaves ('Eric')
queue.popleft() # The second to arrive now leaves ('John')
print(queue) # deque(['Michael', 'Terry', 'Graham'])
Iterator is an object in python which implements two methods, iter and next. We can iterate over it by using for loop.
class Cubes(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def next(self):
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start*self.start
self.start += 1
return current
iterator = Cubes(a, b)
it = Cubes(1,5) # gives an iterator object which on iteration will give cubes of values 1-4
for cube in it:
print(cube)
*output*
1
8
27
64
Generator functions are the function which have yield statement instead of return. Unlike the return statement yield gives the next object, each time we call it.
Every Generator is an iterator but not every iterator is a generator. Generator syntax is simpler that iterator.
def cubes(start, stop):
for i in range(start, stop):
yield i * I * i
generator = cubes(a, b)
Generator expressions are like list comprehensions. Instead of square bracket, it is enclosed within parenthesis.
generator = (i*i*i for i in range(a, b))
We can iterate over it in a loop.
You can open a file in python by ‘open’ function. open() returns a file object, and is most commonly used with two arguments: open(filename, mode).
f = open('workfile.txt', 'r')
f is file handle here.
We should always close a file handle after doing stuffs.
f.close()
If you want python to close your file, then you could use with
with open("workfile.txt") as f:
for line in f:
print(line)
After printing the lines the file will be closed.
If mode field is empty, it will open the file in ‘r’ read mode.
To read a file’s contents, call f.read(size), which reads some quantity of data and returns it as a string (in text mode) or bytes object (in binary mode). Size is an optional numeric argument. When size is omitted or negative, the entire contents of the file will be read and returned.
“somefile.txt” contains following lines
#This is first line
#This is second line
#This is third line
with open("somefile.txt", "r") as f:
data = f.read()
print(data)
#Out put
This is first line
This is second line
This is third line
f.readline() reads a single line from the file.
with open("some.txt", "r") as f:
data = f.readline()
print(data)
#Out put
This is first line
The readlines() method to get a list of string values from the file, one string for each line of text.
with open("somefile.txt", "r") as f:
data = f.readlines()
print(data)
#Output
['This is first line\n', 'This is second line\n', 'This is third line\n']
The python interpreter compiles the .py script file and saves the results of the compilation to the __pycache__ directory.
When the project is executed again, if the interpreter identifies that the .py script has not been modified, it skips the compile step and runs the previously generated .pyc file stored in the __pycache__ folder.
Python supports name mangling technique. Any identifier of the form __spam
(at least two leading underscores, at most one trailing underscore) is textually replaced with _classname_spam
, where classname is the current class name with leading underscore(s) stripped.
class A:
def __foo(self):
return ('yes')
a = A()
print(A.__dict__.keys())
print(a._A__foo())
f.write(string) writes the contents of string to the file and returns the number of characters written.
f = open('workfile.txt', 'w')
Print(f.write('This is a test\n'))
Memory management in Python involves a private heap containing all Python objects and data structures. The management of this private heap is ensured internally by the Python memory manager. The Python memory manager has different components which deal with various dynamic storage management aspects, like sharing, segmentation, preallocation or caching.
At the lowest level, a raw memory allocator ensures that there is enough room in the private heap for storing all Python-related data by interacting with the memory manager of the operating system. On top of the raw memory allocator, several object-specific allocators operate on the same heap and implement distinct memory management policies adapted to the peculiarities of every object type. For example, integer objects are managed differently within the heap than strings, tuples or dictionaries because integers imply different storage requirements and speed/space tradeoffs. The Python memory manager thus delegates some of the work to the object-specific allocators, but ensures that the latter operate within the bounds of the private heap.
Class variables are dependent on class. These are created outside the methods of a class and same for all instances.
Instance variables are created with a reference with the instance. They are generally created by assignment to self attributes in a class’s methods.
Class attributes are attributes which are owned by the class itself. They are shared by all the instances of the class. They have the same value for every instance.
Built-in class attributes are the attributes which are automatically created by python with a class. Example: __dict__, __doc__, __name__, __module__ , __bases__.