Parameters
Parameters
Concept: Function parameters
A function has two types of parameters: positional and keyword parameters. Positional parameters are required and must be passed in the correct order, while keyword parameters are optional and can be passed in any order by specifying the parameter name. Keyword parameters allow for more flexibility in function calls, as they can be omitted or specified in any order. Each keyword parameter must have a default value, which is used if the parameter is not specified in the function call.
def function_name(positional_param1, positional_param2, keyword_param1=None, keyword_param2=None):
"""
A function with 2 positional parameters and 2 keyword parameters.
"""
# code block
return output_valueThis function can be called in the following ways:
result1 = function_name(arg1, arg2)
result2 = function_name(arg1, arg2, keyword_param1=arg3)
result3 = function_name(arg1, arg2, keyword_param2=arg4)Note that the third line specifies a custom value for the keyword_param2 parameter, while the keyword_param1 parameter is omitted and will use its default value of None.
There is a fine line between a generalized function that can deal with a wide range of inputs and a function that is so generalized that it becomes difficult to maintain. One should therefore be careful when adding extra keyword parameters to a function. That said, this is mostly a problem for programmers who are writing libraries for other programmers to use, and is less of a concern for scripts that are written for a specific purpose.
We can add a keyword parameter to the IP validation functions to specify whether we consider loopback addresses to be valid or not.
To demonstrate keyword (optional) parameters, we'll expand the functionality of the IP validation functions to include optional parameters that allow or disallow loopback and private addresses.
Since almost every function in the script needs to know whether to allow or disallow loopback and private addresses, we'll add the keyword parameters to the is_valid_ip_address() function and pass them down to the is_valid_ipv4_address() and is_valid_ipv6_address() functions.
Because the example function calls the is_valid_ip_address() function, it will also need to be updated to accept the new keyword parameters and pass them down to the is_valid_ip_address() function.
def is_valid_ipv4_address(ip_address, allow_loopback=True, allow_private=True):
"""
Validates if a given string is a valid IPV4 address.
"""
if not type(ip_address) is str:
return False
octets = ip_address.split(".")
if not len(octets) == 4:
return False
if not allow_loopback and ip_address.startswith("127"):
return False
if not allow_private and (ip_address.startswith("10.") or ip_address.startswith("172.16.") or ip_address.startswith("192.168.")):
return False
for octet in octets:
if not octet.isnumeric() or int(octet) < 0 or int(octet) > 255:
return False
return True
def is_valid_ipv6_address(ip_address, allow_loopback=True, allow_private=True):
"""
Validates if a given string is a valid IPV6 address.
"""
if not type(ip_address) is str:
return False
if ip_address.count("::") > 1:
return False
# ::1 is the only loopback address, since this address can be written as 0000::1, 0000::0000:1 or one of a number
# of other ways, we simply remove 0 and : from the string. If it is a loopback, only the final 1 should remain.
if not allow_loopback and ip_address.replace('0', '').replace(':', '') == '1':
return False
if not allow_private and (ip_address.startswith('fc') or ip_address.startswith('fe')):
return False
# An IPV address may contain the '::' sequence 0 or 1 times.
ip_address = ip_address.replace("::", ":")
hexets = ip_address.split(":")
for hexet in hexets:
if len(hexet) > 4 or not is_hexadecimal(hexet):
return False
return True
def is_valid_ip_address(ip_address, allow_loopback=True, allow_private=True):
"""
Validates if a given string is a valid IP address (V4 or V6)
ip_address:
The ip address to validate.
allow_loopback:
A boolean indicating whether to allow loopback addresses, defaults to True.
allow_private:
A boolean indicating whether to allow private addresses, defaults to True.
"""
if is_valid_ipv4_address(ip_address, allow_loopback=allow_loopback, allow_private=allow_private):
return (True, "IPv4")
if is_valid_ipv6_address(ip_address, allow_loopback=allow_loopback, allow_private=allow_private):
return (True, "IPv6")
return (False, None)
def example(input, allow_loopback=True, allow_private=True):
result = is_valid_ip_address(input, allow_loopback=allow_loopback, allow_private=allow_private)
if result[0]:
print(f"{input} is a valid {result[1]} address")
else:
print(f"{input} is invalid")Now that all the functions have been updated to accept the new keyword parameters, we can call the example() function with new arguments to demonstrate both the new functionality and that the order (or presence) of the keyword parameters does not matter.
# Neither keyword parameter: both default to True
example("127.0.0.1")
example("192.168.1.25")
# Only the second one, skipping the first
example("192.168.1.25", allow_private=False)
# Only the first one
example("127.0.0.1", allow_loopback=False)
# Both, in the declared order...
example("10.0.0.1", allow_loopback=False, allow_private=False)
# ...and both in the opposite order: exactly the same result
example("10.0.0.1", allow_private=False, allow_loopback=False)Which will produce the following output:
127.0.0.1 is a valid IPv4 address
192.168.1.25 is a valid IPv4 address
192.168.1.25 is invalid
127.0.0.1 is invalid
10.0.0.1 is invalid
10.0.0.1 is invalid