My comments were getting lengthy, so here is a post:
Dynamically Accessing Attributes
As per this answer, you can access attributes for an instance using vars(my_object)
.
It became more clear from our discussion, because the original post didn't include some details, but you can access instance variables, like so as the OP determined from the link above:
{key: value for key, value in self.__dict__.items() if not key.startswith("__")}
but at that point, it sounds like you should be considering, Abstract Base Classes and why / how you would use them.
Accessing Static Attributes
Hopefully I'm not missing your question, but as it stands now ("How to access my attributes name and values inside my class-methods?"), you already know how to access your attributes and functions available to your class - you were doing that with self
. If you mean after you've created an object from your class, then its almost no different:
settings=FolderLeftSettings()
print(settings.name)
will return the name that was given to it. If you want to set them, you can do:
settings.name = "whatever"
Exceptions
Check out this link in regards to how you should use general exceptions.
What's funny is, your side note about the broad exceptions "(wrong header, missing X, integer is string, and so on)" is actually how you would catch those exceptions,
except (ValueError, OSError)
and so on. So if you know what could go wrong, you could replace your broad except with a tuple of exceptions that you are expecting. In some since, the exceptions have been handled. Consider an ini file that is so corrupt that your parser can't handle it. What would you want your parser to do? I'd want mine to exit with an error code. If you don't want that, why not? Catching the exceptions allows for you to handle the bad data that might potentially be passed, but if not enough data is passed (like if I can't load ini file), then you can't really do anything with that object, because you don't know what the user wants to do to the data - at that point it'd be better if the object didn't even exist. At the end of the day, it largely depends on context. If you wanted to build a GUI and allow users to use your parser through it, I wouldn't want the GUI to crash, so I'd have to do more error handling, but if you're using a CLI, you'd want it to either just exit, or (lets say you were using a loop to iterate over the files) you'd want it to skip that file or take defaults. Let me take the one method that utilizes the try, except clause and recode it to how I would have it for you to consider it ->
From the docs:
If a file named in filenames cannot be opened, that file will be ignored. This is designed so that you can specify an iterable of potential configuration file locations (for example, the current directory, the user’s home directory, and some system-wide directory), and all existing configuration files in the iterable will be read.
def read_from_ini(self):
# the below doesn't throw an error - they handle exceptions on the read() call.
config.read('configfile')
# the if statement won't throw an error, unless self.name is not a datatype
# handled by has_section, but if you convert it to a str() in your __init__
# method, you wont have to worry about that.
if config.has_section(self.name):
try:
# not going to throw an error, unless it doesn't have the key
# I didn't check the docs to see if it will always have this key
# or not, so I am including it under the try clause...
self.last_directory = config[self.name]['last_directory']
# the 2 below might throw a value error, but not likely, since you
# are reading the integers from the config as opposed to taking user
# input and setting them
self.row_size = int((config[self.name]['row_size']))
self.column_size = int((config[self.name]['column_size']))
except (ValueError, KeyError):
print(f"Failed to read {self.name}... Overwriting with defaults")
self.write_to_ini()
else:
print(f"Failed to read {self.name}... Overwriting with defaults")
self.write_to_ini()
The above can be reworked to be more elegant in my opinion, but I chose to make it more simple to understand from your section of code. Here is one way that would make it more elegant:
# returns True if it could use the settings, else False
def read_from_ini(self):
config.read('configfile')
if not config.has_section(self.name):
print(f"Failed to read {self.name}... Overwriting with defaults")
self.write_to_ini()
return False
try:
self.last_directory = config[self.name]['last_directory']
self.row_size = int((config[self.name]['row_size']))
self.column_size = int((config[self.name]['column_size']))
except (ValueError, KeyError):
print(f"Failed to read {self.name}... Overwriting with defaults")
self.write_to_ini()
return False
else:
return True
Notice now, how all the tries, ifs, elses, etc are all on the same block level, simply because I inverted the original if.
Another Suggestion
I would say don't do this:
config = configparser.ConfigParser()
configfile = 'RealGui.ini'
class FolderLeftSettings:
...
A suggestion is to either assign a config instance to the class you are creating or use 1 for all of your instances.
Option 1
class FolderLeftSettings:
def __init__(self, configfile, name, last_directory, row_size, column_size):
self.config = configparser.ConfigParser()
self.configfile = configfile
self.name = name
self.last_directory = last_directory
self.row_size = row_size
self.column_size = column_size
Option 2
main.py
import my_class as mc
# here you can instantiate new configs in a for loop
files = ["path/to/config.ini", "another/path/to/new_config.ini]
for file in files:
config = configparser.ConfigParser()
configfile = file
name = "dynamically_allocated_name_here"
last_directory = "last_dir_here"
row_size = 6
column_size = 9
mc.FolderLeftSettings(config, configfile, name, last_directory, row_size, columns_size)
my_class.py
class FolderLeftSettings:
def __init__(self, config, configfile, name, last_directory, row_size, column_size):
self.config = config
self.configfile = configfile
self.name = name
self.last_directory = last_directory
self.row_size = row_size
self.column_size = column_size
or something of the sort.