Just like most other programming languages, Vim supports functions:
function AnimalGreeting(animal)
echo a:animal . ' says hello!'
endfunction
You can try calling the function and you'll get the following output:
:call AnimalGreeting('cat')
cat says hello!
You can see that function arguments are accessed via the a: scope.
Functions, of course, can return values:
function! AnimalGreeting(animal)
return a:animal . ' says hello!'
endfunction
Now, echo the return value of the function so we can see it:
:echo AnimalGreeting('dog')
dog says hello!
Vim also supports a variable number of arguments via the ... notation (an equivalent to Python's *args):
function! AnimalGreeting(...)
echo a:1 . ' says hi to ' . a:2
endfunction
Invoking this with the arguments 'cat' and 'dog' will produce the following result:
:call AnimalGreeting('cat', 'dog')
cat says hi to dog
You can access a list of all of the arguments with a:000:
function ListArgs(...)
echo a:000
endfunction
Try invoking it with a few arguments:
:call ListArgs('cat', 'dog', 'parrot')
['cat', 'dog', 'parrot']
You can also combine variable arguments with explicitly specified arguments, just like you would in Python:
function! AnimalGreeting(animal, ...)
echo a:animal . ' says hi to ' . a:1
endfunction
And here's the output:
:call AnimalGreeting('cat', 'dog')
cat says hi to dog
You can (and should) use local scope for your functions to keep them inaccessible from outside the file they are defined in:
function! s:AnimalGreeting(animal)
echo a:animal . 'says hi!'
endfunction
function! s:AnimalGreeting(animal)
return a:animal . ' says hello!'
endfunction
This will make sure that s:AnimalGreeting overwrites itself when sourcing the file again. But be careful not to overwrite somebody else's function.