Basic explanation of python functions

what is the purpose of having the arg1, arg2 in the parenthesis next to it?

arg1 and arg2 are the names of the inputs and from there the function can use those inputs. In your case what your function does is to print them. Other functions can do other things with these arguments. But let's go step by step.

def print_two_again(arg1, arg2):
    print "arg1: %r, arg2: %r" % (arg1, arg2) 

In the first two lines starting with def, you define a function. It does something. In your case prints the two arguments it takes.

print_two_again("Steve","Testing")

On the third line what you actually do is to call that function. When you call that function you tell the function to pass "Steve" and "Testing" arguments to the function definition.

Above line is literally called a function call. Let's say you have a program and you want it to print two words. You need to define how you want it to be done. This is called function definition, where you define how things work. That's OK, but not enough. You would want to make it happen. So what you do is to execute that function. This is called a function call.

print_two_again("First","Call")
print_two_again("Second","Call")

In the above lines, what we did is to call the previously defined function two times but with different arguments.

Now let's look at the second line, which probably confuses you.

print "arg1: %r, arg2: %r" % (arg1, arg2) 

print is a built-in function in Python. What above line does here is to pass arg1 and arg2 arguments and print them with the format of "arg1: %r, arg2: %r"


what is the purpose of having the arg1, arg2 in the parenthesis next to it?

In this case, arg1 and arg2 are called arguments. Arguments allow functions to receive inputs it's expected to use in order to perform a task. The inputs are provided by the callers.

For example, in school math, you may've already seen things like z = f(x, y) where a function named f is defined as f(x, y) = x + y. This is the same concept in a programming language.

It also allows you do write more generic, flexible, and reusable code. For example, you don't have to write many different versions of a function to accomplish the same task with slightly different results, avoiding situations like add2(x, y) = x + y and add3(x, y, z) = x + y + z, and so on. You can simply do something like:

def sum(values):  # values is of type 'list'
    result = 0
    for value in values:
        result += value
    return result

And call it like this:

total = sum([1, 2, 3, 4, 5, 6, 7]) # a list of any length with numbers

Or like this:

total = sum([1, 2])

How many arguments a function needs will depend on what it needs to do and other factors.

Update

What confuses me is the print_two_again("Steve","testing") , what is this called and its purpose?

The line print_two_again("Steve","testing") is an invocation of the function (i.e. a function call). This causes the program to 'jump' into the body of the function named print_two_again and start executing the code in it.

The ("Steve","testing") part are the arguments being sent to the function as inputs. These are positional arguments, which basically means that they get "mapped" to the names arg1 and arg2 based on the order in which you've provided them when invoking the function.

For example, consider the function f(x, y) = x - y. If this function is called as z = f(3, 4) then the argument by the name of x will receive the value 3 and y will be 4, to return -1. If you reverse the arguments in the call, then you'd have x=4 and y=3 and it'd return 1 instead. The same is true of the arguments in the function you've provided.

This means that the order of the arguments in a function call is important.

The Python language, like many others, already has a set of built-in functionality. The function named print is an example of this. You can get a lot of information using the pydoc command (pydoc3 if you use Python3, which I'd recommend). For example, the command pydoc3 print produces the following documentation:

Help on built-in function print in module builtins:

print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)

Prints the values to a stream, or to sys.stdout by default.
Optional keyword arguments:
file:  a file-like object (stream); defaults to the current sys.stdout.
sep:   string inserted between values, default a space.
end:   string appended after the last value, default a newline.
flush: whether to forcibly flush the stream.

Note that this is documentation for Python3. Python2 documentation will be slightly different.

There's a direct correlation between your understanding of functions, as seen in your math courses in school, and functions as seen in a programming language. This is because math is part of the underlying foundation of computer science and programming languages, among others (e.g. analysis of algorithms).