0

I know that most are not fans of using exec to do dynamic assignment. However, I would like to treat variable names as data for my purposes (both pragmatic and eccentric).

Consider this example, which I am using during a class initialization

Now I am going to dynamically assign to these variables

 class Example(object):
      __tree_dict = {"self.genomedir": "genomes", 
                "self.pre_align": "pre_alignment", 
                "self.alignments": "alignments",
                "self.trimmed": "trimmed",
                "self.prf_pre": "prf_preprocess",
                "self.scaled": "scaled",
                "self.csv": "csv"}

      def __init__(self, rootdir):
           self._rootdir = Path(rootdir)
           self.outdir = self.mksubdir(self._rootdir, "out")

        for variable, human_readable in self.__tree_dict.items():
            try:
                exec(variable + " = self.mksubdir(self.outdir,human_readable)")
            except:
                 LOGGER.error("%s failed to initialize as %s, defaulting output to root" (variable, human_readable), exc_info=True)
                 exec(variable + '= Path(self._rootdir)')            

This code will run, but I am not sure whether the exception handling will actually work, because when I add a finally statement to write the variable assignment to a logger, eg

        finally:
            LOGGER.info(variable + "set to %s" % getattr(self, variable))

the python interpreter raises

  AttributeError: 'Example' object has no attribute 'self.csv'

(the attribute name changes at runtime, because the dictionary is not ordered - the attribute itself is not important)

The important problem is that when I reference the new variables outside the scope of the for loop, they are accessible w/ no attribute error. Their assignment has taken place and they are attributes of the class. These variables are found both in dir(self) and also by self.

What feature of python is at play here that prevents me from accessing these variables inside the for block (or the finally block)?

EDIT:

A self contained example:

 class Example(object):
       __vars_to_be_assigned: {"self.foo": "value", "self.bar": "not foo"}

       def __init__(self):
             for key, value in self.__vars_to_be_assigned:
                   try:
                        exec(key + " = value")
                   except:
                        print("Exception!")
                   else:
                        print(getattr(self, variable[5:]))

This example should raise an AttributeError

jay
  • 493
  • 1
  • 3
  • 11
  • Can you provide a minimal, self-contained example? Also, Python version will affect this, since the semantics of `exec` changed from Python 2 to 3. – juanpa.arrivillaga Feb 21 '17 at 19:53
  • Are you on Python 3? Likely, this question will be illuminating: http://stackoverflow.com/questions/15086040/behavior-of-exec-function-in-python-2-and-python-3 – juanpa.arrivillaga Feb 21 '17 at 19:59
  • Turns out to be a simple grammar mistake with the attribute reference (self doesn't have attribute self.self.attribute). However, thanks this link is helpful for future exploration – jay Feb 21 '17 at 20:00

1 Answers1

3

This really is a bad use of exec. Much simpler just to do:

class Example(object):
      __tree_dict = {"genomedir": "genomes", 
                "pre_align": "pre_alignment", 
                "alignments": "alignments",
                "trimmed": "trimmed",
                "prf_pre": "prf_preprocess",
                "scaled": "scaled",
                "csv": "csv"}

      def __init__(self, rootdir):
           self._rootdir = Path(rootdir)
           self.outdir = self.mksubdir(self._rootdir, "out")

        for variable, human_readable in self.__tree_dict.items():
            try:
                setattr(self, variable, self.mksubdir(self.outdir,human_readable))
            except:
                 LOGGER.error("%s failed to initialize as %s, defaulting output to root" (variable, human_readable), exc_info=True)
                 setattr(self, variable, Path(self._rootdir)

The reason why you're getting the error though is simply that the attribute is called csv, not self.csv. Using setattr and removing the self. prefix from the values in __tree_dict will ensure consistency between setting and getting the values.

Duncan
  • 92,073
  • 11
  • 122
  • 156
  • Thanks! good catch. Simple mistake. Also, I hadn't considered using setattr. Thanks – jay Feb 21 '17 at 19:57