Python Forum
Variables being overridden to initial values.
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Variables being overridden to initial values.
#1
Hello first post... as a Python user.

A long time PHP coder, trying to make the move over to Django, so I figured I would try my hand at a backup script to get my feet wet with Python. I have played with Python here and there, but this is my first serious attempt at an actual script that I plan to use on a daily bases.

Here my script:
#!/usr/bin/python3*

'''
	Module name:
	Author: 
	Contact: 
	Desc: 
'''
__version__ = '0.0.1'

import shutil
import os, os.path
import datetime



TODAYS_DATE = str(datetime.date.today())

NUMBER_OF_BACKUP = 3

BACKUP_LOCATION = '/home/Superusr/Backup_Scripts/Test_01/Save_Location/'
FOLDER2BACKUP = '/home/Superusr/Backup_Scripts/Test_01/Sample_Folder'

CURRENT_BACKUP_NAME = FOLDER2BACKUP + "_" + TODAYS_DATE

CURRENT_LIST_OF_BACKUPS = []
MOST_RECENT_BACKUP = "Newest"
OLDEST_BACKUP = "Oldest"


def GET_MOST_RECENT_BACKUP_NAME():
	print("Step 2...")
	for names in os.listdir(BACKUP_LOCATION):
		CURRENT_LIST_OF_BACKUPS.append(names)
	
	CURRENT_LIST_OF_BACKUPS.sort()	
	
def SET_MOST_RECENT_BACKUP():
	print("Step 3...")
	MOST_RECENT_BACKUP = CURRENT_LIST_OF_BACKUPS[-1]
	print(MOST_RECENT_BACKUP)
	
def SET_OLDEST_BACKUP():
	print("Step 4...")
	OLDEST_BACKUP = CURRENT_LIST_OF_BACKUPS[0]
	print(OLDEST_BACKUP)



def INITIAL_SETUP():
	print("Step 1...")
	GET_MOST_RECENT_BACKUP_NAME()
	SET_MOST_RECENT_BACKUP()
	SET_OLDEST_BACKUP()

def main():
	INITIAL_SETUP()
	print("Most recent backup is: {}".format(MOST_RECENT_BACKUP))

if __name__ == "__main__": main()
With a result of:
Output:
Step 1... Step 2... Step 3... Sample_Folder_2020-09-15 Step 4... Sample_Folder_2020-09-01 Most recent backup is: Newest ------------------ (program exited with code: 0) Press return to continue
So yes, this is a basic script, I am building it up, but that is the puzzling part. I put the print statements ("print('Step 1...')" in to make sure each function was being called to troubleshoot.
Originally, without the print statements, and the initial variables set to blank ("MOST_RECENT_BACKUP = ''") it was coming back blank with no errors. So after setting an initial value to the variables, and comparing that to the results after each step I realised my variables were being reset to initial setting even though they were defined out of scope. I was thinking it possibly could be because a "global variables" problem vs. a local variable, but the "CURRENT_LIST_OF_BACKUPS" variable is outside, and is initialised and is maintained through the parsing of the script, so I don't know why "MOST_RECENT_BACKUP" and "OLDEST_BACKUP" are not maintaining their new values and are returning to their initial value instead.

I am sure it is a simple oversight on my part, but I honestly don't see it. Wall

Any help would be appreciated, thanks in advance.


OS: Ubuntu 20.04
Python Version: Python 3.8.2
Reply
#2
because they have local scope inside the function. you need to declare them as global, using global keyword if you want to change the global name.
Note, if you don't assign to a name inside the function, it will use the global one, without need to declare it global
e.g.
def SET_MOST_RECENT_BACKUP():
    print("Step 3...")
    global MOST_RECENT_BACKUP
    MOST_RECENT_BACKUP = CURRENT_LIST_OF_BACKUPS[-1]
    print(MOST_RECENT_BACKUP)
CURRENT_LIST_OF_BACKUPS is a list and lists are mutable, that's why it "works" for it.

Note that using globals is generally considered bad, pass arguments to functions and return from functions.
Also, using ALL_CAPS for function and names is not consistent with PEP8 recommendations.
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#3
Recommended python style is to reserve UPPERCASE names for constants. CURRENT_BACKUP_NAME and GET_MOST_RECENT_BACKUP_NAME() aren't constants.

By default variables assigned in a function are local to that function. Unless you mark the variable as global, you're creating a new variable, setting it, then throwing it away when the function exits. The global variable isn't touched.

$ cat scopeit.py
name = "outside"  # this is a global variable

def change_name():
    name = "inside"  # this is a local variable
    print(f"my name in the function is {name}")

change_name()
print(f"my name outside the function is {name}")
Output:
my name in the function is inside my name outside the function is outside
Reply
#4
To clarify my point
spam = 'some spam'
eggs = 'some eggs'
foo = 'some foo'

def func():
    global eggs
    eggs = 'new eggs'
    spam = 'new spam'
    print(f'inside func, spam:{spam}, eggs:{eggs}, foo:{foo}')

print(f'before calling func, spam:{spam}, eggs:{eggs}, foo:{foo}')
func()
print(f'after calling func, spam:{spam}, eggs:{eggs}, foo:{foo}')
Output:
before calling func, spam:some spam, eggs:some eggs, foo:some foo inside func, spam:new spam, eggs:new eggs, foo:some foo after calling func, spam:some spam, eggs:new eggs, foo:some foo
If you can't explain it to a six year old, you don't understand it yourself, Albert Einstein
How to Ask Questions The Smart Way: link and another link
Create MCV example
Debug small programs

Reply
#5
Ok so I get all that, and changed my script, and yes it works. but I don't like it. BUT is works.

#!/usr/bin/python3*

'''
	Module name:
	Author:
	Contact: 
	Desc:
'''
__version__ = '0.0.1'

import shutil
import os, os.path
import datetime

TODAYS_DATE = str(datetime.date.today())
 
NUMBER_OF_BACKUP = 3
 
BACKUP_LOCATION = '/home/Superusr/Backup_Scripts/Test_01/Save_Location/'
FOLDER2BACKUP = '/home/Superusr/Backup_Scripts/Test_01/Sample_Folder'
 
CURRENT_BACKUP_NAME = FOLDER2BACKUP + "_" + TODAYS_DATE

CURRENT_LIST_OF_BACKUPS = []


def GET_MOST_RECENT_BACKUP_NAME():
	print("Step 2...")
	for names in os.listdir(BACKUP_LOCATION):
		CURRENT_LIST_OF_BACKUPS.append(names)
	
	CURRENT_LIST_OF_BACKUPS.sort()	
	
def SET_MOST_RECENT_BACKUP():
	print("Step 3...")
	RESULT = CURRENT_LIST_OF_BACKUPS[-1]
	return RESULT
		
def SET_OLDEST_BACKUP():
	print("Step 4...")
	RESULT = CURRENT_LIST_OF_BACKUPS[0]
	return  RESULT

def INITIAL_SETUP():
	print("Step 1...")
	GET_MOST_RECENT_BACKUP_NAME()
	
def main():
	INITIAL_SETUP()
	
	MOST_RECENT_BACKUP = SET_MOST_RECENT_BACKUP()
	OLDEST_BACKUP = SET_OLDEST_BACKUP()
	
	print("Most recent backup is: {}".format(MOST_RECENT_BACKUP))
		

if __name__ == "__main__": main()
I don't like it because it doesn't future proof the code, I know what I want to do next and I will have to switch it up.

Like, just moving "MOST_RECENT_BACKUP = SET_MOST_RECENT_BACKUP()" and "OLDEST_BACKUP = SET_OLDEST_BACKUP()"
out of the main() but after INITIAL_SETUP() function breaks it.

I guess it is a classic case of trying to run before you can walk.

Thanks again for your help.

After reviewing it some more, I understand why is breaks when the variable are breaking outside the main() function, because the intial_setup() function only get called then. Therefore not set before then.
Reply
#6
If you don't like global variables how about using a class? Then the global variables become instance variables. This also forces you to do initial setup because that code can be placed in a special method named __init__ that runs immediately after an instance is created.
import os

class Backups:
    def __init__(self, backup_location=None):
        '''Called when a new instance is created'''
        if backup_location is None:
            backup_location = os.getcwd()
        self._backup_location = backup_location
        self._index = 0
        self._backups = []
        self.update()

    def update(self):
        self._backups = os.listdir(self._backup_location)
        self._backups.sort()

    def __getitem__(self, index):
        '''Used for list type indexing.  instance[3]'''
        return self._backups[index]

    def __iter__(self):
        '''With __next__ makes this class iterable.  for item in instance:'''
        self._index = 0
        return self

    def __next__(self):
        '''With __iter__ makes this class iterable.  for item in instance:'''
        if (i := self._index) < len(self._backups):
            self._index += 1
            return self._backups[i]
        raise StopIteration

    def newest(self):
        return self._backups[-1]

    def oldest(self):
        return self._backups[0]

    def __len__(self):
        '''Return number of backups for len(instance).'''
        return len(self._backups)


if __name__ == "__main__":
    backups = Backups())
    print(f'Newest backup is: {backups.newest()}')
    print(f'Oldest backup is: {backups.oldest()}')
    for backup in backups:
        print(backup)
    print(f'There are {len(backups)} backups')
    if len(backups) > 2:
        print(f'Backup[2] = {backups[2]}')
Output:
Newest backup is: sticks.py Oldest backup is: __pycache__ __pycache__ allwidgets.py class_instance_var_test.py console.py interactiveconsole.py junk.py monkeypatching.py pythonhighlighter.py snail.py sticks.py There are 10 backups Backup[2] = class_instance_var_test.py
I don't understand what it is you are trying to do, but hopefully this gives you a peek into the kinds of things you can do with Python. And I am a noob. I'm sure this could be done much better.
Reply
#7
@deanhystad 

I saw your post before before all the turmoil from earlier in the week. It is funny, I was always intending to develop the script into a more robust solution, which of course would include OOP ("class") in someway. If you look at my original code, you would see classic OOP structuring with "get 'ers and set 'ers". I guess after so many year is just instinct.

Anyways, after your comment I looked at python classes, and structuring my original code in a class format, it work almost from the beginning, but also was more inline with what I was expecting.

#!/usr/bin/python3

'''
    Module name:
    Author: p2bc
    Author: p2bc
    Contact:
    Desc: Backup script that can be automatic/manual with predefined or dynamic locations
'''
__version__ = '0.0.2'

import shutil
import os, os.path
import datetime


class BackUp:
    
    number_of_backups = 3
    backup_location = '/home/superusr/Backup_Scripts/Test_02/Save_Location/'
    folder_to_backup = '/home/superusr/Backup_Scripts/Test_02/Sample_Folder' 
    current_list_of_backups = []
    oldest_backup = ""
    newest_backup = ""
    
    def __init__(self, today_date = "NULL", number_backups = "NULL", backup_location = "NULL", folder_backup = "NULL", name_backup = "NULL"):
        self.todays_date = str(datetime.date.today()) if  today_date == "NULL" else today_date 
        self.number_of_backups = BackUp.number_of_backups if number_backups == "NULL" else number_backups
        self.backup_location = BackUp.backup_location if backup_location == "NULL" else backup_location
        self.folder_to_backup = BackUp.folder_to_backup if folder_backup == "NULL" else folder_backup
        self.current_backup_name = self.folder_to_backup + "_" + self.todays_date if name_backup == "NULL" else name_backup
        self.get_most_recent_backups()
        self.oldest_backup = self.set_oldest_backup()
        self.newest_backup = self.set_newest_backup()
    
    def get_most_recent_backups(self):
        for names in os.listdir(self.backup_location):
            self.current_list_of_backups.append(names)
        
        self.current_list_of_backups.sort()
        
    def set_oldest_backup(self):
        return self.current_list_of_backups[0]
    
    def set_newest_backup(self):
        return self.current_list_of_backups[-1]


def main():
    a_backup = BackUp()
    print(a_backup.newest_backup)
    print(a_backup.todays_date)
    print(a_backup.current_backup_name )
    print(a_backup.number_of_backups)
    b_backup = BackUp("2008-06-04",5)
    print(b_backup.newest_backup)
    print(b_backup.todays_date)
    print(b_backup.current_backup_name )
    print(b_backup.number_of_backups)
    
    
if __name__ == "__main__": main()
Anyways, with all that taken care of, I took the rest of the time since to refine it and what have you. Nowhere finish, but at least it is functional.
A sample for those who are interested can be found below. Thank you all that helped with your comments, and advice.



#!/usr/bin/python3

'''
    Module name:
    Author: p2bc
    Author: p2bc
    Contact:
    Desc: Backup script that can be automatic/manual with predefined or dynamic locations
'''
__version__ = '0.0.3'

import shutil
import os, os.path
import datetime


class BackUp:
    
    auto = "False"
    number_of_backups = 3
    backup_location = '/home/superusr/Backup_Scripts/Test_03/Save_Location/'
    folder_to_backup = '/home/superusr/Backup_Scripts/Test_03/Sample_Folder' 
    folder_name = ""
    current_list_of_backups = []
    oldest_backup = ""
    newest_backup = ""
    
    def __init__(self, auto = None, today_date = None, number_backups = None, backup_location = None, folder_backup = None, name_backup = None):
        self.auto = BackUp.auto if auto == None else auto
        self.todays_date = str(datetime.date.today()) if  today_date == None else today_date 
        self.number_of_backups = BackUp.number_of_backups if number_backups == None else number_backups
        self.backup_location = BackUp.backup_location if backup_location == None else backup_location
        self.folder_to_backup = BackUp.folder_to_backup if folder_backup == None else folder_backup
        self.current_backup_name = BackUp.folder_to_backup + "_" + self.todays_date if name_backup == None else name_backup
        self.folder_name = self.current_backup_name.split("/")[-1]
        self.get_most_recent_backups()
        self.oldest_backup = self.set_oldest_backup()
        self.newest_backup = self.set_newest_backup()
        self.make_backup(self.auto)
            
    
    def get_most_recent_backups(self):
        for names in os.listdir(self.backup_location):
            self.current_list_of_backups.append(names)
        
        self.current_list_of_backups.sort()
        
    def set_oldest_backup(self):
        return self.current_list_of_backups[0]
    
    def set_newest_backup(self):
        return self.current_list_of_backups[-1]
        
    def make_backup(self, auto="FALSE"):
        responce = auto.lower()
        if self.return_if_true(responce):  ##  Another way of thinking of this, "Is this True?" Yes it is, therefore it is 'True' that this is 'True'.
            self.run_backup(self.auto)
        else:
            while True:
                try:
                    responce = str(raw_input("Would you like to make a backup... (Yes/No) : "))
                    if self.return_if_true(responce):  ##  Another way of thinking of this, "Is this True?" Yes it is, therefore it is 'True' that this is 'True'.
                        self.run_backup(self.auto)
                        break
                    elif     self.return_if_false(responce):  ##  Another way of thinking of this, "Is this False?" Yes it is, therefore it is 'True' that this is 'False'.
                        print("Thanks for coming, Bye for now!")
                        break
                    print("Needs to be a 'Yes' or 'No' answer.")
                except Exception as e:
                    print(e)

            
    def run_backup(self, auto="FALSE"):
        if os.path.exists(self.backup_location + self.folder_name):
            print(self.return_if_false(auto,"No need to make a backup, there is already a backup for that date."))
        else:
            try:
                shutil.copytree(self.folder_to_backup ,  self.backup_location + self.folder_name )
                print(self.return_if_false(auto,"Congrats you made a backup"))
            except Exception as e:
                print(e)
                
                
    @staticmethod    
    def return_if_true(responce, msg=""): ## Testing for the a certain result , typically the one you expect. In this case  "True" or "Yes"
        responce = responce.lower() if isinstance(responce, str) else "true"
        if responce == "true" or responce == "t" or responce == "yes" or responce == "y":
            return msg if len(msg) > 0 else True  ##  It is "True" because you are testing for a specific result, in this case "True". Think of it as, "It is 'True' that this is 'True'"
    
    @staticmethod    
    def return_if_false(responce, msg=""): ## Testing for the a certain result , typically the one you expect. In this case  "False" or "No"
        responce = responce.lower()
        if responce == "false" or responce == "f" or responce == "no" or responce == "n":
            return msg if len(msg) > 0 else True  ##  It is "True" because you are testing for a specific result, in this case "False". Think of it as, "It is 'True' that this is 'False'"


    def __repr__(self):
        return "Backup()"
        
    def __str__(self):
        pass




def main():
    a_backup = BackUp() #Will prompt the user as a confirmation
    b_backup = BackUp("True") #Will run without a prompt, best for when you want to run as a cron job
    c_backup = BackUp("", "2019-1-31", 5) #So that you get a prompt, but you change the backup reference to another date, and the number of backs there are to '5'
    
    
if __name__ == "__main__": main()
Thanks again everyone
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Changing the initial worksheet name in an MS Excel file azizrasul 3 947 Oct-02-2022, 07:56 PM
Last Post: azizrasul
  Creating a loop with dynamic variables instead of hardcoded values FugaziRocks 3 1,482 Jul-27-2022, 08:50 PM
Last Post: rob101
  How to move multiple columns to initial position SriRajesh 4 1,414 Jul-02-2022, 10:34 AM
Last Post: deanhystad
  Create array of values from 2 variables paulo79 1 1,082 Apr-19-2022, 08:28 PM
Last Post: deanhystad
  variables vcnt, ocnt, and mcnt adding previous values and not resetting to 0 archanut 2 1,929 Feb-12-2021, 06:56 PM
Last Post: deanhystad
  Giving all possible values to four different variables quest_ 7 2,948 Jan-18-2021, 05:18 AM
Last Post: deanhystad
  Print variable values from a list of variables xnightwingx 3 2,626 Sep-01-2020, 02:56 PM
Last Post: deanhystad
  Assign dynamic values to Variables srikanthpython 6 3,416 Jun-06-2020, 03:36 PM
Last Post: srikanthpython
  Differential equations with initial condition in Python (change a working code) Euler2 1 1,827 May-29-2020, 04:06 PM
Last Post: Euler2
  Help with initial conditions Dantooine 1 1,358 Oct-18-2019, 07:22 PM
Last Post: UGuntupalli

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020