Variables being overridden to initial values. - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: General Coding Help (https://python-forum.io/forum-8.html) +--- Thread: Variables being overridden to initial values. (/thread-30131.html) |
Variables being overridden to initial values. - p2bc - Oct-06-2020 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: 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. Any help would be appreciated, thanks in advance. OS: Ubuntu 20.04 Python Version: Python 3.8.2 RE: Variables being overridden to initial values. - buran - Oct-06-2020 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. RE: Variables being overridden to initial values. - bowlofred - Oct-06-2020 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}")
RE: Variables being overridden to initial values. - buran - Oct-06-2020 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}')
RE: Variables being overridden to initial values. - p2bc - Oct-06-2020 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. RE: Variables being overridden to initial values. - deanhystad - Oct-06-2020 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]}') 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.
RE: Variables being overridden to initial values. - p2bc - Oct-10-2020 @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 |