diff --git a/Numpy/P40_CipherText.py b/Numpy/P40_CipherText.py new file mode 100644 index 0000000..e092f31 --- /dev/null +++ b/Numpy/P40_CipherText.py @@ -0,0 +1,59 @@ +# Author: OMKAR PATHAK +# This program illustrates a simple example for encrypting/ decrypting your text + +LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +LETTERS = LETTERS.lower() + +def encrypt(message, key): + ''' This function lets you to encrypt your message based on a key ''' + encrypted = '' + for chars in message: + if chars in LETTERS: + num = LETTERS.find(chars) + num += key + if num>25: + num=num%25 + num=num-1 + encrypted =encrypted + LETTERS[num] + + return encrypted + +def decrypt(message, key): + ''' This function lets you to decrypt your message based on a key ''' + decrypted = '' + for chars in message: + if chars in LETTERS: + num = LETTERS.find(chars) + if num>25: + num=num%25 + num=num-1 + num = num -key + decrypted =decrypted+LETTERS[num] + + return decrypted + +def main(): + message = str(input('Enter your message: ')) + key = int(input('Enter you key [1 - 26]: ')) + choice = input('Encrypt or Decrypt? [E/D]: ') + + if choice.lower().startswith('e'): + print(encrypt(message, key)) + else: + print(decrypt(message, key)) + +if __name__ == '__main__': + main() + + # OUTPUT: + # omkarpathak@omkarpathak-Inspiron-3542:~/Documents/GITs/Python-Programs/Programs$ python P40_CipherText.py + # Enter your message: omkar + # Enter you key [1 - 26]: 2 + # Encrypt or Decrypt? [E/D]: e + # qomct + # + # omkarpathak@omkarpathak-Inspiron-3542:~/Documents/GITs/Python-Programs/Programs$ python P40_CipherText.py + # Enter your message: qomct + # Enter you key [1 - 26]: 2 + # Encrypt or Decrypt? [E/D]: d + # omkar diff --git a/Numpy/P41_PortScanner.py b/Numpy/P41_PortScanner.py new file mode 100644 index 0000000..f93d3ce --- /dev/null +++ b/Numpy/P41_PortScanner.py @@ -0,0 +1,26 @@ +# Author: OMKAR PATHAK +# This program illustrates a simple port scanner using Python that scans for open ports + +import socket,sys + +def connect(host): + print('Scanning host:', host) + try: + for port in range(1, 1024): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + connection = s.connect_ex((host, port)) #NOTE: connect() needs a tuple! + if (connection == 0): + print('Port {} is open'.format(port)) + s.close() + except KeyboardInterrupt: + print('Exiting because you pressed Ctrl + C') + sys.exit() + + except socket.error: + print('Couldn\'t connect to Server') + +if __name__ == '__main__': + userInput = input('Enter the Server address(URL) to check for open ports: ') + remoteServerIP = socket.gethostbyname(userInput) + print('Server IP:',remoteServerIP) + connect(remoteServerIP) diff --git a/Numpy/P42_MultiprocessingPipes.py b/Numpy/P42_MultiprocessingPipes.py new file mode 100644 index 0000000..bc389ee --- /dev/null +++ b/Numpy/P42_MultiprocessingPipes.py @@ -0,0 +1,25 @@ +# Author: OMKAR PATHAK +# This example illustrates an example for multiprocessing and synchronization using pipes + +from multiprocessing import Process, Pipe + +def parentData(parent): + ''' This function sends the data for the child process ''' + parent.send(['Hello']) + parent.close() + +def childData(child): + ''' This function sends the data for the parent process ''' + child.send(['Bye']) + child.close() + +if __name__ == '__main__': + parent, child = Pipe() # Create Pipe + process1 = Process(target = parentData, args = (parent, )) # Create a process for handling parent data + process2 = Process(target = childData, args = (child, )) # Create a process for handling child data + process1.start() # Start the parent process + process2.start() # Start the child process + print(parent.recv()) # Display data received from child (BYE) + print(child.recv()) # Display data received from parent (HELLO) + process1.join() # Wait till the process completes its execution + process2.join() diff --git a/Numpy/P43_BinarySearchTree.py b/Numpy/P43_BinarySearchTree.py new file mode 100644 index 0000000..eac303f --- /dev/null +++ b/Numpy/P43_BinarySearchTree.py @@ -0,0 +1,145 @@ +# Author: OMKAR PATHAK +# This program illustrates an example of Binary Search Tree using Python + +class Node(object): + def __init__(self, data): + self.data = data + self.leftChild = None + self.rightChild = None + + def insert(self, data): + ''' For inserting the data in the Tree ''' + if self.data == data: + return False # As BST cannot contain duplicate data + + elif data < self.data: + ''' Data less than the root data is placed to the left of the root ''' + if self.leftChild: + return self.leftChild.insert(data) + else: + self.leftChild = Node(data) + return True + + else: + ''' Data greater than the root data is placed to the right of the root ''' + if self.rightChild: + return self.rightChild.insert(data) + else: + self.rightChild = Node(data) + return True + + + def find(self, data): + ''' This function checks whether the specified data is in tree or not ''' + if(data == self.data): + return True + elif(data < self.data): + if self.leftChild: + return self.leftChild.find(data) + else: + return False + else: + if self.rightChild: + return self.rightChild.find(data) + else: + return False + + def preorder(self): + '''For preorder traversal of the BST ''' + if self: + print(str(self.data), end = ' ') + if self.leftChild: + self.leftChild.preorder() + if self.rightChild: + self.rightChild.preorder() + + def inorder(self): + ''' For Inorder traversal of the BST ''' + if self: + if self.leftChild: + self.leftChild.inorder() + print(str(self.data), end = ' ') + if self.rightChild: + self.rightChild.inorder() + + def postorder(self): + ''' For postorder traversal of the BST ''' + if self: + if self.leftChild: + self.leftChild.postorder() + if self.rightChild: + self.rightChild.postorder() + print(str(self.data), end = ' ') + +class Tree(object): + def __init__(self, initial_data = []): + self.root = None + + # If provided, add initial data + for data in initial_data: + self.insert(data) + + def insert(self, data): + if self.root: + return self.root.insert(data) + else: + self.root = Node(data) + return True + + def find(self, data): + if self.root: + return self.root.find(data) + else: + return False + + def preorder(self): + if self.root is not None: + print() + print('Preorder: ') + self.root.preorder() + + def inorder(self): + print() + if self.root is not None: + print('Inorder: ') + self.root.inorder() + + def postorder(self): + print() + if self.root is not None: + print('Postorder: ') + self.root.postorder() + + + def pprint(self, head_node=0, _pre="", _last=True, term=False): + + head_node = self.root if head_node == 0 else head_node + + data = "*" if head_node is None else head_node.data + + print(_pre, "`- " if _last else "|- ", data, sep="") + _pre += " " if _last else "| " + + if term: return + + for i, child in enumerate([head_node.leftChild, head_node.rightChild]): + self.pprint(child, _pre, bool(i) ,term=not(bool(child))) + + +if __name__ == '__main__': + tree = Tree() + tree.insert(10) + tree.insert(12) + tree.insert(5) + tree.insert(4) + tree.insert(20) + tree.insert(8) + tree.insert(7) + tree.insert(15) + tree.insert(13) + tree.pprint() + print(tree.find(1)) + print(tree.find(12)) + tree.preorder() + tree.inorder() + tree.postorder() diff --git a/Numpy/P44_Closures.py b/Numpy/P44_Closures.py new file mode 100644 index 0000000..6f84fa8 --- /dev/null +++ b/Numpy/P44_Closures.py @@ -0,0 +1,21 @@ +# Author: OMKAR PATHAK + +# Wikipedia: +# A closure is a record storing a function[a] together with an environment: +# a mapping associating each free variable of the function (variables that are used locally, but +# defined in an enclosing scope) with the value or reference to which the name was bound when +# the closure was created.A closure—unlike a plain function—allows the function to access those +# captured variables through the closure's copies of their values or references, even when the function +# is invoked outside their scope. + +def outerFunction(text): + text = text + + def innerFunction(): + print(text) + + return innerFunction + +if __name__ == '__main__': + myFunction = outerFunction('Hey!') + myFunction() diff --git a/Numpy/P45_MoreOnClosures.py b/Numpy/P45_MoreOnClosures.py new file mode 100644 index 0000000..9d95bf8 --- /dev/null +++ b/Numpy/P45_MoreOnClosures.py @@ -0,0 +1,27 @@ +# Closures + +import logging +logging.basicConfig(filename='example.log', level=logging.INFO) + + +def logger(func): + def log_func(*args): + logging.info( + 'Running "{}" with arguments {}'.format(func.__name__, args)) + print(func(*args)) + return log_func + +def add(x, y): + return x+y + +def sub(x, y): + return x-y + +add_logger = logger(add) +sub_logger = logger(sub) + +add_logger(3, 3) +add_logger(4, 5) + +sub_logger(10, 5) +sub_logger(20, 10) diff --git a/Numpy/P46_Decorators.py b/Numpy/P46_Decorators.py new file mode 100644 index 0000000..b8f3469 --- /dev/null +++ b/Numpy/P46_Decorators.py @@ -0,0 +1,21 @@ +# Author: OMKAR PATHAK +# In this example program we will see how decorators work in Python + +# Decorators provide a simple syntax for calling higher-order functions. By definition, +# a decorator is a function that takes another function and extends the behavior of the +# latter function without explicitly modifying it. Sounds confusing – but it's really not, +# especially after we go over a number of examples. + +def decorator(myFunc): + def insideDecorator(*args): + print('insideDecorator Function executed before {}'.format(myFunc.__name__)) + return myFunc(*args) + return insideDecorator + +@decorator # Decorator function that takes below function as an argument +def display(*args): + ''' This function is passed as an argument to the decorator function specified above after @ sign ''' + print('In display function') + print(*args) + +display('Hello','Hi',123) diff --git a/Numpy/P47_MoreOnDecorators.py b/Numpy/P47_MoreOnDecorators.py new file mode 100644 index 0000000..f86bb66 --- /dev/null +++ b/Numpy/P47_MoreOnDecorators.py @@ -0,0 +1,27 @@ +# Author: OMKAR PATHAK +# In this example, we will be seeing some more concepts of decorators such as +# property decorator, getters and setters methods. + +class BankAccount(object): + def __init__(self, firstName, lastName): + self.firstName = firstName + self.lastName = lastName + + @property # property decorator + def fullName(self): + return self.firstName + ' ' + self.lastName + + @fullName.setter + def fullName(self, name): + firstName, lastName = name.split(' ') + self.firstName = firstName + self.lastName = lastName + +if __name__ == '__main__': + acc = BankAccount('Omkar', 'Pathak') + print(acc.fullName) # Notice that we can access the method for our class BankAccount without + # parenthesis! This is beacuse of property decorator + + # acc.fullName = 'Omkar Pathak' #This throws an error! Hence setter decorator should be used. + acc.fullName = 'Jagdish Pathak' + print(acc.fullName) diff --git a/Numpy/P48_CountingSort.py b/Numpy/P48_CountingSort.py new file mode 100644 index 0000000..3d914a3 --- /dev/null +++ b/Numpy/P48_CountingSort.py @@ -0,0 +1,52 @@ +# Author: OMKAR PATHAK + +# Approach: +# Counting sort, like radix sort and bucket sort, +# is an integer based algorithm (i.e. the values of the input +# array are assumed to be integers). Hence counting sort is +# among the fastest sorting algorithms around, in theory. The +# particular distinction for counting sort is that it creates +# a bucket for each value and keep a counter in each bucket. +# Then each time a value is encountered in the input collection, +# the appropriate counter is incremented. Because counting sort +# creates a bucket for each value, an imposing restriction is +# that the maximum value in the input array be known beforehand. + +# Implementation notes: +# 1] Since the values range from 0 to k, create k+1 buckets. +# 2] To fill the buckets, iterate through the input list and +# each time a value appears, increment the counter in its +# bucket. +# 3] Now fill the input list with the compressed data in the +# buckets. Each bucket's key represents a value in the +# array. So for each bucket, from smallest key to largest, +# add the index of the bucket to the input array and +# decrease the counter in said bucket by one; until the +# counter is zero. + +# Best Case O(n+k); Average Case O(n+k); Worst Case O(n+k), +# where n is the size of the input array and k means the +# values range from 0 to k. + +def countingSort(myList): + maxValue = 0 + for i in range(len(myList)): + if myList[i] > maxValue: + maxValue = myList[i] + + buckets = [0] * (maxValue + 1) + + for i in myList: + buckets[i] += 1 + + i = 0 + for j in range(maxValue + 1): + for a in range(buckets[j]): + myList[i] = j + i += 1 + + return myList + +if __name__ == '__main__': + sortedList = countingSort([1,23,4,5,6,7,8]) + print(sortedList) diff --git a/Numpy/P49_RockPaperScissors.py b/Numpy/P49_RockPaperScissors.py new file mode 100644 index 0000000..eac20f8 --- /dev/null +++ b/Numpy/P49_RockPaperScissors.py @@ -0,0 +1,51 @@ +# Author: OMKAR PATHAK +# This program illustrates a game of Rock Paper Scissors. +# RULES: +# Rock beats scissors +# Scissors beats paper +# Paper beats rock + +import random, time + +def rockPaperScissors(): + # R => Rock, P => Paper, S => Scissors + computerOptions = ['R', 'P', 'S'] + computer = computerOptions[random.randint(0, 2)] + + forOptions = {'R': 'Rock', 'P': 'Paper', 'S':'Scissors'} + + try: + player = input('Enter your choice [R]ock [P]aper [S]cissors: ') + player = player.upper() + if player in computerOptions: + if player == computer: + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('You both tied!') + elif player == 'R': + if computer == 'P': + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Sorry, you lose! Try again.') + else: + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Congrats, you win!') + elif player == 'S': + if computer == 'R': + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Sorry, you lose! Try again.') + else: + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Congrats, you win!') + elif player == 'P': + if computer == 'S': + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Sorry, you lose! Try again.') + else: + print('Player:',forOptions.get(player),'Computer:',forOptions.get(computer)) + print('Congrats, you win!') + else: + print('Please enter only R, P or S as your choice') + except: + exit() + +if __name__ == '__main__': + rockPaperScissors() diff --git a/Numpy/P50_ListComprehensions.py b/Numpy/P50_ListComprehensions.py new file mode 100644 index 0000000..a7597f1 --- /dev/null +++ b/Numpy/P50_ListComprehensions.py @@ -0,0 +1,52 @@ +# Author: OMKAR PATHAK +# In this example we will see how to write list comprehensions to make our tasks easier + +# Python.org says: +# List comprehensions provide a concise way to create lists. +# Common applications are to make new lists where each element is +# the result of some operations applied to each member of another sequence +# or iterable, or to create a subsequence of those elements that satisfy a certain condition. + +numbers = [] +for i in range(10): + numbers.append(i) +print(numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +# Side Effect of above operation:It creates a variable(or overwrites) named 'x' +# that still exists after the loop completes. To get rid of this Side Effect we use List comprehensions. + +# List comprehension: +numbers = [i for i in range(10)] +print(numbers) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + +# Let us see few more examples +squares = [i * i for i in range(10)] +print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + +# This is same as: +squares = [] +for i in range(10): + squares.append(i * i) + +# Some more: +odds = [i for i in numbers if i % 2 != 0] +print(odds) # [1, 3, 5, 7, 9] + +# This is same as: +odds = [] +for i in numbers: + if i % 2 != 0: + odds.append(i) + +# We can also use functions in comprehensions +def isSqaure(x): + import math + sqrt = int(math.sqrt(x)) + return x == sqrt * sqrt + +squares = [x for x in range(100) if isSqaure(x) == True] +print(squares) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + +# Some Complex comprehensions: +pairs = [[x, x * x] for x in numbers] +print(pairs) # [[0, 0], [1, 1], [2, 4], [3, 9], [4, 16], [5, 25], [6, 36], [7, 49], [8, 64], [9, 81]] diff --git a/Numpy/P51_PythonJSON.py b/Numpy/P51_PythonJSON.py new file mode 100644 index 0000000..363c5f8 --- /dev/null +++ b/Numpy/P51_PythonJSON.py @@ -0,0 +1,25 @@ +# Author: OMKAR PATHAK +# This example shows how to use Python with JSON + +import json + +# For storing on json format +def storeJSON(fileName, data = {}): + with open(fileName, 'w') as fd: + json.dump(data, fd, indent = 4, separators = (',', ': ')) + +# For loading data from a JSON file +def loadJSON(fileName): + with open(fileName) as fd: + data = json.load(fd) + print(data) + return data + +if __name__ == '__main__': + data = loadJSON('example.json') + print(data['menu']['value']) # File + data['menu']['value'] = 'movie' + storeJSON('example.json', data) + print() + loadJSON('example.json') + print(data['menu']['value']) # movie diff --git a/Numpy/P52_BucketSort.py b/Numpy/P52_BucketSort.py new file mode 100644 index 0000000..a8fe614 --- /dev/null +++ b/Numpy/P52_BucketSort.py @@ -0,0 +1,55 @@ +# Author: OMKAR PATHAK +# This program will illustrate how to implement bucket sort algorithm + +# Wikipedia says: Bucket sort, or bin sort, is a sorting algorithm that works by distributing the +# elements of an array into a number of buckets. Each bucket is then sorted individually, either using +# a different sorting algorithm, or by recursively applying the bucket sorting algorithm. It is a +# distribution sort, and is a cousin of radix sort in the most to least significant digit flavour. +# Bucket sort is a generalization of pigeonhole sort. Bucket sort can be implemented with comparisons +# and therefore can also be considered a comparison sort algorithm. The computational complexity estimates +# involve the number of buckets. + +# Time Complexity of Solution: +# Best Case O(n); Average Case O(n); Worst Case O(n) + +from P26_InsertionSort import insertionSort +import math + +DEFAULT_BUCKET_SIZE = 5 + +def bucketSort(myList, bucketSize=DEFAULT_BUCKET_SIZE): + if(len(myList) == 0): + print('You don\'t have any elements in array!') + + minValue = myList[0] + maxValue = myList[0] + + # For finding minimum and maximum values + for i in range(0, len(myList)): + if myList[i] < minValue: + minValue = myList[i] + elif myList[i] > maxValue: + maxValue = myList[i] + + # Initialize buckets + bucketCount = math.floor((maxValue - minValue) / bucketSize) + 1 + buckets = [] + for i in range(0, bucketCount): + buckets.append([]) + + # For putting values in buckets + for i in range(0, len(myList)): + buckets[math.floor((myList[i] - minValue) / bucketSize)].append(myList[i]) + + # Sort buckets and place back into input array + sortedArray = [] + for i in range(0, len(buckets)): + insertionSort(buckets[i]) + for j in range(0, len(buckets[i])): + sortedArray.append(buckets[i][j]) + + return sortedArray + +if __name__ == '__main__': + sortedArray = bucketSort([12, 23, 4, 5, 3, 2, 12, 81, 56, 95]) + print(sortedArray) diff --git a/Numpy/P53_ShellSort.py b/Numpy/P53_ShellSort.py new file mode 100644 index 0000000..46472d1 --- /dev/null +++ b/Numpy/P53_ShellSort.py @@ -0,0 +1,28 @@ +# Author: OMKAR PATHAK +# This program illustrates the shell sort implementation in Python + +# According to Wikipedia "Shell sort or Shell's method, is an in-place comparison sort. +# It can be seen as either a generalization of sorting by exchange (bubble sort) or sorting by +# insertion (insertion sort). The method starts by sorting pairs of elements far apart from each other, +# then progressively reducing the gap between elements to be compared. Starting with far apart elements +# can move some out-of-place elements into position faster than a simple nearest neighbor exchange." + +# Best Case O(n logn); Average Case O(depends on gap sequence); Worst Case O(n) + +def shellSort(myList): + gap = len(myList) // 2 + while gap > 0: + for i in range(gap, len(myList)): + currentItem = myList[i] + j = i + while j >= gap and myList[j - gap] > currentItem: + myList[j] = myList[j - gap] + j -= gap + myList[j] = currentItem + gap //= 2 + + return myList + +if __name__ == '__main__': + myList = [12, 23, 4, 5, 3, 2, 12, 81, 56, 95] + print(shellSort(myList)) diff --git a/Numpy/P54_PythonCSV.py b/Numpy/P54_PythonCSV.py new file mode 100644 index 0000000..64585d7 --- /dev/null +++ b/Numpy/P54_PythonCSV.py @@ -0,0 +1,30 @@ +# Author: OMKAR PATHAK +# In this example we will see how to use CSV files with Python + +# csv.QUOTE_ALL = Instructs writer objects to quote all fields. +# csv.QUOTE_MINIMAL = Instructs writer objects to only quote those fields which contain special characters such +# as delimiter, quotechar or any of the characters in lineterminator. +# csv.QUOTE_NONNUMERIC = Instructs writer objects to quote all non-numeric fields. +# Instructs the reader to convert all non-quoted fields to type float. +# csv.QUOTE_NONE = Instructs writer objects to never quote fields. + +import csv + +def csvRead(filePath): + with open(filePath) as fd: + reader = csv.reader(fd, delimiter = ',') + for row in reader: + print(row[0] + ' ' + row[1]) + +def csvWrite(filePath, data): + with open(filePath, 'a') as fd: + writer = csv.writer(fd, delimiter=',', quoting=csv.QUOTE_NONNUMERIC) + writer.writerow(data) + +if __name__ == '__main__': + # data = ['Firstname', 'Lastname'] + # csvWrite('example.csv', data) + userInput = input('What is your Fullname? ') + userInput = userInput.split(' ') + csvWrite('example.csv', userInput) + csvRead('example.csv') diff --git a/Numpy/P55_Isogram.py b/Numpy/P55_Isogram.py new file mode 100644 index 0000000..6c7b230 --- /dev/null +++ b/Numpy/P55_Isogram.py @@ -0,0 +1,29 @@ +# Author: OMKAR PATHAK + +# ISOGRAM: An isogram (also known as a "nonpattern word") is a logological term for a word +# or phrase without a repeating letter + +def is_isogram(word): + # Convert the word or sentence in lower case letters. + clean_word = word.lower() + # Make ann empty list to append unique letters + letter_list = [] + for letter in clean_word: + # If letter is an alphabet then only check + if letter.isalpha(): + if letter in letter_list: + return False + letter_list.append(letter) + + return True + +if __name__ == '__main__': + print(is_isogram("")) # True + print(is_isogram("isogram")) # True + print(is_isogram("eleven")) # False + print(is_isogram("subdermatoglyphic")) # True + print(is_isogram("Alphabet")) # False + print(is_isogram("thumbscrew-japingly")) # True + print(is_isogram("Hjelmqvist-Gryb-Zock-Pfund-Wax")) # True + print(is_isogram("Emily Jung Schwartzkopf")) # True + print(is_isogram("accentor")) # False diff --git a/Numpy/P56_Pangram.py b/Numpy/P56_Pangram.py new file mode 100644 index 0000000..be2f31d --- /dev/null +++ b/Numpy/P56_Pangram.py @@ -0,0 +1,36 @@ +# Author: OMKAR PATHAK + +# PANGRAM: A sentence containing every letter of the alphabet. + +from collections import Counter + +def pangram(sentence): + sentence = sentence.lower() + check = 'abcdefghijklmnopqrstuvwxyz' + alphabets = [] + for letter in sentence: + if letter.isalpha(): + if letter in alphabets: + pass + else: + alphabets.append(letter) + + alphabets = ''.join(alphabets) + if Counter(check) == Counter(alphabets): + return True + else: + return False + +# A short version of above function: +def pangram2(sentence): + alphabet = list(map(chr, range(97, 123))) + formattedString = ''.join(c for c in sentence if c.isalpha()).lower() + return set(alphabet) == set(formattedString) + +if __name__ == '__main__': + print(pangram('the quick brown fox jumps over the lazy dog')) # True + print(pangram('the_quick_brown_fox_jumps_over_the_lazy_dog')) # True + print(pangram('the 1 quick brown fish jumps over the 2 lazy dogs')) # False + print(pangram('Five quacking Zephyrs jolt my wax bed.')) # True + print(pangram('the quick brown fox jumped over the lazy FOX')) # False + print(pangram(' ')) # False diff --git a/Numpy/P57_Anagram.py b/Numpy/P57_Anagram.py new file mode 100644 index 0000000..4d282d0 --- /dev/null +++ b/Numpy/P57_Anagram.py @@ -0,0 +1,27 @@ +# Author: OMKAR PATHAK + +# ANAGRAM: An anagram is direct word switch or word play, the result of rearranging the letters +# of a word or phrase to produce a new word or phrase, using all the original letters exactly once + +# We are taking a word and a list. We return the anagrams of that word from the given list and return the +# list of anagrams else return empty list + +from collections import Counter + +def anagram(word, myList): + word = word.lower() + anagrams = [] + for words in myList: + if word != words.lower(): + if Counter(word) == Counter(words.lower()): + anagrams.append(words) + return anagrams + +if __name__ == '__main__': + print(anagram("ant", ["tan", "stand", "at"])) # ['tan'] + print(anagram("master", ["stream", "pigeon", "maters"])) # ['stream', 'maters'] + print(anagram("good", ["dog", "goody"])) # [] + print(anagram("allergy",[ + "gallery", "ballerina", "regally", "clergy", "largely", "leading" + ])) # ['gallery', 'regally', 'largely'] + print(anagram("BANANA", ["Banana"])) # [] diff --git a/Numpy/P58_PerfectNumber.py b/Numpy/P58_PerfectNumber.py new file mode 100644 index 0000000..bcce192 --- /dev/null +++ b/Numpy/P58_PerfectNumber.py @@ -0,0 +1,21 @@ +# Author: OMKAR PATHAK + +# Wikipedia : In number theory, a perfect number is a positive integer that is equal to the sum of +# its proper positive divisors, that is, the sum of its positive divisors excluding the number itself +# (also known as its aliquot sum). Equivalently, a perfect number is a number that is half the sum of all +# of its positive divisors (including itself). +# Example : The first perfect number is 6, because 1, 2, and 3 are its proper positive divisors, +# and 1 + 2 + 3 = 6. Equivalently, the number 6 is equal to half the sum of all its positive divisors: +# ( 1 + 2 + 3 + 6 ) / 2 = 6. The next perfect number is 28 = 1 + 2 + 4 + 7 + 14. This is followed by the +# perfect numbers 496 and 8128. + +def perfectNumber(number): + sum = 0 + for x in range(1, number): + if number % x == 0: + sum += x + return sum == number + +if __name__ == '__main__': + print(perfectNumber(6)) # True + print(perfectNumber(3)) # False diff --git a/Numpy/P59_PascalTriangle.py b/Numpy/P59_PascalTriangle.py new file mode 100644 index 0000000..8a84384 --- /dev/null +++ b/Numpy/P59_PascalTriangle.py @@ -0,0 +1,32 @@ +# Author: OMKAR PATHAK + +# PASCAL TRAINGLE: To build the triangle, start with "1" at the top, then continue placing numbers +# below it in a triangular pattern. Each number is the numbers directly above it added together. + +# generates the nth row of Pascal's Triangle +def pascalRow(n): + if n == 0: + return [1] + else: + N = pascalRow(n-1) + return [1] + [N[i] + N[i+1] for i in range(n-1)] + [1] + +# create a triangle of n rows +def pascalTriangle(n): + triangle = [] + for i in range(n): + triangle.append(pascalRow(i)) + return triangle + +if __name__ == '__main__': + for i in pascalTriangle(7): + print(i) + + # OUTPUT: + # [1] + # [1, 1] + # [1, 2, 1] + # [1, 3, 3, 1] + # [1, 4, 6, 4, 1] + # [1, 5, 10, 10, 5, 1] + # [1, 6, 15, 20, 15, 6, 1] diff --git a/Numpy/P60_PickleModule.py b/Numpy/P60_PickleModule.py new file mode 100644 index 0000000..b0c68e4 --- /dev/null +++ b/Numpy/P60_PickleModule.py @@ -0,0 +1,32 @@ +# Author: OMKAR PATHAK + +# In this example we will see how to use pickle module for storing the data efficiently! +# The pickle module translates an in-memory Python object into a serialized byte stream—a string of bytes +# that can be written to any file-like object. + +import pickle + +def storeData(): + # initializing data to be stored in db + Omkar = {'key' : 'Omkar', 'name' : 'Omkar Pathak', 'age' : 21, 'pay' : 40000} + Jagdish = {'key' : 'Jagdish', 'name' : 'Jagdish Pathak', 'age' : 50, 'pay' : 50000} + + # database + db = {} + db['Omkar'] = Omkar + db['Jagdish'] = Jagdish + + dbfile = open('examplePickle', 'ab') # Its important to use binary mode + pickle.dump(db, dbfile) # source, destination + dbfile.close() + +def loadData(): + dbfile = open('examplePickle', 'rb') # for reading also binary mode is important + db = pickle.load(dbfile) + for keys in db: + print(keys,'=>',db[keys]) + dbfile.close() + +if __name__ == '__main__': + storeData() + loadData() diff --git a/Numpy/P61_AddressBook.py b/Numpy/P61_AddressBook.py new file mode 100644 index 0000000..98dec39 --- /dev/null +++ b/Numpy/P61_AddressBook.py @@ -0,0 +1,150 @@ +# Author: OMKAR PATHAK + +# In this small mini project we will be creating a simple address book application that will store, search and +# delete records + +import pickle, os + +class AddressBook(object): + def __init__(self, name = None, address = None, email = None, phone = None): + self.name = name + self.address = address + self.email = email + self.phone = phone + self.contacts = {} + self.filename = 'addressbook' + + def __str__(self): + return '[Name: {0} | Address: {1} | Email: {2} | Phone: {3}]'.format(self.name, self.address, self.email, self.phone) + + def __repr__(self): + return '[Name: {0} | Address: {1} | Email: {2} | Phone: {3}]'.format(self.name, self.address, self.email, self.phone) + + # Adding details provided by the user in our Address Book + def addContacts(self): + try: + if os.path.exists(self.filename) and os.path.getsize(self.filename) > 0: + myAddressBook = open(self.filename, 'rb') + data = pickle.load(myAddressBook) + myAddressBook.close() + else: + myAddressBook = open(self.filename, 'wb') + data = {} + + contact = self.getDetailsFromUser() + data[contact['Name']] = contact + myAddressBook = open(self.filename, 'wb') + pickle.dump(data, myAddressBook) + myAddressBook.close() + print('Contact Added Successfully!') + except: + print('There was an error! Contact was not added.') + finally: + myAddressBook.close() + + # Getting the details from the user to adding the Address Book + def getDetailsFromUser(self): + try: + self.contacts['Name'] = str(input('Enter Contact\'s Full Name: ')) + self.contacts['Address'] = str(input('Enter Contact\'s Address: ')) + self.contacts['Email'] = str(input('Enter Contact\'s Email Address: ')) + self.contacts['Phone'] = int(input('Enter Contact\'s Phone Number: ')) + return self.contacts + except KeyboardInterrupt as error: + raise error + + # To display ALL the contact in our Address Book + def displayContacts(self): + if os.path.exists(self.filename) and os.path.getsize(self.filename) > 0: + myAddressBook = open(self.filename, 'rb') + data = pickle.load(myAddressBook) + myAddressBook.close() + if data: + for records in data.values(): + print(records) + myAddressBook.close() + else: + print('No Record in database.') + + # To search for a specific contact in our Address Book + def searchContacts(self): + if os.path.exists(self.filename) and os.path.getsize(self.filename) > 0: + myAddressBook = open(self.filename, 'rb') + data = pickle.load(myAddressBook) + myAddressBook.close() + try: + contactToSearch = input('Enter the name of the contact to search: ') + counter = 0 + for contact in data.values(): + if contactToSearch in contact['Name']: + print(data[contact['Name']]) + counter += 1 + if counter == 0: + print('No record found whose name is:', contactToSearch) + except: + print('Error occured!') + else: + print('No Record in database.') + + # For modifying contacts + def modifyContacts(self): + if os.path.exists(self.filename) and os.path.getsize(self.filename) > 0: + myAddressBook = open(self.filename, 'rb') + data = pickle.load(myAddressBook) + myAddressBook.close() + try: + contactToModify = input('Enter the name of the contact to modify (Only enter full name): ') + # Search for the record to update + for contact in data.values(): + if contactToModify == contact['Name']: + contact = data[contactToModify] + break + option = int(input('1. To modify name, 2. To modify address, 3. To modify email, 4. To modify phone: ')) + if option == 1: + contact['Name'] = input('Enter Name to modify: ') + del data[contactToModify] + data[contact['Name']] = contact + print('Successful') + elif option == 2: + contact['Address'] = input('Enter Address to modify: ') + del data[contactToModify] + data[contactToModify] = contact + print('Successful') + elif option == 3: + contact['Email'] = input('Enter Email to modify: ') + del data[contactToModify] + data[contactToModify] = contact + print('Successful') + elif option == 4: + contact['Phone'] = input('Enter Phone to modify: ') + del data[contactToModify] + data[contactToModify] = contact + print('Successful') + else: + print('Incorrect option selected.') + except: + print('Error occured. No such record found. Try Again!') + finally: + myAddressBook = open(self.filename, 'wb') + pickle.dump(data, myAddressBook) + myAddressBook.close() + else: + print('No Record in database.') + +if __name__ == '__main__': + myBook = AddressBook() + print('Enter 1. To Add Contacts 2. For Searching a Contact 3. For Modifying a Contact 4. To Display Contacts 5. To Exit') + while True: + choice = int(input('Enter your choice: ')) + if choice == 1: + myBook.addContacts() + elif choice == 2: + myBook.searchContacts() + elif choice == 3: + myBook.modifyContacts() + elif choice == 4: + myBook.displayContacts() + elif choice == 5: + exit() + else: + print('Invalid Option. Try Again!') diff --git a/Numpy/P62_BinaryTree.py b/Numpy/P62_BinaryTree.py new file mode 100644 index 0000000..56a3bc1 --- /dev/null +++ b/Numpy/P62_BinaryTree.py @@ -0,0 +1,82 @@ +# Author: OMKAR PATHAK +# A data structure in which a record is linked to two successor records, usually referred to as +# the left branch when greater and the right when less than the previous record. + +class BinaryTree(object): + + def __init__(self,nodeData): + self.left = None + self.right = None + self.nodeData = nodeData + + def getLeftChild(self): + return self.left + + def getRightChild(self): + return self.right + + def setnodeDataValue(self,value): + self.nodeData = value + + def getnodeDataValue(self): + return self.nodeData + + def insertRight(self,newnodeData): + if self.right == None: + self.right = BinaryTree(newnodeData) + else: + tree = BinaryTree(newnodeData) + tree.right = self.right + self.right = tree + + def insertLeft(self,newnodeData): + if self.left == None: + self.left = BinaryTree(newnodeData) + else: + tree = BinaryTree(newnodeData) + self.left = tree + tree.left = self.left + + + + +def printTree(tree): + if tree != None: + printTree(tree.getLeftChild()) + print(tree.getnodeDataValue()) + printTree(tree.getRightChild()) + + +def pprint(head_node, _pre="", _last=True, term=False): + data = "*" if head_node is None else head_node.nodeData + + print(_pre, "`- " if _last else "|- ", data, sep="") + _pre += " " if _last else "| " + + if term: return + + left = head_node.getLeftChild() + right = head_node.getRightChild() + + for i, child in enumerate([left, right]): + pprint(child, _pre, bool(i) ,term=not(bool(child))) + + + + +def testTree(): + myTree = BinaryTree("1") + myTree.insertLeft("2") + myTree.insertRight("3") + myTree.insertRight("4") + printTree(myTree) + pprint(myTree) + +if __name__ == '__main__': + testTree() + + # OUTPUT + # 2 + # 1 + # 4 + # 3 diff --git a/Numpy/P63_Graph.py b/Numpy/P63_Graph.py new file mode 100644 index 0000000..0d562b2 --- /dev/null +++ b/Numpy/P63_Graph.py @@ -0,0 +1,80 @@ +# Author: OMKAR PATHAK +# In this example, we will see how to implement graphs in Python + +class Vertex(object): + ''' This class helps to create a Vertex for our graph ''' + def __init__(self, key): + self.key = key + self.edges = {} + + def addNeighbour(self, neighbour, weight = 0): + self.edges[neighbour] = weight + + def __str__(self): + return str(self.key) + 'connected to: ' + str([x.key for x in self.edges]) + + def getEdges(self): + return self.edges.keys() + + def getKey(self): + return self.key + + def getWeight(self, neighbour): + try: + return self.edges[neighbour] + except: + return None + +class Graph(object): + ''' This class helps to create Graph with the help of created vertexes ''' + def __init__(self): + self.vertexList = {} + self.count = 0 + + def addVertex(self, key): + self.count += 1 + newVertex = Vertex(key) + self.vertexList[key] = newVertex + return newVertex + + def getVertex(self, vertex): + if vertex in self.vertexList: + return self.vertexList[vertex] + else: + return None + + def addEdge(self, fromEdge, toEdge, cost = 0): + if fromEdge not in self.vertexList: + newVertex = self.addVertex(fromEdge) + if toEdge not in self.vertexList: + newVertex = self.addVertex(toEdge) + self.vertexList[fromEdge].addNeighbour(self.vertexList[toEdge], cost) + + def getVertices(self): + return self.vertexList.keys() + + def __iter__(self): + return iter(self.vertexList.values()) + + +if __name__ == '__main__': + graph = Graph() + graph.addVertex('A') + graph.addVertex('B') + graph.addVertex('C') + graph.addVertex('D') + + graph.addEdge('A', 'B', 5) + graph.addEdge('A', 'C', 6) + graph.addEdge('A', 'D', 2) + graph.addEdge('C', 'D', 3) + + for vertex in graph: + for vertexes in vertex.getEdges(): + print('({}, {}) => {}'.format(vertex.getKey(), vertexes.getKey(), vertex.getWeight(vertexes))) + + # OUTPUT: + # (C, D) => 3 + # (A, C) => 6 + # (A, D) => 2 + # (A, B) => 5 diff --git a/Numpy/P64_DepthFirstTraversal.py b/Numpy/P64_DepthFirstTraversal.py new file mode 100644 index 0000000..5cf6da8 --- /dev/null +++ b/Numpy/P64_DepthFirstTraversal.py @@ -0,0 +1,82 @@ +# Author: OMKAR PATHAK + +# Depth first search is performed in three ways: +# Inorder +# Preorder +# Postorder + +class Node(object): + def __init__(self, data = None): + self.left = None + self.right = None + self.data = data + + # for setting left node + def setLeft(self, node): + self.left = node + + # for setting right node + def setRight(self, node): + self.right = node + + # for getting the left node + def getLeft(self): + return self.left + + # for getting right node + def getRight(self): + return self.right + + # for setting data of a node + def setData(self, data): + self.data = data + + # for getting data of a node + def getData(self): + return self.data + + +# in this we traverse first to the leftmost node, then print its data and then traverse for rightmost node +def inorder(Tree): + if Tree: + inorder(Tree.getLeft()) + print(Tree.getData(), end = ' ') + inorder(Tree.getRight()) + return + +# in this we first print the root node and then traverse towards leftmost node and then to the rightmost node +def preorder(Tree): + if Tree: + print(Tree.getData(), end = ' ') + preorder(Tree.getLeft()) + preorder(Tree.getRight()) + return + +# in this we first traverse to the leftmost node and then to the rightmost node and then print the data +def postorder(Tree): + if Tree: + postorder(Tree.getLeft()) + postorder(Tree.getRight()) + print(Tree.getData(), end = ' ') + return + +if __name__ == '__main__': + root = Node(1) + root.setLeft(Node(2)) + root.setRight(Node(3)) + root.left.setLeft(Node(4)) + + print('Inorder Traversal:') + inorder(root) + print('\nPreorder Traversal:') + preorder(root) + print('\nPostorder Traversal:') + postorder(root) + + # OUTPUT: + # Inorder Traversal: + # 4 2 1 3 + # Preorder Traversal: + # 1 2 4 3 + # Postorder Traversal: + # 4 2 3 1 diff --git a/Numpy/P65_BreadthFirstTraversal.py b/Numpy/P65_BreadthFirstTraversal.py new file mode 100644 index 0000000..1517593 --- /dev/null +++ b/Numpy/P65_BreadthFirstTraversal.py @@ -0,0 +1,51 @@ +# Author: OMKAR PATHAK + +# Breadth First Traversal is the one in which we print the data level wise. Refer below code and output for more +# explanation + +class Node(object): + def __init__(self, data = None): + self.leftChild = None + self.rightChild = None + self.data = data + +def height(node): + if node is None: + return 0 + else: + leftHeight = height(node.leftChild) + rightHeight = height(node.rightChild) + + if leftHeight > rightHeight: + return leftHeight + 1 + else: + return rightHeight + 1 + +def breadthFirstTraversal(root): + if root == None: + return 0 + else: + h = height(root) + for i in range(h + 1): + printBFT(root, i) + +def printBFT(root, level): + if root is None: + return + else: + if level == 1: + print(root.data, end = ' ') + elif level > 1: + printBFT(root.leftChild, level - 1) + printBFT(root.rightChild, level - 1) + +if __name__ == '__main__': + root = Node(1) + root.leftChild = Node(2) + root.rightChild = Node(3) + root.leftChild.leftChild = Node(4) + + breadthFirstTraversal(root) + + # OUTPUT: + # 1 2 3 4 diff --git a/Numpy/P66_HeapSort.py b/Numpy/P66_HeapSort.py new file mode 100644 index 0000000..0324b56 --- /dev/null +++ b/Numpy/P66_HeapSort.py @@ -0,0 +1,50 @@ +# Author: OMKAR PATHAK + +# Approach: +# Heap sort happens in two phases. In the first phase, the array +# is transformed into a heap. A heap is a binary tree where +# 1) each node is greater than each of its children +# 2) the tree is perfectly balanced +# 3) all leaves are in the leftmost position available. +# In phase two the heap is continuously reduced to a sorted array: +# 1) while the heap is not empty +# - remove the top of the head into an array +# - fix the heap. + +# Time Complexity of Solution: +# Best O(nlog(n)); Average O(nlog(n)); Worst O(nlog(n)). + +def HeapSort(alist): + heapify(alist) # create the heap + end = len(alist) - 1 + while end > 0: + alist[end], alist[0] = alist[0], alist[end] + shiftDown(alist, 0, end - 1) + end -= 1 + +def heapify(alist): + ''' This function helps to maintain the heap property ''' + # start = (len(alist) - 2) // 2 (faster execution) + start = len(alist) // 2 + while start >= 0: + shiftDown(alist, start, len(alist) - 1) + start -= 1 + +def shiftDown(alist, start, end): + root = start + while root * 2 + 1 <= end: + child = root * 2 + 1 + # right child exists and is greater than left child + if child + 1 <= end and alist[child] < alist[child + 1]: + child += 1 + # if child is greater than root(parent), then swap their positions + if child <= end and alist[root] < alist[child]: + alist[root], alist[child] = alist[child], alist[root] + root = child + else: + return + +if __name__ == '__main__': + alist = [12, 2, 4, 5, 2, 3] + HeapSort(alist) + print('Sorted Array:',alist) diff --git a/Numpy/P67_SieveOfEratosthenes.py b/Numpy/P67_SieveOfEratosthenes.py new file mode 100644 index 0000000..ff81f35 --- /dev/null +++ b/Numpy/P67_SieveOfEratosthenes.py @@ -0,0 +1,34 @@ +# Auhtor: OMKAR PATHAK + +# Sieve of Eratosthenes is one of the efficient algorithms to find all the prime numbers upto n, where n can be +# upto 10 million. This algorithm is very efficient and fast and hence is preferred by many competitive programmers. + +# Algo: +# 1. Create a list of consecutive integers from 2 to n: (2, 3, 4, …, n). +# 2. Initially, let p equal 2, the first prime number. +# 3. Starting from p, count up in increments of p and mark each of these numbers greater than p itself in the list. +# These numbers will be 2p, 3p, 4p, etc.; note that some of them may have already been marked. +# 4. Find the first number greater than p in the list that is not marked. If there was no such number, stop. Otherwise, +# let p now equal this number (which is the next prime), and repeat from step 3. +# When the algorithm terminates, all the numbers in the list that are not marked are prime. + +def SieveOfEratosthenes(n): + primes = [True] * (n + 1) + p = 2 # because p is the smallest prime + + while(p * p <= n): + # if p is not marked as False, this it is a prime + if(primes[p]) == True: + # mark all the multiples of number as False + for i in range(p * 2, n + 1, p): + primes[i] = False + + p += 1 + + # printing all primes + for i in range(2, n): + if primes[i]: + print(i) + +if __name__ == '__main__': + SieveOfEratosthenes(1000) diff --git a/Numpy/P68_TopologicalSort.py b/Numpy/P68_TopologicalSort.py new file mode 100644 index 0000000..c7db7a4 --- /dev/null +++ b/Numpy/P68_TopologicalSort.py @@ -0,0 +1,66 @@ +# Author: OMKAR PATHAK + +# Time Complexity: O(|V| + |E|) +# One important point to remember is that topological sort can be applied only to acyclic graph. + +class Graph(): + def __init__(self, count): + self.vertex = {} + self.count = count # vertex count + + # for printing the Graph vertexes + def printGraph(self): + for i in self.vertex.keys(): + print(i,' -> ', ' -> '.join([str(j) for j in self.vertex[i]])) + + # for adding the edge beween two vertexes + def addEdge(self, fromVertex, toVertex): + # check if vertex is already present, + if fromVertex in self.vertex.keys(): + self.vertex[fromVertex].append(toVertex) + else: + # else make a new vertex + self.vertex[fromVertex] = [toVertex] + self.vertex[toVertex] = [] + + def topologicalSort(self): + visited = [False] * self.count # Marking all vertices as not visited + stack = [] # Stack for storing the vertex + for vertex in range(self.count): + # Call the recursive function only if not visited + if visited[vertex] == False: + self.topologicalSortRec(vertex, visited, stack) + + print(' '.join([str(i) for i in stack])) + # print(stack) + + # Recursive function for topological Sort + def topologicalSortRec(self, vertex, visited, stack): + + # Mark the current node in visited + visited[vertex] = True + + # mark all adjacent nodes of the current node + try: + for adjacentNode in self.vertex[vertex]: + if visited[adjacentNode] == False: + self.topologicalSortRec(adjacentNode, visited, stack) + except KeyError: + return + + # Push current vertex to stack which stores the result + stack.insert(0,vertex) + +if __name__ == '__main__': + g= Graph(6) + g.addEdge(5, 2) + g.addEdge(5, 0) + g.addEdge(4, 0) + g.addEdge(4, 1) + g.addEdge(2, 3) + g.addEdge(3, 1) + # g.printGraph() + g.topologicalSort() + + # OUTPUT: + # 5 4 2 3 1 0 diff --git a/Numpy/P69_ReverseWords.py b/Numpy/P69_ReverseWords.py new file mode 100644 index 0000000..8c69e0e --- /dev/null +++ b/Numpy/P69_ReverseWords.py @@ -0,0 +1,12 @@ +# Author: OMKAR PATHAK + +# Python program to reverse the words + +userInput = input() +userInput = userInput.split() + +print(' '.join(userInput[::-1])) + +# OUTPUT: +# Computer Science +# Science Computer diff --git a/Numpy/P70_SimpleProgressBar.py b/Numpy/P70_SimpleProgressBar.py new file mode 100644 index 0000000..ee03455 --- /dev/null +++ b/Numpy/P70_SimpleProgressBar.py @@ -0,0 +1,18 @@ +# This is the program for creating a simple progress bar. You may need this in many of your projects. +# You can install a module for progress bar by 'pip3 install progressbar2' + +import sys, time + +def progressBar(count, total, suffix=''): + barLength = 60 + filledLength = int(round(barLength * count / float(total))) + + percent = round(100.0 * count / float(total), 1) + bar = '=' * filledLength + '-' * (barLength - filledLength) + + sys.stdout.write('[%s] %s%s ...%s\r' % (bar, percent, '%', suffix)) + sys.stdout.flush() + +for i in range(10): + time.sleep(1) + progressBar(i, 10) diff --git a/Programs/P02_VariableScope.py b/Programs/P02_VariableScope.py index 505eb22..43c5f31 100644 --- a/Programs/P02_VariableScope.py +++ b/Programs/P02_VariableScope.py @@ -14,3 +14,21 @@ def test(): if __name__ == '__main__': test() print(x) #prints 'Global x' + + +#Author: OMKAR PATHAK +#This programs shows the rules for variable scope + +# LEGB Rule: Local, Enclosing, Global, Built-in + +x = 80 # Global x + +def test(): + #global x + y = 100 # Local y + x = 20 + print(x + y) #prints 'Local x' and 'Local y' + +if __name__ == '__main__': + test() + print(x) #prints 'Global x'