While working on a Python script, I was going nuts on a very strange behaviour of a variable, which was supposed to be changed inside a function and then be used again later in the script. But for an unknown reason (at least at the begin), the variable changed back to the default value.
But let's start at the beginning - where the variable is defined.
At the beginning of the Python script, the verbose variable is declared and set to False:
#!/usr/bin/python3
# ---------------------------------------------------------------------
# Define variables and defaults
ignore_list=[]
verbose=False
Later in the script the getargs() function is responsible to handle script arguments from the user. This is also where the -v / --verbose flag is defined:
# ---------------------------------------------------------------------
# Arguments
def getargs() :
parser = argparse.ArgumentParser(description='Script help')
parser.add_argument('-i', '--ignore', dest='ignore_list', required=False, help='Set warning threshold')
parser.add_argument('-o', '--output', dest='output', choices=['nagios', 'prometheus'], required=False, help='Set output type to either nagios or prometheus (defaults to nagios)')
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', default=False, required=False, help='Enable verbose mode for debugging plugin')
args = parser.parse_args()
The verbose argument is using the action 'store_true', which will automatically set the variable to True if this argument is used.
A bit further down the script, but still inside the getargs() function, the temporary variable args.verbose is read and its value (True if used) is used to overwrite the "verbose" variable, declared at the beginning of the script:
if (args.verbose):
verbose = args.verbose
print(verbose)
And finally inside the main part of the script, getargs() is called and I verified the value of the "verbose" variable:
# ---------------------------------------------------------------------
# Main
getargs()
print(verbose)
But when I executed the script, I got two different outputs:
$ python3 script.py -v
True
False
The first output (True) comes from the first print(verbose) line inside the getargs() function.
The second output (False) comes from the print(verbose) line in the main part of the script.
So why would the variable fall back to the default after leaving the getargs() function? Yeah... I was scratching my head a lot, because in other programming languages this would have worked (at least the limited list of languages I know of).
The (somewhat explaining) answer was found on StackOverflow by user f.rodrigues:
That's because 'global x' is different than 'fun x', 'fun x' overlaps/masks the 'global x', does whatever you do with it and then it ceases to exist. The mask is not there so former 'global x' is again the current 'x'.
Let me rewrite this explanation - if I understood this correctly: The variable "verbose" is a global variable because it is defined inside the main part of the Python script. When the variable is overwritten inside a function (here getargs() function), this overwritten value remains inside the function and is not transported outside of the function.
To handle this, the function needs to be told that it is in fact working with a global variable - prior to setting any value to the variable.
Inside the getargs() function I therefore added the "global verbose" line:
if (args.verbose):
global verbose
verbose = args.verbose
print(verbose)
And after executing the Python script once more, both print outputs now show True:
$ python3 script.py -v
True
True
I received this helpful information from Claire M. on the BlueSky Social Media platform, appreciate the comment!
But why does Python behave this way? Why is a variable not simply set to a new value and retains the value across the whole script, including functions - as the data "flows" through the script?
The reason is not obvious to a Python beginner (or occasional Python script writer like me): There are mutable and immutable types of variables.
Mutable means the variable's value can be changed everywhere, across all functions, as the variable is handled during script execution. Mutable types are dict, list and set.
Immutable means the variable's value "reverts" to the global value once leaving a function. Or differently said: The global variable is actually never changed, only a "copied" variable inside the function has its value changed. By using the "global var" declaration inside the function (see above) the value of the "outer" global value is changed, too. Immutable types are int, float, str.
No comments yet.
AWS Android Ansible Apache Apple Atlassian BSD Backup Bash Bluecoat CMS Chef Cloud Coding Consul Containers CouchDB DB DNS Database Databases Docker ELK Elasticsearch Filebeat FreeBSD Galera Git GlusterFS Grafana Graphics HAProxy HTML Hacks Hardware Icinga Influx Internet Java KVM Kibana Kodi Kubernetes LVM LXC Linux Logstash Mac Macintosh Mail MariaDB Minio MongoDB Monitoring Multimedia MySQL NFS Nagios Network Nginx OSSEC OTRS Office OpenSearch PGSQL PHP Perl Personal PostgreSQL Postgres PowerDNS Proxmox Proxy Python Rancher Rant Redis Roundcube SSL Samba Seafile Security Shell SmartOS Solaris Surveillance Systemd TLS Tomcat Ubuntu Unix VMWare VMware Varnish Virtualization Windows Wireless Wordpress Wyse ZFS Zoneminder