<jupyter_start><jupyter_text>Learn Python in Jupyter Notebook<jupyter_code># This is a Jupyter notebook used for Python. It consists of cells.
# In order to execute the current cell, press Ctrl + Enter
# In order to execute the current cell and switch to the next new cell, press Shift + Enter
12<jupyter_output><empty_output><jupyter_text>Arithmetic Operations<jupyter_code>45-29 # Operation like a calculator
# _ or 'underscore' is a special variable in Python, which stores the most recent output value of the interpreter
# _ can be used with other expressions and operators as well
print(_,_+4)<jupyter_output>16 20<jupyter_text>Variables are used to store values. Here, a,b,c,d,e,f,g are the variables used to store arithmetic operations. print() is used to display the output.<jupyter_code>a=11+5 # addition
b=11-5 # subtraction
c=11*5 # multiplication
d=11/5 # division
e=11//5 # returns quotient as result
f=11%5 # (modulus) returns remainder as result 
g=11**5 # exponents

print("Addition 11+5 :",a) 
print("Subtraction 11-5 :",b)
print("Multiplication 11*5 :",c)
print("Division 11/5 :",d)
print("Division (// returns quotient) 11//5 :",e)
print("Division (% returns remainder) 11%5 :",f)
print("Exponent 11**5 :",g)<jupyter_output>Addition 11+5 : 16
Subtraction 11-5 : 6
Multiplication 11*5 : 55
Division 11/5 : 2.2
Division (// returns quotient) 11//5 : 2
Division (% returns remainder) 11%5 : 1
Exponent 11**5 : 161051<jupyter_text>Remember, the arithmetic operations always follow the PEDMAS rule. Consider the two examples below:<jupyter_code>print("Result of 5+2*7-8/2**2+1 is :", 5+2*7-8/2**2+1)
print("Result of (5+2)*7-(8/2**2)+1 is :",(5+2)*7-(8/2**2)+1)<jupyter_output>Result of (5+2)*7-(8/2**2)+1 is : 48.0<jupyter_text>Multiple Variable Assignments<jupyter_code>a,b,c = 4,5.5,'Hello'
print(a)
print(b)
print(c)
a,b,*c = [1,2,3,4,5]
print(a)
print(b)
print(c)<jupyter_output>1
2
[3, 4, 5]<jupyter_text>Operations upon variables.Variable name should not start with digit.id Functionhelp Function<jupyter_code>x=4
y=5
print("x+y:",x+y)

# demonstrating the id function
a=1
print("\na:",a,", id of a is:",id(a)) # id(variable) return a unique identifier for an object within the memory
b=a # variable 'b' is pointing to 'a'
print("b:",b,", id of b is:",id(b)) # id of 'b' is same as id of 'a' as they both refer to the same object in memory 

b=2 # value of variable 'b' is changed
print("\nb:",b,", id of b is:",id(b)) # id of 'b' will be changed

# Help Function is called if we require any help in Python
# For example, keywords. We should not use these reserved keywords as our variable names
help('keywords')<jupyter_output>x+y: 9

a: 1 , id of a is: 140734316831376
b: 1 , id of b is: 140734316831376

b: 2 , id of b is: 140734316831408

Here is a list of the Python keywords.  Enter any keyword to get more help.

False               class               from                or
None                continue            global              pass
True                def                 if                  raise
and                 del                 import              return
as                  elif                in                  try
assert              else                is                  while
async               except              lambda              with
await               finally             nonlocal            yield
break               for                 not<jupyter_text>Boolean OperationsBoolean expressionsis and is not statements<jupyter_code>print("11 is greater than 5:",11>5) # greater than
print("11 is less than 5:",11<5) # less than
print("11 is equal to 5:",11==5) # equal to
print("11 is 5:",11 is 5) # is statement is equivalent to ==
print("11 is not equal to 5:",11!=5) # not equal to
print("11 is not 5:",11 is not 5) # is not statement is equivalent to !=
print("11 is lesser than or equal to  5:",11<=5) # less than equal to
print("11 is greater than or equal to 5:",11>=5) # greater than equal to<jupyter_output>11 is greater than 5: True
11 is less than 5: False
11 is equal to 5: False
11 is 5: False
11 is not equal to 5: True
11 is not 5: True
11 is lesser than or equal to  5: False
11 is greater than or equal to 5: True<jupyter_text>Boolean operators (AND, OR, NOT)<jupyter_code># AND: Returns true only if both conditions are true 
# OR: Returns true if either one of both the conditions is true 
# NOT: reverses the result

print("11>5 or 11<5: ",11>5 or 11<5)
print("11>5 and 11<5: ",11>5 and 11<5)
print("False or True: ",False or True)
print("False and True: ",False and True)
print("not(True): ",not(True))
print("not(False): ",not(False))<jupyter_output>11>5 or 11<5:  True
11>5 and 11<5:  False
False or True:  True
False and True:  False
not(True):  False
not(False):  True<jupyter_text>StringsStrings can be specified using both single or double quotes. Strings are immutable.<jupyter_code>x="Orange" # using double quotes
y='Yellow' # using single quotes

print("x:",x)
print("y:",y)

print("\nx==y:",x==y) # equal to
print("x is y:",x is y) # is statement is equivalent to ==
print("x!=y:",x!=y) # not equal to
print("x<y:",x<y) # less than, returns true as 'o' comes before 'y'
print("x>y",x>y) # greater than, returns false as 'o' comes before 'y'
print("1=='1':",1=='1') # 1 is an integer while '1' is a string

# A String Peculiarity
s1='abcdef'
s2='abcdef'
print('\ns1:',s1)
print('s2:',s2)
print("s1 is s2:",s1 is s2) # returns True
s3='abc-def'
s4='abc-def'
print('\ns3:',s3)
print('s4:',s4)
print("s3 is s4:",s3 is s4) # returns False because of the introduction of special character
print("s3 == s4:",s3 == s4)<jupyter_output>x: Orange
y: Yellow

x==y: False
x is y: False
x!=y: True
x<y: True
x>y False
1=='1': False

s1: abcdef
s2: abcdef
s1 is s2: True

s3: abc-def
s4: abc-def
s3 is s4: False
s3 == s4: True<jupyter_text>len() is used to determine the length of specified variable, string, list, etc. Spaces are also counted.<jupyter_code>s="This is a sample text." # special characters and white spaces are also counted
print("s:",s)
print("\nLength of the string 's' is:",len(s))<jupyter_output>s: This is a sample text.

Length of the string 's' is: 22<jupyter_text>in operator is used to check whether a substring is present in the main string<jupyter_code>a="This is a sample string"
print("a:",a)

print("\n'sample' in a:",'sample' in a) 
print("'test' in a:",'test' in a) 
print("'sample' not in a:",'sample' not in a) # not in means not a substring
s="Some text"
print("s:",s)
length=len(s)
print("Length:",length)

print("\nFirst character, s[0]:",s[0]) # first character
print("Character at index position 7, s[7]:",s[7]) # fetches the character at index position 7 in the string
print("Last character, ,s[length-1]:",s[length-1]) # last character

# Negative indexing is also possible. 
print("\nLast character using negative indexing, s[-1]:",s[-1]) # fetches last element
print("Character at index position 4 from the end, s[-4]:",s[-4]) # fetches 4th element from the end (equivalent to index position 7 from the beginning)
print("First character, s[-(length)]:",s[-(length)]) # fetches first element (equivalent to s[0])<jupyter_output>s: Some text
Length: 9

First character, s[0]: S
Character at index position 7, s[7]: x
Last character, ,s[length-1]: t

Last character using negative indexing, s[-1]: t
Character at index position 4 from the end, s[-4]: t
First character, s[-(length)]: S<jupyter_text>Multiple variables of different datatypes can also be assigned together. The type() defines the class of the object.<jupyter_code>a,b,c,d,e=1,'Hello',"Hi",4.5,True # multiple assignment to various variables
print("a:",a)
print("b:",b)
print("c:",c)
print("d:",d)
print("e:",e)

print("\nClass of a:",type(a)) 
print("Class of b:",type(b))
print("Class of c:",type(c))
print("Class of d:",type(d))
print("Class of e:",type(e))<jupyter_output>a: 1
b: Hello
c: Hi
d: 4.5
e: True

Class of a: <class 'int'>
Class of b: <class 'str'>
Class of c: <class 'str'>
Class of d: <class 'float'>
Class of e: <class 'bool'><jupyter_text>Escape sequences are specified using backslash (\\).\n is used as a wildcard in order to print a new blank line.<jupyter_code>print("Text 1\nText 2") # two separate lines are printed
print("Text 1\\nText 2") # In order to print \n as text, we insert a backslash as an escape sequence.
print(r"Text 1\nText 2") # Letter 'r' can also be placed before the string as an alternative to escape sequence (works similar to above)
print("Text 1\tText 2") # \t represents a tab<jupyter_output>Text 1	Text 2<jupyter_text>Three quotes (single or double) are used to define a paragraph, example:<jupyter_code>paragraph= """This is a test paragraph using double quotes.
This is another line.\n"""
print(paragraph)

second_paragraph='''This is a second paragraph using single quotes.
Multiple lines can be used.\n'''
print(second_paragraph)

third_paragraph='''This is a third paragraph. \
Multiple lines can be used. \
If we use \ in the end of line, it continues to remain in the same line.\n'''
print(third_paragraph)

print("Class of paragraph:",type(paragraph)) # paragraph is used mostly for documentation. It is of String datatype.
print("Length of paragraph:",len(paragraph))<jupyter_output>This is a test paragraph using double quotes.
This is another line.

This is a second paragraph using single quotes.
Multiple lines can be used.

This is a third paragraph. Multiple lines can be used. If we use \ in the end of line, it continues to remain in the same line.

Class of paragraph: <class 'str'>
Length of paragraph: 68<jupyter_text>Multiplication of strings<jupyter_code>s1='Hey! '
print(s1*3) # 3 copies of string s1<jupyter_output>Hey! Hey! Hey!<jupyter_text>Strings can be concatenated using the + sign.<jupyter_code>"This is a "+'concatenated string.'<jupyter_output><empty_output><jupyter_text>Functions on String<jupyter_code>s="this is a sample string."
print("s:",s)

print("\ns.upper():",s.upper()) # converts string into upper case
print("s.lower():",s.lower()) # converts string into lower case
print("s.capitalize():",s.capitalize()) # capitalize the first letter
print('s.replace(" ","_"):',s.replace(" ","_")) # replaces blank space with underscvore
print("s.count('s'):",s.count('s')) # returns the number of occurrences, without overlap
print("s.title():",s.title()) # returns the string with first capital letter of each word
print("s.split():",s.split()) # splits the string (on blank spaces)
print("s.split('s'):",s.split('s')) # splits the string (on character 's')
print("s.find('amp'):",s.find('amp')) # returns the starting index position of the first occurence
print("s.index('amp'):",s.index('amp')) # returns the index of specified string
print("s[1:12]:",s[1:12]) # extracts substring from index position 1 to index position 11
print("s[1:12:2]:",s[1:12:2]) # extracts substring from index position 1 to index position 11 while skipping two places
print("s[::-1]:",s[::-1]) # reverses the string
print("s.startswith('this'):",s.startswith('this')) # checks the start of the string
print("s.endswith('this'):",s.endswith('this')) # checks the end of the string
print("s.replace('sample','test'):",s.replace('sample','test'))

a="    String with spaces      "
print("\na:"+a+" has length:",len(a))

print("a.lstrip(): "+a.lstrip()+" has length:",len(a.lstrip())) # lstrip removes the white spaces from the left side
print("a.rstrip(): "+a.rstrip()+" has length:",len(a.rstrip())) # rstrip removes the white spaces from the right side
print("a.strip(): "+a.strip()+" has length:",len(a.strip())) # strip() removes the white spaces from the left and right end

b="00543"
print("\nb:",b)
print("b.lstrip('0'):",b.lstrip('0'))

c='<NAME>'
print('\nc:',c)
print('c.swapcase():',c.swapcase()) # Swaps cases for all the letters of the string

d='12345ABCdef'
print('\nd:',d)
print('d.isalnum():',d.isalnum()) # Checks whether the string is alphanumeric

e='56893'
print('\ne:',e)
print('e.isnumeric():',e.isnumeric()) # Checks whether the string is numeric

f='Learning_Python'
print('\nf:',f)
print('f.isalpha():',f.isalpha()) # Checks whether the string is alphabetic

g='Coding'
print('\ng:',g)
print('g.isalpha():',g.isalpha()) # Checks whether the string is alphabetic
print('g.islower():',g.islower()) # Checks whether the string has lowercase
print('g.isupper():',g.isupper()) # Checks whether the string has uppercase
print('g.istitle():',g.istitle()) # Checks whether the string has a title cased
print('g.isspace():',g.isspace()) # Checks whether all characters in string are whitespace

# partition() return a tuple that includes the first occurrence of the separator sandwiched between the first half and the end half
s='Test-String'
print("\ns:",s)
print("s.partition('-'):",s.partition('-'))<jupyter_output>s: this is a sample string.

s.upper(): THIS IS A SAMPLE STRING.
s.lower(): this is a sample string.
s.capitalize(): This is a sample string.
s.replace(" ","_"): this_is_a_sample_string.
s.count('s'): 4
s.title(): This Is A Sample String.
s.split(): ['this', 'is', 'a', 'sample', 'string.']
s.split('s'): ['thi', ' i', ' a ', 'ample ', 'tring.']
s.find('amp'): 11
s.index('amp'): 11
s[1:12]: his is a sa
s[1:12:2]: hsi  a
s[::-1]: .gnirts elpmas a si siht
s.startswith('this'): True
s.endswith('this'): False
s.replace('sample','test'): this is a test string.

a:    String with spaces       has length: 28
a.lstrip(): String with spaces       has length: 24
a.rstrip():     String with spaces has length: 22
a.strip(): String with spaces has length: 18

b: 00543
b.lstrip('0'): 543

c: SamPlE TeXt
c.swapcase(): sAMpLe tExT

d: 12345ABCdef
d.isalnum(): True

e: 56893
e.isnumeric(): True

f: Learning_Python
f.isalpha(): False

g: Coding
g.isalpha(): True
g.islower(): False
g.isupper(): False
g.ist[...]<jupyter_text>String SubstitutionVariables can be printed within a string using format function as follows:<jupyter_code>s= "I want {} colors".format("two") # {} are used for substituting text
print(s)
print("I want {n} colors".format(n="three"))
n=4
print(f"I want {n} colors") # .format() can be also be replaced using f in the starting

color1='blue'
color2='pink'
number=2
print('\nMy {} favourite colors are {} and {}'.format(number,color1,color2))
print('My {a} favourite colors are {b} and {c}'.format(a=number,b=color1,c=color2))

print("\nString substitution is possible using arguments. {0} {1} {2} and so on....".format("Text_1","Text_2","Text_3"))
print("My text has string: %s" %("Hello.!"))
print("My text has string containing %% sign and %s" %("Hello!!")) # % sign acts as an escape sequence 
print("My text has number: %d" %(42.4))
print("My text has number with 0 decimal places: %.0f" %(42.4))
print("My text has number with 4 decimal places: %.4f" %(42.4))

pi=3.1415926
e=2.7182818
print('\nValues are: pi={} and e={}'.format(pi,e))
values=[pi,e]
print('Values are: pi={values[0]:.2f} and e={values[1]:.3f}'.format(values=values))<jupyter_output>I want two colors
I want three colors
I want 4 colors

My 2 favourite colors are blue and pink
My 2 favourite colors are blue and pink

String substitution is possible using arguments. Text_1 Text_2 Text_3 and so on....
My text has string: Hello.!
My text has string containing % sign and Hello!!
My text has number: 42
My text has number with 0 decimal places: 42
My text has number with 4 decimal places: 42.4000

Values are: pi=3.1415926 and e=2.7182818
Values are: pi=3.14 and e=2.718<jupyter_text>ListsA List is created using square brackets []. Lists are mutable<jupyter_code>[] #This is an empty List<jupyter_output><empty_output><jupyter_text>A list can have multiple elements of same or different data type.<jupyter_code>a= [12, 'abc', 8.95, "xyz",[2,6]]
print("a:",a)
b= [4,7,2,4]
print("b:",b)
c=a+b # concatenation
print("c:",c)

print("\nLength of c:",len(c))
print("'abc' in a:",'abc' in a)
print("35 in b:",35 in b)
print("12 not in a:",12 not in a)
print("2*b:",2*b) # 2 copies of list b
print("min(b):",min(b)) # minimum in list (list must not have strings)
print("max(b):",max(b)) # maximum in list (list must not have strings)
print("sum(b):",sum(b)) # sum of list elements (list must not have strings)
a=[1,2,3,4,5]
print("Original List: {}" .format(a))

# Append method is used to add new elements in the pre-exisiting list.
a.append(6)
print("List after append(): {}" .format(a))

# extend method is used to extend the list
a.extend([7,8,9])
print("List after extend(): {}" .format(a))

# Pop method is used to remove the last element from the list.
a.pop()
print("List after pop(): {}" .format(a))

# Insert into position
a.insert(4,'Text')
print("List after insert(): {}" .format(a))
# A specific element can be referred from a list. For instance:  
a=[1,2,3,4,5]
print("a:",a)

L=(len(a)) 
print("\nLength of a (L): {}" .format(L))# The length of the list. Indexing starts from zero.
print("a[2]:",a[2]) # Third element
print("a[0]:",a[0]) # First element
print("a[L-1]:",a[L-1]) # Last element

# Setting a value in list (mutable)
# For example, set a value of 6 at index position 2
a[2]=6 # 3 is changed to 6
print("\nList after setting new value: {}" .format(a))

# A specific element can be removed from a list using its index position in the pop function.
# For example, remove element 6
a.pop(2) # a.pop(index)
print("List after pop(2): {}" .format(a))

# An element can be deleted from a list using del function by specifying its index position
del(a[0])
print("Final List after deleting an element: {}" .format(a))
a=["Ravi","Rahul","Rohan"]
print("a:",a)

print("\na[2][2]:",a[2][2]) # Third element, Third character
print("a[1][3]:",a[1][3]) # Second element, Fourth character
print("a[0][2]:",a[0][2]) # First element, Third character

# nesting
b= [12, 'abc', ["Text",5.34,23,True], "xyz",[2,6]] 
print("\nb:",b)

print("\nb[1]:",b[1])
print("b[2]:",b[2])
print("b[2][1]:",b[2][1])
print("b[2][0][3]:",b[2][0][3])
a=[1,5,8,2,5,2]
print("a:",a)

print("Class of a:",type(a))
a.reverse() # reverses the list
print("After a.reverse(), a:",a)
a=[1,5,8,2,5,2]
print("a:",a)

print("a.count(5):",a.count(5)) # returns the number of occurences of element 5
print("a.index(5):",a.index(5)) # returns the first index position of element 5
a.remove(5) # removes the first occurence of element 5
print("List after removing 5: ",a)

a.sort() # sorts the list in ascending order
print("\nList after sorting: ",a)

b='This is a sample string consisting of multiple words for the testing purpose.'.split()
b.sort(key=len) # key argument to sort() accepts a function for producing a sort key from an item
print("\nList after sorting using key argument:\n ",b)
a=[1,5,8,2,5,2]
print("a:",a)

print("\na[3:5]:",a[3:5]) # fetches elements from index position 3 to index position 4
print("a[1:]:",a[1:]) # fetches elements from index position 1 till end
print("a[:3]:",a[:3]) # fetches elements from starting till index position 2

b=a # list B is referencing to list A
print("\nList A is {x} and List B is {y}".format(x=a,y=b))
a[2]=5 # change in List A
print("After changing a[2]=5,\nList A is {x} and List B is {y}".format(x=a,y=b)) # notice that list B also changes at the same position since both are referencing to the same list in memory.

c=a[:] # List C is created by cloning list A, a new list is created and not referenced
print("\nList A is {x} and List C is {z}".format(x=a,z=c))
a[2]=3
print("After changing a[2]=3,\nList A is {x} and List C is {z}".format(x=a,z=c))
# notice that list C doesn't changes because both are the different lists and they are not referencing to the same list in memory.
a=[1,2,3]
b=[1,2,3]

print('a==b:',a==b) # Values are equivalent
print('a is b:',a is b) # Separate objects

print('\nid(a):',id(a))
print('id(b):',id(b))<jupyter_output>a==b: True
a is b: False

id(a): 2479002506376
id(b): 2479002505672<jupyter_text>TuplesTuples are exactly similar to Lists, but they are immutable. Tuples are defined inside parenthesis ().<jupyter_code>a=(1,5,8,2,5,2)
print("a:",a)
print("Type: ",type(a))
print("Sum: ",sum(a)) # sum of a tuple containing integers
print("a.count(5): ",a.count(5)) # returns the number of occurences of element 5
print("a.index(8): ",a.index(8)) # returns the first index position of element 8

# functions like reverse, remove and sort will not work since tuples are not mutable in nature.

# sorted function returns a sorted list and can be called on any iterable series (here tuple)
b=sorted(a) # sorted in ascending order
print("\nb: ",b)
print("Type of b: ",type(b)) # b is a list

c=sorted(a,reverse=True) # sorted in descending order
print("\nc: ",c)
print("Sum: ",sum(c)) 

d=reversed(a) # reverses any iterable series (here tuple), returns an object
print('\nIterable Series: {}'.format(a))
print('Reversed List: {}'.format(list(d))) # object converted explicitly into list
a=(100,) # only one element
print('{} is a tuple: {}'.format(a,type(a)))

b= 1,2,3,4,5 # multiple elements without parenthesis
print('\n{} is a tuple: {}'.format(b,type(b)))

# Swapping elements
c,d=10,20
print('\nc: {} & d: {}'.format(c,d))
c,d=d,c
print('After Swapping\nc: {} & d: {}'.format(c,d))

e=tuple('Apple')
print('\ne: {}'.format(e))<jupyter_output>(100,) is a tuple: <class 'tuple'>

(1, 2, 3, 4, 5) is a tuple: <class 'tuple'>

c: 10 & d: 20
After Swapping
c: 20 & d: 10

e: ('A', 'p', 'p', 'l', 'e')<jupyter_text>String PartitionThe partition() method divides a string into three around a separator: prefix, separator, suffixUsing tuple unpacking, the result can be destructured.Underscore(_) can be used as a dummy name in place of the separator<jupyter_code>a='Jack-Martin'
first_name, separator, last_name = a.partition('-') # returns a tuple
print('Name is: {} {}'.format(first_name,last_name))

b='Jack|24'
name, _, age = b.partition('|') # _ is used for invaluable results
print('\nName is: {}\nAge is: {}'.format(name,age))<jupyter_output>Name is: <NAME>

Name is: Jack
Age is: 24<jupyter_text>Objects created explicitly using constructors (int(), str(), list(), float())<jupyter_code>print("int(4): ",int(4))
print("int(4.5): ",int(4.5)) # type conversion from float to integer
print("int('78'): ", int('78')) # type conversion from string to integer
print("int(True): ",int(True)) # type conversion from boolean to integer

print("\nstr('Hey'): ",str('Hey'))
print("str(53): ",str(53)) # type conversion from integer to string
print("str(46.2): ", str(46.2)) # type conversion from float to string
print("str(True): ",str(True)) # type conversion from boolean to string

print("\nfloat(3.6): ",float(3.6))
print("float('21.64'): ", float('21.64')) # type conversion from string to float
print("float(435): ", float(435)) # type conversion from integer to float
print("float(True): ",float(True)) # type conversion from boolean to float

print("\nbool(1): ",bool(1))
print("bool(0): ", bool(0))

print("\nlist([3,6,'fan']): ",list([3,6,'fan']))
print("tuple([2,8,'test', False]): ",tuple([2,8,'test', False]))
print("tuple('String123'): ", tuple('String123'))

# Default Values
print("\nDefault Values\nint(): ",int()) # 0
print("float(): ",float()) #0.0
print("str(): ",str()) # '' (blank string)
print("list(): ",list()) # [] (blank list)
print("bool(): ", bool()) # False<jupyter_output>int(4):  4
int(4.5):  4
int('78'):  78
int(True):  1

str('Hey'):  Hey
str(53):  53
str(46.2):  46.2
str(True):  True

float(3.6):  3.6
float('21.64'):  21.64
float(435):  435.0
float(True):  1.0

bool(1):  True
bool(0):  False

list([3,6,'fan']):  [3, 6, 'fan']
tuple([2,8,'test', False]):  (2, 8, 'test', False)
tuple('String123'):  ('S', 't', 'r', 'i', 'n', 'g', '1', '2', '3')

Default Values
int():  0
float():  0.0
str():  
list():  []
bool():  False<jupyter_text>Advanced Numbers: Octal, Hexadecimal, Binary, Complex<jupyter_code># Normal Integer
i=50
print("Decimal Value:",i)

# converting integer to octal value
o=oct(i)
print("\nOctal Value:",o,", type of o:",type(o)) # number prefixed by 0o (zero and a lowercase "o" or uppercase "O") represents an octal number

# converting integer to hex value
h=hex(i)
print("\nHexadecimal Value:",h,", type of h:",type(h)) # number prefixed by 0x (zero and a lowercase "x" or uppercase "X") represents a hexadecimal number

# converting integer to binary value
b=bin(i)
print("\nBinary Value:",b,", type of b:",type(b)) # number prefixed by 0b (zero and a lowercase "b" or uppercase "B") represents a binary number

# Real & Imaginary Number
x=5+6j
print("\nComplex Number:",x,", type of x:",type(x))
print("Defining complex number using complex():",complex(4,3))

# converting octal to integer value
print("\nOctal to integer: 0o62 is {}".format(0o62))

# converting hex to integer value
print("\nHexadecimal to integer: 0x32 is {}".format(0x32))

# converting binary to integer value
print("\nBinary to integer: 0b110010 is {}".format(0b110010))

# Float numbers representation
print('\nFloat Representation:\n4e8 is {}'.format(4e8))
print('-9.53e18 is {}'.format(-9.53e18))
print('float(nan) is',float('nan'))
print('float(inf) is',float('inf'))
print('float(-inf) is',float('-inf'))

# String representation
print('\nString Representation:\nUnicode: \\u0e21 is {}'.format('\u0e21'))
print('Octal: \\521 is {}'.format('\521'))
print('Hex: \\x521 is {}'.format('\x521'))<jupyter_output>Decimal Value: 50

Octal Value: 0o62 , type of o: <class 'str'>

Hexadecimal Value: 0x32 , type of h: <class 'str'>

Binary Value: 0b110010 , type of b: <class 'str'>

Complex Number: (5+6j) , type of x: <class 'complex'>
Defining complex number using complex(): (4+3j)

Octal to integer: 0o62 is 50

Hexadecimal to integer: 0x32 is 50

Binary to integer: 0b110010 is 50

Float Representation:
4e8 is 400000000.0
-9.53e18 is -9.53e+18
float(nan) is nan
float(inf) is inf
float(-inf) is -inf

String Representation:
Unicode: \u0e21 is ม
Octal: \521 is ő
Hex: \x521 is R1<jupyter_text>Importing Libraries<jupyter_code># math library
import math as m # math module is a library of mathematical functions and constants. As keyword is used to define alias.
print("m.sqrt(16): ",m.sqrt(16)) # square root
print("m.pi: ",m.pi) # value of pi
print("m.e: ",m.e,"\n") # value of e

# The help function is used to find more information on math library
help(m.sqrt)<jupyter_output>m.sqrt(16):  4.0
m.pi:  3.141592653589793
m.e:  2.718281828459045 

Help on built-in function sqrt in module math:

sqrt(x, /)
    Return the square root of x.<jupyter_text>DictionaryDictionaries are defined using a Key-Value pair in curly brackets {}. A key-value can be an integer, string, etc.<jupyter_code>empty_dict={}
empty_dict
a= {
    0:"Zero",
    1:"One",
    2:"Two",
    3:"Three",
    4:"Four",
    5:"Five"
}
# 0,1,2, etc are the keys. Zero, One, Two, etc are the values.  
print("a:",a)

b= {
    4:"four",
    5:"five",
    6:"six",
    7:"seven",
    8:"eight"
}
print("b:",b)

a.update(b) # a dictionary is updated with keys & values of dictionary b, only unique values are stored
print("\nAfter Updating Dictionary,\na:",a)<jupyter_output>a: {0: 'Zero', 1: 'One', 2: 'Two', 3: 'Three', 4: 'Four', 5: 'Five'}
b: {4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight'}

After Updating Dictionary,
a: {0: 'Zero', 1: 'One', 2: 'Two', 3: 'Three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight'}<jupyter_text>A key in dictionary can hold multiple datatypes (Boolean, Integer, Float, String, Tuple) i.e. datatype should be immutableA value in dictionary can hold multiple datatypes (List, Tuple, Boolean, Integer, Float, String) i.e. both immutable and mutable<jupyter_code>a= {
    "Text":"String",
    18:"Integer",
    True:"Boolean",
    (1,2,3):"Tuple",
    3.5:"Float",
    3.5:"Float_duplicate" # 3.5 is the duplicate key, the returned value will be the last defined value
}
print("Dictionary a:",a) # Printing order of dictionary is not ordered

b={
    "Name":"Jack",
    "Age":34,
    "Amount":76890.56,
    "Access":False,
    "Address":(22,'ABC','XYZ'),
    "Mobile":[12345,95748,"45225"]
}
print("\nDictionary b:",b)

# Accessing the dictionary elements using keys
print("\nb['Name']:",b['Name'])
print("a[True]:",a[True])

# Adding new Key-Values pairs in dictionary
a["New Key"]="New Value"
b["Software"]="Python"

print("\na.keys():",a.keys()) # retrieving the list of keys of Dictionary A
print("b.values():",b.values()) # retrieving the list of values of Dictionary B
print("a.items():",a.items()) # returns the list of all items (tuples) in dictionary A

# Converting a tuple into dictionary
x=(('a',1),('b',2),('c',3),('d',4),('e',5))
y=dict(x)
print("\nx: ",x,"\nTuple converted into dictionary: ",y)
# Using pprint library (Pretty Printing) for viewing dictionary neatly
from pprint import pprint as pp
a={
    "Name":"Jack",
    "Age":34,
    "Amount":76890.56,
    "Access":False,
    "Address":(22,'ABC','XYZ'),
    "Mobile":[12345,95748,"45225"]
}
pp(a) # Keys are arranged in a sorted manner<jupyter_output>{'Access': False,
 'Address': (22, 'ABC', 'XYZ'),
 'Age': 34,
 'Amount': 76890.56,
 'Mobile': [12345, 95748, '45225'],
 'Name': 'Jack'}<jupyter_text>SetsSets contains unique elements. They are defined by curly brackets {}.<jupyter_code>s1={1,2,3,4,5}
s2={2,5,1,5,3,2,5,3,4,1,2,4,4,5,2,1,1,3} # elements are repeated
print("Set s1: {}".format(s1))
print("Set s2: {} , only unique elements are contained in the set".format(s2))


# Adding an element in set using add()
s1.add(6)
print("\nAfter adding element in List s1: {}".format(s1))

# Removing an element in set using remove()
s1.remove(6)
s1.discard(6) # always succeeds without raising KeyError
print("After removing element in List s1: {}".format(s1))


# Check if element is present in the set
print("\n6 in s1:",6 in s1)


# Making a copy of set
s2=s1.copy()
print("\ns2 (Copy of set s1): ",s2)

# Clearing contents of set s2
s2.clear()
print("s2: ",s2) # Empty Set


# Converting a set into list using the list constructor
a={3,4,2,4,3,2,4,1,1,1,4,5,3,4,2,2,3}
b=list(a)
print("\nSet a: ",a,"\nSet coverted into list: ",b)

# Converting a list into set using the set constructor
a=[3,4,2,4,3,2,4,1,1,1,4,5,3,4,2,2,3]
b=set(a)
print("\nList a: ",a,"\nList coverted into set: ",b)
a={1,2,3,4}
b={3,4,5,6}
c={5,6}
print("a:",a)
print("b:",b)
print("c:",c)

print("\na.union(b):",a.union(b)) # A union B
print("a.intersection(b):",a.intersection(b)) # A intersection B
print("a.symmetric_difference(b):",a.symmetric_difference(b)) # ~(A intersection B), all other uncommon elements
print("a.difference(b):",a.difference(b)) # A prime
print("b.difference(a):",b.difference(a)) # B prime

print("\nset(a).issuperset(c):",set(a).issuperset(c))
print("set(b).issuperset(c):",set(b).issuperset(c))
print("set(c).issubset(a):",set(c).issubset(a))
print("set(c).issubset(b):",set(c).issubset(b))
print("set(c).isdisjoint(a):",set(c).isdisjoint(a))

d={10,20,30,40,20,10,20,30,50,70}
e={10,20,30,40,20,10,20,30,50,60}

print("\nd:",d)
print("e:",e)
d.update(e) # Update a set with the union of itself and others
print("After updating, d:",d)<jupyter_output>a: {1, 2, 3, 4}
b: {3, 4, 5, 6}
c: {5, 6}

a.union(b): {1, 2, 3, 4, 5, 6}
a.intersection(b): {3, 4}
a.symmetric_difference(b): {1, 2, 5, 6}
a.difference(b): {1, 2}
b.difference(a): {5, 6}

set(a).issuperset(c): False
set(b).issuperset(c): True
set(c).issubset(a): False
set(c).issubset(b): True
set(c).isdisjoint(a): True

d: {70, 40, 10, 50, 20, 30}
e: {40, 10, 50, 20, 60, 30}
After updating, d: {70, 40, 10, 50, 20, 60, 30}<jupyter_text>FrozensetsFrozen sets are a native data type in Python that have the qualities of sets, but are immutable like tuples. The primary reason to use them is to write more clear, functional code. By defining a variable as a frozen set, we’re telling future readers: do not modify this!<jupyter_code>scores = {1,2}
scores.add(3)
print(scores)
scores = {1,2}
frozen_scores = frozenset(scores)

# AttributeError: 'frozenset' object has no attribute 'add'
frozen_scores.add(3)
myList = [1,1,2,3,4]
frozenList = frozenset(myList)
print(frozenList) # frozenset({1, 2, 3, 4})

myString = "Hello"
frozenString = frozenset(myString)
print(frozenString) # frozenset({'e', 'l', 'H', 'o'})<jupyter_output>frozenset({1, 2, 3, 4})
frozenset({'H', 'l', 'e', 'o'})<jupyter_text>User_Defined Input<jupyter_code>a=input("Enter number: ") # input function is used for inputting through keyboard 
print("User entered: {}. It is a string".format(a))
print(type(a)) # result is of string data type

# In order to convert string into number, we can use object constructor int()
b=int(a)
print("\nAfter conversion, {} is a integer".format(b))
print(type(b),'\n')<jupyter_output>Enter number: 56
User entered: 56. It is a string
<class 'str'>

After conversion, 56 is a integer
<class 'int'><jupyter_text>Conditional statements if-else, elif statements<jupyter_code># Indentation is critical in order to avoid error

marks= int(input("Enter Marks: ")) # input() is enclosed inside int() which converts string into integer
print("Marks scored: {}".format(marks))

if marks>=75: # if statement
    print("Grade A" ) 
elif marks>=60 and marks<75: # else if statement
    print("Grade B")
else: # else statement
    print("Grade C")<jupyter_output>Enter Marks: 78
Marks scored: 78
Grade A<jupyter_text>Range()<jupyter_code># range(n) generates a sequence from 0 to n-1
# list(range(n)) is used to represent the range in form of a list

print(range(5), list(range(5))) 
print(range(4,8), list(range(4,8))) 
print(range(1,10,2), list(range(1,10,2)))<jupyter_output>range(0, 5) [0, 1, 2, 3, 4]
range(4, 8) [4, 5, 6, 7]
range(1, 10, 2) [1, 3, 5, 7, 9]<jupyter_text>For Loop<jupyter_code>a=[5,2,6,2,5,1,5] 
for i in a: # iterates over each element in list
    print(i)

print("\n")    
    
a=[5,2,6,2,5,1,5]
for i in range(len(a)): # iterates over each index in list
    print("Element {}: {}".format(i,a[i]))    
    
print("\n")
    
a=[5,2,6,2,5,1,5]
print("Using enumerate,")
for i,k in enumerate(a,start=1): # enumerate() yields (index,value) tuples
    print("Element {}: {}".format(i,k))
    
list(enumerate(a,start=1)) # enumerate function can also be used directly on the list
# List Comprehension using for loop

# Method_1
a=[10,20,30,40,50]
print("a: ",a)
b=[]
for i in a:
    b.append((i*10))
print("b: ",b)

# Method_2
a=[10,20,30,40,50]
b=[i*10 for i in a]
print("b: ",b)
# Example: number squared for even and cubed for odd

x = [1,2,3,4,5,6,7,8,9]
[y**2 if y%2==0 else y**3 for y in x]
# Dictionary Comprehension

a=[10,20,30,40,50]
{x:x**2 for x in a}<jupyter_output><empty_output><jupyter_text>While loop<jupyter_code>a=[5,2,6,2,5,1,5]
i=0
while i<len(a):
    print("Element {}: {}".format(i,a[i]))
    i=i+1
# List Comprehension using while loop
# Data cleaning program (list containing numbers and string are filtered)

i=0
x=["String",84.1, "text", 45.32,23,"Number",24,36.4,"Some text", 31]
y=[]
l=len(x)
print("Original list: {}".format(x))

while i < l:
    if isinstance(x[i],int) or isinstance(x[i],float):
        y.append(x[i])
        x.pop(i)
        l-=1
        i-=1
    i=i+1
    
print("\nAfter Data cleaning,\nList with numbers: {}".format(y))
print("List with strings: {}".format(x))<jupyter_output>Original list: ['String', 84.1, 'text', 45.32, 23, 'Number', 24, 36.4, 'Some text', 31]

After Data cleaning,
List with numbers: [84.1, 45.32, 23, 24, 36.4, 31]
List with strings: ['String', 'text', 'Number', 'Some text']<jupyter_text>Break and continue statements<jupyter_code>for i in range(10):
    if (i==4): 
        continue # element 4 is skipped
    if(i==8):
        break # loop stops as it reaches 8
    print(i)<jupyter_output>0
1
2
3
5
6
7<jupyter_text>User_Defined Functions<jupyter_code># Functions are defined for code-reusability
# Compiling the above code in function

a=["String",84.1, "text", 45.32,23,"Number",24,36.4,"Some text", 31] # Global variable
print("Original list: {}".format(a),"\n")

def data_clean(x): # function declaration
    '''    
    List containing numbers and string are filtered 
    ''' # Documentation of function is enclosed in paragraph
    i=0 
    y=[] # Local variable 
    l=len(x)
    while i < l:
        if isinstance(x[i],int) or isinstance(x[i],float):
            y.append(x[i])
            x.pop(i)
            l-=1
            i-=1
        i=i+1
    return y,x # return statement returns the value 

def sum_list(x):
    '''
    Sum of list elements 
    '''
    sum=0
    for i in x:
        if isinstance(i,int) or isinstance(i, float):
            sum=sum+i
        else: pass # pass statement means do nothing
    return sum

# function calling
d=sum_list(a)   
b,c=data_clean(a)

# On calling the help(), we can see the documentation of the function.
help(data_clean)   
print("Returning function data_clean:",b,c,"\n")

help(sum_list)
print("Returning function sum_list:",d,"\n")
def add(a,b=5):
    sum=a+b
    return sum

print("Calling function when only one parameter is defined: ",add(3)) # default value for b is taken
print("Calling function when both parameters are defined: ",add(4,8)) # default value is replaced<jupyter_output>Calling function when only one parameter is defined:  8
Calling function when both parameters are defined:  12<jupyter_text>Objects and Classes<jupyter_code>a=[1,2,3,4,5] 
dir(a) # returns the list of all Python's objects classes<jupyter_output><empty_output><jupyter_text>Inheritance<jupyter_code>class Human(): # defining class Human
    # attributes
    legs=2
    ears=2
    hands=2
    eyes=2
    
    # function (callable)
    def get_legs(self): # self method is used to refer to the parameters of the instance of a class
        return self.legs
    @property
    def get_ears(self): # property (not a function, hence not callable)
        return self.ears
    
class man(Human): # defining class man, Human is inherited by defining inside parenthesis
    gender='male'
    
class woman(Human): # defining class woman, Human is the parent class
    gender='female'
    
Jon=man() # object created
print("Jon.gender:",Jon.gender)
print("Jon.eyes:",Jon.eyes)
Jon.nose=1
print("Jon.nose:",Jon.nose)
print("Jon.get_legs():",Jon.get_legs()) # parenthesis is required when we are calling the function
print("Jon.get_legs:",Jon.get_legs) # parenthesis is omitted, bound method message appears depicting the function  
print("Jon.get_ears:",Jon.get_ears) # although parenthesis is omitted, bound method message would not appear as '@property' is defined above the method which makes it a property

Jeny=woman()
print("\nJeny.gender:",Jeny.gender)
print("Jeny.hands:",Jeny.hands)<jupyter_output>Jon.gender: male
Jon.eyes: 2
Jon.nose: 1
Jon.get_legs(): 2
Jon.get_legs: <bound method Human.get_legs of <__main__.man object at 0x00000241302056A0>>
Jon.get_ears: 2

Jeny.gender: female
Jeny.hands: 2<jupyter_text>Example for Python in-built Decorator @property:https://www.journaldev.com/14893/python-property-decorator Special methods: __init____str____name__> Before executing code in a module (file), Python will define a few special variables>* If the Python interpreter is running the file as the main program, it sets the special __name__ variable to have a value "__main__”>* If the file is being imported from another module, __name__ will be set to that module’s (file’s) name> To direct the Python interpreter when it first reads a file, add the following conditional (to the bottom) of our script:>>`if __name__ == "__main__ ”:>      main()`> * This will run the main function, if the file is loaded as the main program<jupyter_code>class Student():
    def __init__(self,name, age, city): # __init__ is a special method used to create an instance
        # Data Attributes for initializing objects
        self.name=name;
        self.age=age;
        self.city=city;
    def details(self):
        print("Name is: ", self.name)
        print("Age is: ",  self.age)
        print("City is: ",self.city)
    def __str__(self): # __str__ is a special method used to return a string representation of an object
        return "| %s | %s | %s |" %(self.name,self.age,self.city)
        
a=Student("James",24,"Dubai")
b=Student("Jack",23,"New York")
c=Student("Julia",24,"Delhi")
d=Student("Jim",21,"California")

a.details()
print("\nb.name:",b.name)
print("c.city:",c.city)
print("d:",d)

# Special method __name__
print()
print(__name__) # Evaluates to '__main__' or the actual module name, depending on how the enclosing module is being used<jupyter_output>Name is:  James
Age is:  24
City is:  Dubai

b.name: Jack
c.city: Delhi
d: | Jim | 21 | California |

__main__<jupyter_text>* More OOPS Examples: https://github.com/udacity/DSND_Term2/tree/master/lessons/ObjectOrientedProgramming/JupyterNotebooks Note: For public vs private methods, use an underscore _ before the method name to keep it non-public. For example:<jupyter_code>class M(object):
    
    def abc(self): # abc is a public function
        print('Use Tab button to see public method!')
        
    def _xyz(self): # xyz is a private function
        print("Private method is not visible while using the Tab button!")
        
m = M()
m.abc()
m._xyz()

print()

class N(M):
    
    def pqr(self): # pqr is a public function
        print('Use Tab button to see public method!')

n=N()
n.abc()
n.pqr()
n._xyz()
dir(M)<jupyter_output><empty_output><jupyter_text>Defining Histogram using dictionary<jupyter_code>def histogram(x):
    a=dict()
    for i in x:
        if i not in a:
            a[i]=1
        else:
            a[i]+=1
    return a

# Example_1
s='This is a sample text string'
print("String:",s)

h1=histogram(s)
print("\nHistogram h1:",h1)
print("No. of times 'i' present in h1: %d" %(h1.get('i'))) # fetching a particular frequency of an individual element using get()
print("No. of times 's' present in h1: %d" %(h1.get('s')))

# Example_2
import random as rd
l=[]
for i in range(100):
    k=rd.randint(0,9)
    l.append(k)
print("\nList:",l)
    
h2=histogram(l)
print("\nHistogram h2:",h2)
print("No. of times '3' present in h2: %d" %(h2.get(3)))
print("No. of times '7' present in h2: %d" %(h2.get(7)))<jupyter_output>String: This is a sample text string

Histogram h1: {'T': 1, 'h': 1, 'i': 3, 's': 4, ' ': 5, 'a': 2, 'm': 1, 'p': 1, 'l': 1, 'e': 2, 't': 3, 'x': 1, 'r': 1, 'n': 1, 'g': 1}
No. of times 'i' present in h1: 3
No. of times 's' present in h1: 4

List: [0, 3, 4, 2, 7, 5, 8, 1, 2, 4, 2, 1, 4, 0, 9, 4, 6, 6, 1, 4, 4, 7, 6, 4, 1, 9, 8, 0, 5, 8, 1, 5, 5, 3, 9, 8, 2, 6, 9, 3, 1, 3, 3, 4, 9, 2, 1, 1, 1, 5, 1, 3, 8, 1, 6, 3, 9, 3, 4, 4, 3, 7, 7, 3, 7, 4, 1, 5, 8, 7, 3, 1, 1, 7, 1, 4, 0, 3, 9, 1, 3, 4, 0, 5, 6, 2, 3, 8, 6, 5, 7, 9, 3, 5, 6, 7, 0, 0, 3, 7]

Histogram h2: {0: 7, 3: 16, 4: 13, 2: 6, 7: 10, 5: 9, 8: 7, 1: 16, 9: 8, 6: 8}
No. of times '3' present in h2: 16
No. of times '7' present in h2: 10<jupyter_text>Lambda Statement<jupyter_code># Lambda functions are defined when the functions are used very less

# Example 1
a=[1,2,3,4,5]
print("a: ",a)
def expr(x): # some function for calculation
    return ((x**x+x)/x)-x # some expression
print("Result using function:",expr(4))

# the above function can be illustrated using lambda function
a=lambda x:((x**x+x)/x)-x
print("Result using lambda expression:",a(4))

# Example 2
b="This is a sample text"
c=b.split()
print("\nString:",b)
print("After split():",c)
d=' ' # empty string with space
print("Joining list by '%s' using join():" %(d),d.join(c)) # join() is used to join a list of strings using the string d in between its elements (works opposite to the split())
e=' | '
print("Joining list by '%s' using join():" %(e),e.join(c))

# using lambda 
x=lambda a:' '.join(a)
print("Joining list by ' ' using lambda expression:",x(c))

# Example 3
s=lambda x,y:x+y
print('\nCalculating Sum using lambda function:',s(3,4))<jupyter_output>a:  [1, 2, 3, 4, 5]
Result using function: 61.0
Result using lambda expression: 61.0

String: This is a sample text
After split(): ['This', 'is', 'a', 'sample', 'text']
Joining list by ' ' using join(): This is a sample text
Joining list by ' | ' using join(): This | is | a | sample | text
Joining list by ' ' using lambda expression: This is a sample text

Calculating Sum using lambda function: 7<jupyter_text>pow()The function pow() takes two arguments, equivalent to x^y. With three arguments it is equivalent to (x^y)%z, but may be more efficient for long integers.<jupyter_code>print("2 ^ 5: ",2**5)
print("2 ^ 5: ",pow(2,5))
print("(2 ^ 5) % 3: ",pow(2,5,3))<jupyter_output>2 ^ 5:  32
2 ^ 5:  32
(2 ^ 5) % 3:  2<jupyter_text>abs()The function abs() returns the absolute value of a number. The argument may be an integer or a floating point number. If the argument is a complex number, its magnitude is returned.<jupyter_code>print("abs(2.39): ",abs(2.39))
print("abs(12+2j): ",abs(12+2j))<jupyter_output>abs(2.39):  2.39
abs(12+2j):  12.165525060596439<jupyter_text>round()The function round() will round a number to a given precision in decimal digits (default 0 digits). It does not convert integers to floats.<jupyter_code>print("round(3.14): ",round(3.14))
print("round(3.14,1): ",round(3.14,1))
print("round(314,-2): ",round(314,-2))
print("round(2379,-2): ",round(2379,-2))<jupyter_output>round(3.14):  3
round(3.14,1):  3.1
round(314,-2):  300
round(2379,-2):  2400<jupyter_text>divmod()divmod() takes two arguments and returns a tuple of two values, quotient and remainder<jupyter_code>a=5
b=2
print("a:",a)
print("b:",b)
print("a//b:",a//b) # Quotient
print("a%b:",a%b) # Remainder

quot, rem= divmod(a,b)
print("\nQuotient using divmod():",quot)
print("Remainder using divmod():",rem)<jupyter_output>a: 5
b: 2
a//b: 2
a%b: 1

Quotient using divmod(): 2
Remainder using divmod(): 1<jupyter_text>zip()<jupyter_code># zip() contains one element from each sequence
x=zip("Hello", "12345")
print("x: ",list(x)) # result is changed into list
print("Type: ",type(x)) # zip is a class

y=list(zip([1,2,3,4],('abc','pqr','xyz')))
print("\ny: ",y) # contains three elements (shorter length is considered)

# in order to get back the lists, we can unzip the list of tuples using the asterisk
a,b=zip(*y)
print("\nAfter unzip, \na: {} \nb: {}" .format(a,b))

# zip() in dictionaries
print(f"\nUsing zip() on dictionaries:")

d1 = {'a':1,'b':2}
print(f"d1: {d1}")
d2 = {'c':4,'d':5}
print(f"d2: {d2}")

print(f"list(zip(d1,d2)): {list(zip(d1,d2))}")
print(f"list(zip(d2,d1.values())): {list(zip(d2,d1.values()))}")

# Example: two strings from L1 and L2 concatenated together with a connector between them
def concatenate(L1, L2, connector):
    return [word1+connector+word2 for (word1,word2) in zip(L1,L2)]
print("\nConcatenating strings with connector using zip():",concatenate(['A','B'],['a','b'],'-'))

# Transpose of a matrix

a=[[1,2,3],
   [4,5,6],
   [7,8,9]] 
transpose = [list(i) for i in zip(*a)] 
transpose<jupyter_output><empty_output><jupyter_text>map()map() is used to apply the function on the specified sequence<jupyter_code>a=[1,2,3,4,5,6,7,8,9]
print("List:",a)

def add_two(x): # function that adds 2 to each element 
    x=x+2
    return x
print("\nFunction is applied to all the elements of list using map():",list(map(add_two,a))) 

# lambda function is mostly integrated in these cases in order to reduce the complexity of the program 
c=list(map(lambda x:x+2, a))
print("Lambda expression is applied to all the elements of list using map():",c)

a = [1,2,3,4]
b = [5,6,7,8]
c = [9,10,11,12]
print(f"\nMap() with multiple iterables: {list(map(lambda x,y,z:x+y+z,a,b,c))}")

# map() for finding the length of each word in the phrase (broken by spaces) and return the values in a list
def word_lengths(phrase):
    return list(map(len, phrase.split()))
phrase="This is a testing phrase"
print("\nPhrase: ",phrase)
print("Words Length: ",word_lengths(phrase))<jupyter_output>List: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Function is applied to all the elements of list using map(): [3, 4, 5, 6, 7, 8, 9, 10, 11]
Lambda expression is applied to all the elements of list using map(): [3, 4, 5, 6, 7, 8, 9, 10, 11]

Map() with multiple iterables: [15, 18, 21, 24]

Phrase:  This is a testing phrase
Words Length:  [4, 2, 1, 7, 6]<jupyter_text>filter()filter() is used to filter the elements by applying the function on the particular sequence<jupyter_code>a=[1,2,3,4,5,6,7,8,9]
print("a:",a)

def div_3(x):
    if(x%3==0):
        return x
print("Filter elements in list using filter():",list(filter(div_3,a))) 

def filter_words(word_list, letter):
    return list(filter(lambda word:word[0]==letter,word_list))

words = ['hello','are','cat','dog','ham','hi','go','to','heart']
print("\nWords:",words)
print("Filtering Words starting with 'h' from the list:",filter_words(words,'h'))<jupyter_output>a: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Filter elements in list using filter(): [3, 6, 9]

Words: ['hello', 'are', 'cat', 'dog', 'ham', 'hi', 'go', 'to', 'heart']
Filtering Words starting with 'h' from the list: ['hello', 'ham', 'hi', 'heart']<jupyter_text>reduce()The function reduce(function, sequence) continually applies the function to the sequence. It then returns a single value. If seq = [ s1, s2, s3, ... , sn ], calling reduce(function, sequence) works like this:* At first the first two elements of seq will be applied to function, i.e. func(s1,s2). The list on which reduce() works looks now like this: [ function(s1, s2), s3, ... , sn ]* In the next step the function will be applied on the previous result and the third element of the list, i.e. function(function(s1, s2),s3). The list looks like this now: [ function(function(s1, s2),s3), ... , sn ]* It continues like this until just one element is left and return this element as the result of reduce()<jupyter_code>from functools import reduce

arr = [77,41,42,13]
print("Sum of all elements using reduce(): ",reduce(lambda x,y: x+y,arr)) # similar to sum()

# Example: Get Difference of two elements if the first element is bigger, else add two elements
def myfunc(a,b):
    return a-b if (a > b) else a+b
print("\nmyfunc() result using reduce(): ",reduce(myfunc, arr))

# Example: Converting a list of digits into number
def digits_to_num(digits):
    return reduce(lambda x,y:x*10 + y,digits)
print("\ndigits_to_num() result:",digits_to_num([3,4,3,2,1]))<jupyter_output>Sum of all elements using reduce():  173

myfunc() result using reduce():  65

digits_to_num() result: 34321<jupyter_text>Reading and Writing files  write() method is used to writeread() method reads from the current position until the end of the filereadLines() method is for reading all text on file and return a list of linesreadline() method reads a single line from the current position<jupyter_code># Creating file Test1.txt
with open('Test1.txt','w') as wf: # w represents writing mode, wf is the alias name 
    wf.write("This is a sample test line") # write() is used to write text on file
    wf.write("\nThis is a next line. ")
    wf.write("Continuing last line.")
    
with open('Test1.txt','r') as twf: # r represents reading mode
    print('Writing and Reading text files using write() and read():\n',twf.read(),'\n') # read() is used to read text on file

with open('Test1.txt','a') as twf: # a represents appending in the existing file
    twf.write(" This is the appended text.")
    
with open('Test1.txt','r') as twf: 
    print("Appending text in an existing file using readlines():\n",twf.readlines(),'\n') # readlines() is used to read text on file and returns a list of lines
    
# copying from one file into another    
with open('Test1.txt','r') as rf:
    with open('Test2.txt','w') as wf:
          for line in rf:
                wf.write(line)
                
with open('Test2.txt','r') as twf: 
    print("Copying from one file into another file:\n",twf.readline()) #  readline() is used to read the first line
    print("Reading partial characters:",twf.read(23)) # reading next 23 characters from the last reading position
    print("Moving pointer in the file:",twf.seek(75))
    print("Reading Line:",twf.read())

print("\nName of the file:",twf.name) # fetching the name of file
print("Mode of the file:",twf.mode) # fetching the mode of file
twf.close() # closing file<jupyter_output>Writing and Reading text files using write() and read():
 This is a sample test line
This is a next line. Continuing last line. 

Appending text in an existing file using readlines():
 ['This is a sample test line\n', 'This is a next line. Continuing last line. This is the appended text.'] 

Copying from one file into another file:
 This is a sample test line

Reading partial characters: This is a next line. Co
Moving pointer in the file: 75
Reading Line:  is the appended text.

Name of the file: Test2.txt
Mode of the file: r<jupyter_text>Error handling Raise an exception to interrupt program flowHandle an exception to resume controlUnhandled exceptions will terminate the programException objects contain information about the exceptional eventIllustration of try and except blocksFinally block always executes in the end<jupyter_code># Errors are catched using try & except blocks

# Scenario 1:
try:
    b=34/0
except:
    print('Error! Division by Zero is not possible')
    
# Scenario 2:
try:
    float("Text")
except:
    print('Error! Text cannot be converted into Float datatype')

# Scenario 3:
try:
    'Text'+32
except:
    print('Error! String and Integer cannot be added')<jupyter_output>Error! Division by Zero is not possible
Error! Text cannot be converted into Float datatype
Error! String and Integer cannot be added<jupyter_text>Built-in Exceptions            Exception        Explanation                KeyboardInterrupt        Raised when user hits Ctrl-C, the interrupt key                OverflowError        Raised when a floating-point expression evaluates to a value that is too large                ZeroDivisionError        Raised when attempting to divide by 0                IOError        Raised when an I/O operation fails for an I/O-related reason                IndexError        Raised when a sequence index is outside the range of valid indexes                NameError        Raised when attempting to evaluate an unassigned identifier (name)                TypeError        Raised when an operation of function is applied to an object of the wrong type                ValueError        Raised when operation or function has an argument of the right type but incorrect value                KeyError        Raised when lookup in a mapping fails<jupyter_code>def convert(s):
    '''Convert to an integer'''
    error=0
    try:
        return int(s)
        print("Conversion Succeeded, x={}".format(x))
    except (ValueError, TypeError) as e:
        print('Conversion Failed: {}'.format(str(e)))
        error=1
        raise # Exceptional conditions can be signaled using raise, without an argument it re-raises the current exception
    finally:
        if error==0:
            print('Successful !!')
        elif error==1:
            print('Error is due to the wrong input: {}'.format(s))
convert(34)
convert("Thirty") # Throws ValueError
convert([34,45]) # Throws TypeError<jupyter_output>Conversion Failed: int() argument must be a string, a bytes-like object or a number, not 'list'
Error is due to the wrong input: [34, 45]<jupyter_text>Iterator* Iterable objects can be passed to the built-in iter() to get an iterator.* Iterator objects can be passed to the built-in next() to fetch the next item.<jupyter_code>iterable=['Cow','Hen','Dog','Cat']
iterator=iter(iterable)
print('Iterable List: ',iterable)
print('Iterator:',iterator) # object
print()
while 1:
    try:
        print(next(iterator))
    except StopIteration as e:
        break<jupyter_output><empty_output><jupyter_text>Generators* **specify iterable sequences:** all generators are iterators* **are lazily evaluated:** the next value in the sequence is computed on demand* **can model infinite sequences:** such as data streams with no definite end* **are composable into pipelines:** for natural stream processing* Generators adapt the Just in Time Computations.* Generator functions contains atleast one use of the **yield** keyword.* Each call to a generator function creates a new generator object.* These are generally used for infinite (or large) sequences, sensor readings, mathematical series, massive files, etc<jupyter_code>def testgen():
    yield 1
    yield 2
    yield 3
    
g=testgen()
print('Generator:',g) # object
print()
print(next(g)) # works similar to iterator
for each in g: # printing remaining elements
    print(each)
# Generator Execution

def take(count,iterable):
    ''' Takes item from the front of an iterable
    
    Args:
        count: The maxximum number of items to retrieve
        iterable: The source series
        
    Yields:
        At most 'count' items from an 'iterable'
    '''
    counter=0
    for item in iterable:
        if counter==count:
            return
        counter+=1
        yield item

def distinct(iterable):
    ''' Return unique items by eliminating duplicates
    
    Args:
        iterable: The source series
    
    Yields:
        Unique elements in order from 'iterable'
    '''
    seen=set()
    for item in iterable:
        if item in seen:
            continue
        yield item
        seen.add(item)
    
def run_pipeline():
    items=[3,1,4,1,1,2,5,1,3,6,1,6,1,7,3,1,3,2,4]
    return [item for item in take(5,distinct(items))]

run_pipeline()
# Defining model specifications

squares=(x*x for x in range(1,int(10e5)))
print('Model:',squares) # returns generator object, saves memory by just defining and not running it
print('Sum of model:',sum(squares))
a=[1,2,3]
b=[4,5,6]
from itertools import chain
c=chain(a,b) # joining two lists
print('Chain Object:',c) # object
list(c)<jupyter_output>Chain Object: <itertools.chain object at 0x0000024130205278><jupyter_text>All / Any* **all** returns True if all values are True, else returns False* **any** returns True if any value is True, else returns False<jupyter_code>print('all([True,False,False]):',all([True,False,False]))
print('any([True,False,False]):',any([True,False,False]))<jupyter_output>all([True,False,False]): False
any([True,False,False]): True<jupyter_text>Python DeBugger (PDB)PDB is a standard command-line debugger to find errors in our code. It includes features to let us pause our program, look at the values of variables, and watch program execution step-by-step, so we can understand what our program actually does and find bugs in the logic.* PDB's **where** command shows the current call stack* PDB's **next** command lets execution continue to the next line of code* PDB's **continue** command lets program execution continue indefinitely, or until it is stopped with control-c* PDB's **list** command shows the source code at the current location* PDB's **return** command resumes execution until the end of the current execution<jupyter_code># Used for detecting error in code (for debugging purposes)
import pdb

x = [3,1,2]
y = 6
z = 7

result = y + z
print(result)

# Set a trace using Python Debugger
pdb.set_trace()

result2 = y+x # Error Here
print(result2)<jupyter_output>13
--Return--
> <ipython-input-82-9cfd62d41a4c>(12)<module>()->None
-> pdb.set_trace()
(Pdb) y
6
(Pdb) x
[3, 1, 2]
(Pdb) x[0]+y
9
(Pdb) q
{'fraction_non_alphanumeric': 0.15612968591691995, 'fraction_numerical': 0.03927727119216481, 'mean_word_length': 4.571119473189087, 'pattern_counts': {'":': 13, '<': 216, '<?xml version=': 0, '>': 226, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '57', 'dir': 'jupyter-structured-clean-dedup', 'id': '316928', 'license': 'MIT', 'n_tokens_mistral': 21854, 'n_tokens_neox': 20416, 'n_words': 7087, 'path': 'Learn Python in Jupyter Notebook.ipynb', 'repo_name': 'jamwine/Python-Beginners-Guide', 'size': '59230'}