Unit 4 Notes
Unit 4 Notes
• Pure Functions: These functions have two main properties. First, they always produce the same
output for the same arguments irrespective of anything else. Secondly, they have no side-effects
i.e. they do modify any argument or global variables or output something.
• Recursion: There are no “for” or “while” loop in functional languages. Iteration in functional
languages is implemented through recursion.
• Functions are First-Class and can be Higher-Order: First-class functions are treated as first-class
variable. The first-class variables can be passed to functions as a parameter, can be returned from
functions or stored in data structures.
• Variables are Immutable: In functional programming, we can’t modify a variable after it’s been
initialized. We can create new variables – but we can’t modify existing variables.
original_nums = [1, 2, 3, 4]
changed_nums = pure_func(original_nums)
print(orginal_nums)
print(changed_nums)
greet(shout)
greet(whisper)
Built-in Higher-order Functions: The following built-in higher-order functions are some of the
commonly used functions that return an iterator instead of a list or other data types for memory/space
efficiency reasons.
The map function:
This function gives us the ability to apply any function to every element on an object that is iterable (an
object over which we can iterate, or use a loop). For example, if we wanted to append a greeting to multiple
names in a list, we could do the following:
names = ['Bhavya', 'Abhijeet', 'Alkesh', 'Muskaan']
def add_greeting(name: str):
return "Hello " + name
names_and_greetings = map(add_greeting, names)
# this map function print will return an iterator
print(names_and_greetings)
# since it is an iterator, we can iterate over it and print the names separately
for the name in names_and_greetings:
print(name)
Output:
<map object at 0x7f87d27c9340>
Hello Bhavya
Hello Abhijeet
Hello Alkesh
Hello Muskaan
The filter Function
This function, as the name suggests, filters out some values from an iterable according to a specified
condition. The filter function returns either True or False.
Let's say we want to filter out the numbers that are even from a list of numbers, we can do it the following
way.
# a function that returns true for an even number given as input and false for an odd number
def is_even(num: int) -> bool:
return num % 2 == 0
nums = [1, 2, 3, 4, 5, 6, 7, 8]
# the filter function will extract values from the list that give 'True'
evens = filter(is_even, nums)
print(list(evens)) # since the filter will return an iterable object we print it as a type list
Output:
[2, 4, 6, 8]
You can even combine the two functions - map and filter and use them for expensive data manipulations!
Lambda Expressions OR Anonymous function: Lambda expressions are another concept of functional
programming. They are essentially anonymous functions. While creating functions in python, we usually
use the def keyword and give the function a unique name. However, lambda expressions allow us to skip
that process and write small functions much more quickly.
The syntax of a lambda expression is as follows:
lambda [arguments] : expression
For example, we can write a higher-order function that returns the nthsup> power of a number using a
lambda expression.
# creating a higher-order function that returns a lambda expression
def higher_order_power(n: int) -> int:
# this lambda expression returns the nth power of x
return lambda x: x ** n
power3 = higher_order_power(3)
print(power3(4)) # prints the cube of 4
Output:64
The higher-order function returned to us the 3rd power of 4 which is 64.
x = lambda a, b : a * b
print(x(5, 6))
def myfunc(n):
return lambda a : a * n
mydoubler = myfunc(2)
print(mydoubler(11))
Let's look at an example of a lambda function to see how it works. We'll compare it to a regular user-defined
function.
Assume I want to write a function that returns twice the number I pass it. We can define a user-defined
function as follows:
def f(x):
return x * 2
f(3)
>> 6
Now for a lambda function. We'll create it like this:
lambda x: x * 3
As I explained above, the lambda function does not have a return keyword. As a result, it will return the
result of the expression on its own. The x in it also serves as a placeholder for the value to be passed into
the expression. You can change it to whatever you want.
Now if you want to call a lambda function, you will use an approach known as immediately invoking the
function. That looks like this:
(lambda x : x * 2)(3)
>> 6
The reason for this is that since the lambda function does not have a name you can invoke (it's anonymous),
you need to enclose the entire statement when you want to call it.
When Should You Use a Lambda Function?
You should use the lambda function to create simple expressions. For example, expressions that do not
include complex structures such as if-else, for-loops, and so on.
So, for example, if you want to create a function with a for-loop, you should use a user-defined function.
reduce() function: Unlike map and filter functions, reduce() is not a built-in function, but is defined in
built-in functools module. It also needs two arguments, a function and an iterable. However it returns a
single value. The argument function is applied to two successive items in the list from left to right. Result
of the function in first call becomes first argument and third item in list becomes second. Cumulative result
is the return value of reduce() function.
In the example below, add() function is defined to return addition of two numbers. This function is used in
reduce() function along with a range of numbers between 0 to 100. Output is sum of first 100 numbers.
import functools
def add(x,y):
return x+y
num=functools.reduce(add, range(101))
print ('sum of first 100 numbers ',num)
Output:
sum of first 100 numbers 5050
We can use a lambda function instead of user-defined add() function for the same output.
num=functools.reduce(lambda x,y:x+y, range(101))
Lambda function can also be used as a filter. Following program use lambda function that filters all
vowels from given string.
string='''
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
'''
consonants=list(filter(lambda x: x not in ['a','e','i','o','u'],string))
print (''.join(consonants))
Lambda function as argument to map() function. The lambda function itself takes two arguments taken
from two lists and returns first number raised to second.The resulting mapped object is then parsed to output
list.
powersmap=map(lambda x,y: x**y, [10,20,30], [4,3,2])
print (list(powersmap))
• Predictability: It’s crucial to have predictable code since it makes testing and debugging easier.
When a function includes side effects, it can be more challenging to predict how it would act in
certain circumstances. Pure functions are essential to functional programming because they make
it simple to predict a function’s result given a specific input. Proving advantage to Developer.
• Modularity: Functional programming facilitates the simpler avoidance of errors brought on by
mutable states and side effects through the use of pure functions and immutability.
• Concurrency & Parallelism: Due to the simplicity of creating multi-threaded or multi-process
applications the functions can run concurrently without running the danger of colliding. Making no
issue for race situations and synchronization problems are not a concern because functions do not
depend on any external state. Additionally, Functional programming promotes the use of
immutable data structures and encourages writing pure functions, which makes it easier to write
parallel and concurrent code.
• Code optimization & Refactoring: Since functions have predictable outputs, the compiler or
interpreter can make assumptions about how the function will behave and optimize accordingly.
This makes functional programming a good choice for applications that need to be highly
performant.
• Readability, Reusability & Maintainability: Small, reusable functions can be used by developers to
build more complex programs thanks to functional programming. Additionally, it promotes the use
of functions and discourages the use of side effects, making the code easier to read and understand.
This reduces the complexity of the code and makes it simpler to maintain.
• Testability: With the use of pure functions, functional programming makes it easier to test code, as
it ensures that the output of a function only depends on its input.
• Avoidance of Bugs: By Using the above mention methods when combined helps to avoid bugs easy
for the developer to detect any bugs and resolve them.
Logic programming: Writing code in Python, C, C++, Java, etc. we have observed paradigms like object-
oriented programming (OOPS), abstraction, looping constructs and numerous other states of programming.
Logic Programming is just another programming paradigm that works on relationships. These relationships
are built using facts and rules and are stored as a database of relations. It is a programming methodology
that works on formal and explicit logic of events.
Relation:Relations are the basis of logic programming. A relation can be defined as a fact that follows a
certain rule. For example, a relation given by [ A -> B ] is read as “if A is true, then B occurs”. In language
terms, this can be read as, “If you are an Engineer, then you are a Graduate” and infers that, “Engineers are
Graduates”. In programming languages, the semantics of writing relations changes based on the language’s
syntax, but this is the overall rationality behind what relations mean.
Facts: Every program that is built on logic needs facts. To achieve a defined goal, facts need to be provided
to the program. As the name suggests in general English, facts are merely the truth. True statements that
represent the program and the data. For instance, Washington is the capital of the USA.
Rules: Rules, like programming syntax are the constraints that help in drawing conclusions from a domain.
These are logical clauses that the program or the fact needs to follow to build a relation.
2. It is also used in prototyping models. Since expressions and patterns can be replicated using logic,
prototyping is made easy.
3. Pattern matching algorithms within image processing, speech recognition and various other
cognitive services also use logic programming for pattern recognition.
4. Scheduling and Resource Allocation are major operations tasks that logic programming can help
solve efficiently and completely.
How to Solve Problems with Logic Programming : Logic Programming uses facts and rules for solving
the problem. That is why they are called the building blocks of Logic Programming. A goal needs to be
specified for every program in logic programming. To understand how a problem can be solved in logic
programming, we need to know about the building blocks − Facts and Rules –
Facts: Actually, every logic program needs facts to work with so that it can achieve the given goal. Facts
basically are true statements about the program and data. For example, Delhi is the capital of India.
Rules: Actually, rules are the constraints which allow us to make conclusions about the problem domain.
Rules basically written as logical clauses to express various facts. For example, if we are building any game
then all the rules must be defined.
Rules are very important to solve any problem in Logic Programming. Rules are basically logical
conclusion which can express the facts. Following is the syntax of rule −
A∶− B1,B2,...,Bn.
Here, A is the head and B1, B2, ... Bn is the body.
For example − ancestor(X,Y) :- father(X,Y).
ancestor(X,Z) :- father(X,Y), ancestor(Y,Z).
This can be read as, for every X and Y, if X is the father of Y and Y is an ancestor of Z, X is the ancestor
of Z. For every X and Y, X is the ancestor of Z, if X is the father of Y and Y is an ancestor of Z.
Kanren: Kanren is a library within PyPi that simplifies ways of making business logic out of code. The
logic, rules, and facts we discussed previously can be turned into code using ‘kanren’. It uses advanced
forms of pattern matching to understand the input expressions and build its own logic from the given input.
Examples: kanren enables the expression of relations and the search for values which satisfy them. The
following code is the "Hello, world!" of logic programming. It asks for `1` number, `x`, such that `x == 5`
>>> from kanren import run, eq, membero, var, conde
>>> x = var()
>>> run(1, x, eq(x, 5))
(5,)
Multiple variables and multiple goals can be used simultaneously. The following code asks for a number
x such that `x == z` and `z == 3`
>>> z = var()
>>> run(1, x, eq(x, z),
eq(z, 3))
(3,)
kanren uses [unification], an advanced form of pattern matching, to match within expression trees.
The following code asks for a number, x, such that `(1, 2) == (1, x)` holds.
>>> run(1, x, eq((1, 2), (1, x)))
(2,)
The above examples use `eq`, a *goal constructor* to state that two expressions are equal. Other goal
constructors exist such as `membero(item, coll)` which states that `item` is a member of `coll`, a
collection.
The following example uses `membero` twice to ask for 2 values of x, such that x is a member of `(1, 2,
3)` and that x is a member of `(2, 3, 4)`.
>>> run(2, x, membero(x, (1, 2, 3)), # x is a member of (1, 2, 3)
membero(x, (2, 3, 4))) # x is a member of (2, 3, 4)
(2, 3)
### Logic Variables
As in the above examples, `z = var()` creates a logic variable. You may also, optionally, pass a token
name for a variable to aid in debugging:
>>> z = var('test')
>>> z
~test
Lastly, you may also use `vars()` with an integer parameter to create multiple logic variables at once:
>>> a, b, c = vars(3)
>>> a
~_1
>>> b
~_2
>>> c
~_3
We can use intermediate variables for more complex queries. Who is Bart's grandfather?
>>> y = var()
>>> run(1, x, parent(x, y),
parent(y, 'Bart'))
('Abe',)
We can express the grandfather relationship separately. In this example we use `conde`, a goal constructor
for logical *and* and *or*.
>>> def grandparent(x, z):
... y = var()
... return conde((parent(x, y), parent(y, z)))
>>> run(1, x, grandparent(x, 'Bart'))
('Abe,')
QUESTION:
• Define a relation food and program these facts: avocado, carrot, tomato and broccoli are food.
• Define a relation color and program these facts: carrot is orange, avocado is green, broccoli is
green and tomato is red.
• Define a relation likes and program these facts: Jeff likes carrot, avocado and baseball, Bill likes
avocado and baseball, Steve likes tomato and baseball, Mary likes broccoli and Peter likes
baseball.
food = Relation()
color = Relation()
likes = Relation()
fact(food, "avocado")
fact(food, "carrot")
fact(food, "tomato")
fact(food, "broccoli")
PyDatalog: pyDatalog adds the logic programming paradigm to Python's toolbox, in a pythonic way. You
can now run logic queries on databases or Python objects, and use logic clauses to define python classes.
In particular, pyDatalog can be used as a query language:
▪ it can perform multi-database queries (from memory datastore, 11 relational databases, and noSQL
database with appropriate connectors)
▪ it is more expressive than SQL, with a cleaner syntax;
▪ it facilitates re-use of SQL code snippet (e.g. for frequent joins or formula);
Datalog = SQL + recursivity
Datalog is a truly declarative language derived from Prolog, with strong academic foundations. It
complements Python very well for:
▪ managing complex sets of related information (e.g. in data integration or the semantic web).
▪ simulating intelligent behavior (e.g. in games),
▪ performing recursive algorithms (e.g. in network protocol, code and graph analysis, parsing)
▪ solving discrete constraint problems.
▪
def square(n):
return n*n
pyDatalog module deals with data using its terms. That means, we need to create pyDatalog terms using
create_terms() method of pyDatalog class in pyDatalog module. This method globally declares the datalog
constants, variables, and unprefixed predicates. Then, based on several operators, we can query and filter
the results based on the specified conditions. Of course, the relational operators have been overridden (i.e.,
modified) to work as desired with pyDatalog variables.
Also, you may note that input_values has been reversed, as in_() method loop parses values in reverse
order as mentioned, so this pre-reversal will maintain the ascending order of output.
Variables and expressions: The next step is to declare the variables we'll use. They must start with an
upper-case letter:
pyDatalog.create_terms('X,Y')
Variables appear in logic queries, which return a printable result
# give me all the X so that X is 1
print(X==1)
X
-
1
Queries can contain several variables and several criteria ('&' is read 'and'):
# give me all the X and Y so that X is True and Y is False
print((X==True) & (Y==False))
X|Y
-----|------
True | False
Note the parenthesis around each equality: they are required to avoid confusion with (X==(True &
Y)==False).
Some queries return an empty result :
# give me all the X that are both True and False
print((X==True) & (X==False))
[]
Besides numbers and booleans, variables can represent strings. Furthermore, queries can contain python
expressions:
# give me all the X and Y so that X is a name and Y is 'Hello ' followed by the first letter of X
print((X==raw_input('Please enter your name : ')) & (Y=='Hello ' + X[0]))
Please enter your name : World
X|Y
------|--------
World | Hello W
In the second equality, X is said to be bound by the first equality, i.e. the first equality gives it a value,
making it possible to evaluate the expression in the second equality.
pyDatalog has no symbolic resolver (yet) ! If a variable in an expression is not bound, the query returns
an empty solution :
# give me all the X and Y so that Y is 1 and Y is X+1
print((Y==1) & (Y==X+1))
[]
To use your own functions in logic expressions, define them in Python, then ask pyDatalog to create
logical terms for them:
def twice(a):
return a+a
pyDatalog.create_terms('twice')
print((X==1) & (Y==twice(X)))
X|Y
--|--
1|2
Note that X must be bound before calling the function.
Similarly, pyDatalog variables can be passed to functions in the Python standard library:
# give me all the X and Y so that X is 2 and Y is the square root of X
import math
pyDatalog.create_terms('math')
print(X==2) & (Y==math.sqrt(X))
X|Y
--|--------------
2 | 1.41421356237
Loops: Let's first declare the Variables we'll need:
A loop can be created by using the .in() method (we'll see that there are other ways to create loops later):
from pyDatalog import pyDatalog
pyDatalog.create_terms('X,Y,Z')
# give me all the X so that X is in the range 0..4
print(X.in_((0,1,2,3,4)))
X
-
0
1
3
2
4
Here is the procedural equivalent
for x in range(5):
print x
0
1
2
3
4
Parallel Programming: The more complex a program gets the more often it is handy to divide it into
smaller pieces. This does not refer to source code, only, but also to code that is executed on your machine.
One solution for this is the usage of subprocesses in combination with parallel execution. Thoughts behind
this are:
• A single process covers a piece of code that can be run separately
• Certain sections of code can be run simultaneously, and allow parallelization in principle
• Using the features of modern processors, and operating systems, for example every core of a
processor we have available to reduce the total execution time of a program
• To reduce the complexity of your program/code, and outsource pieces of work to specialized agents
acting as subprocesses
Using sub-processes requires you to rethink the way your program is executed, from linear to parallel. It is
similar to changing your work perspective in a company from an ordinary worker to a manager - you will
have to keep an eye on who is doing what, how long does a single step take, and what are the dependencies
between the intermediate results.
A possible use case is a main process, and a daemon running in the background (master/slave) waiting to
be activated. Also, this can be a main process that starts worker processes running on demand. In practice,
the main process is a feeder process that controls two or more agents that are fed portions of the data, and
do calculations on the given portion.
Keep in mind that parallelization is both costly, and time-consuming due to the overhead of the
subprocesses that is needed by your operating system. Compared to running two or more tasks in a linear
way, doing this in parallel you may save between 25 and 30 percent of time per subprocess, depending on
your use-case. For example, two tasks that consume 5 seconds each need 10 seconds in total if executed in
series, and may need about 8 seconds on average on a multi-core machine when parallelized. 3 of those 8
seconds may be lost to overhead, limiting your speed improvements.
Using Process
The Process class in multiprocessing allocates all the tasks in the memory in one go. Every task created
using the Process class has to have a separate memory allocated.
Imagine a scenario wherein ten parallel processes are to be created where every process has to be a separate
system process.
Here’s an example (order of the output is non-deterministic):
The Process class initiated a process for numbers ranging from 0 to 10. target specifies the function to be
called, and args determines the argument(s) to be passed. start() method commences the process. All the
processes have been looped over to wait until every process execution is complete, which is detected using
the join() method. join() helps in making sure that the rest of the program runs only after the
multiprocessing is complete.
sleep() method helps in understanding how concurrent the processes are!
# output:
# 1 2 3 4 5 10 11 12 13 14
In this program, two separate processes run the numbers function at the same time with different parameters.
First, we create two Process objects and assign them the function they will execute when they start running,
also known as the target function. Second, we tell the processes to go ahead and run their tasks. And third,
we wait for the processes to finish running, then continue with our program.
While the output looks the same as if we ran the numbers function twice sequentially, we know that the
numbers in the output were printed by independent processes.
• better usage of the CPU when dealing with high CPU-intensive tasks
• more control over a child compared with threads
• easy to code
The first advantage is related to performance. Since multiprocessing creates new processes, you can make
much better use of the computational power of your CPU by dividing your tasks among the other cores.
Most processors are multi-core processors nowadays, and if you optimize your code you can save time by
solving calculations in parallel.
The second advantage looks at an alternative to multiprocessing, which is multithreading. Threads are not
processes though, and this has its consequences. If you create a thread, it’s dangerous to kill it or even
interrupt it as you would do with a normal process. Since the comparison between multiprocessing and
multithreading isn’t in the scope of this article, I encourage you to do some further reading on it.
The third advantage of multiprocessing is that it’s quite easy to implement, given that the task you’re trying
to handle is suited for parallel programming.
1. Same task are performed on different 1. Different task are performed on the same or different
subsets of same data. data.
3. As there is only one execution thread 3. As each processor will execute a different thread or
operating on all sets of data, so the speedup process on the same or different set of data, so speedup is
is more. less.
5. It is designed for optimum load balance on 5. Here, load balancing depends upon on the e availability
multiprocessor system. of the hardware and scheduling algorithms like static and
dynamic scheduling.
Network programming: Python plays an essential role in network programming. The standard library of
Python has full support for network protocols, encoding, and decoding of data and other networking
concepts, and it is simpler to write network programs in Python than that of C++.
There are two levels of network service access in Python. These are:
• Low-Level Access
• High-Level Access
In the first case, programmers can use and access the basic socket support for the operating system using
Python's libraries, and programmers can implement both connection-less and connection-oriented protocols
for programming.
Application-level network protocols can also be accessed using high-level access provided by Python
libraries. These protocols are HTTP, FTP, etc
There are several reasons for using Python for this course. The simplicity of python makes it the most
powerful language. Python is syntactically simplest to implement amongst it's counterparts.
Also, you can do almost everything with python. 'Ohh.. Almost everything, Can we make a website using
python? Can we make face detection application using python? can we make our own personal assistant
using python? Can we make a penetration testing tool using python?
The answer to all of the above questions is a big YES!
The third party libraries support provided by python makes it limitless. There is a proper documentation for
the third party libraries as well, hence using them in your application becomes easier.
Lastly, python is powerful enough to make websites like Quora and provide the backbone for the Google
search engine, so yes, python is the perfect choice for network programming.
Sockets:
To understand what sockets are, let's start with the Internet Connection. The Internet Connection basically
connects two points across the internet for data sharing and other stuff. One process from computer C1 can
communicate to a process from computer C2, over an internet connection. It has following properties:
• Reliable: It means until the cables connecting two computers are safe, data will be transferred
safely.
• Point-to-Point: Connection is established between 2 points.
• Full-Duplex: It means transfer of information can occur in both ways i.e. from client to server as
well as server to client simultaneously(at the same time).
Sockets are the endpoints of a bidirectional, point-to-point communication channel. Given an internet
connection, say between client(a browser) and the serverwe will have two sockets. A Client Socket and
a Server Socket.
Socket acts on two parts: IP Address + Port Number
For now, just focus on Connect and Bind methods.
Connect is used by the client socket to start a connection with the server. This request is fulfilled
by bind method of the server socket. If you are having problem with the code, don't worry. Every bit of it
will be explained separately with examples.
Before concluding, let's see some differences between the client and the server sockets:
• Unlike client sockets, server sockets are not short lived. For example: you might need youtube.com
for a single request but youtube.com has to be up 24*7 for any request which it might receive from
users across the globe.
• Unlike client socket which uses Ephermal Port for connection, server socket requires a standard or
well defined port for connection like: Port 80 for Normal HTTP Connection, Port 23 for Telnet etc.
There are two type of sockets: SOCK_STREAM and SOCK_DGRAM. Below we have a comparison of
both types of sockets.
SOCK_STREAM SOCK_DGRAM
Socket Module in Python :To create a socket, we must use socket.socket() function available in the Python
socket module, which has the general syntax as follows:
S = socket.socket(socket_family, socket_type, protocol=0)
• socket_family: This is either AF_UNIX or AF_INET. We are only going to talk about INET
sockets in this tutorial, as they account for at least 99% of the sockets in use.
• bind( ):This method binds the socket to an address. The format of address depends on socket
family mentioned above(AF_INET).
• listen(backlog):This method listens for the connection made to the socket. The backlog is the
maximum number of queued connections that must be listened before rejecting the connection.
• accept( ): This method is used to accept a connection. The socket must be bound to an address
and listening for connections. The return value is a pair(conn, address) where conn is a new
socket object which can be used to send and receive data on that connection, and address is the
address bound to the socket on the other end of the connection.
• gethostname() This method returns a string containing the hostname of the machine where the
python interpreter is currently executing. For example: localhost.
• gethostbyname() If you want to know the current machine's IP address, you may
use gethostbyname(gethostname())
#!/usr/bin/python
#This is tcp_server.py script
import socket #line 1: Import socket module
s = socket.socket() #line 2: create a socket object
host = socket.gethostname() #line 3: Get current machine name
port = 9999 #line 4: Get port number for connection
s.bind((host,port)) #line 5: bind with the address
print "Waiting for connection..."
s.listen(5) #line 6: listen for connections
while True:
conn,addr = s.accept() #line 7: connect and accept from client
print 'Got Connection from', addr
conn.send('Server Saying Hi')
conn.close() #line 8: Close the connection
This script will do nothing as of now. It waits for a client to connect at the port specified. If we run this
script now, without having a client, it will wait for the connection,
Similarly, every website you visit has a server on which it is hosted, which is always waiting for clients to
connect. Now let's create a client.py program and try to connect with our server.py.
• Unreliable: When a UDP message is sent, there is no way to know if it will reach its destination or
not; it could get lost along the way. In UDP, there is no concept of acknowledgment, retransmission,
or timeout (as in TCP).
• Not ordered: If two messages are sent to the same recipient, the order in which they arrive cannot
be predicted.
• Lightweight: There is no ordering of messages, no tracking connections, etc. Hence UDP messages
are used when the rate of data transmission required is more and relibility is not important.
• Datagrams: Packets are sent individually and are checked for integrity only if they arrive.
SERVER PROGRAM:
#!usr/bin/python
import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # For UDP
udp_host = socket.gethostname() # Host IP
udp_port = 12345 # specified port to connect
#print type(sock) ============> 'type' can be used to see type
# of any variable ('sock' here)
sock.bind((udp_host,udp_port))
while True:
print "Waiting for client..."
data,addr = sock.recvfrom(1024) #receive data from client
print "Received Messages:",data," from",addr
CLIENT PROGRAM:
#!usr/bin/python
import socket
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # For UDP
udp_host = socket.gethostname() # Host IP
udp_port = 12345 # specified port to connect
msg = "Hello Python!"
print "UDP target IP:", udp_host
print "UDP target Port:", udp_port
sock.sendto(msg,(udp_host,udp_port)) # Sending message to UDP server