Why must a new global variable be created to reference the current class instance in “exec”?












1















I have a class that contains ~20 methods, and in def __init__(self, ...): I have to call many of these methods (~9) but I didn't want to have to call each individual method one by one.



So I took the easy way out and created two list list comprehensions, that use exec to call each method:



[exec("self.create%s()" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("self.compile%sPage(self)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


When I ran this code using python3 filename.py I got an error, that read:



NameError: name 'self' is not defined


Through trial and error I found that; in order to get this code to work I had to create a copy of self called instance and make the new instance variable a global variable and then call the method using ClassName.methodName(instance) instead of self.methodName():



With the working code being:



global instance; instance = self
[exec("ClassNamecreate%s(instance)" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("ClassName.compile%sPage(instance)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


Why is this? Why is the self variable undefined in exec despite it being available to the scope that exec is being called in?



Update: I'm using Python 3.6.7










share|improve this question

























  • list comprehension in exec with empty locals: NameError

    – Aran-Fey
    Nov 25 '18 at 16:50






  • 1





    Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

    – PM 2Ring
    Nov 25 '18 at 18:15
















1















I have a class that contains ~20 methods, and in def __init__(self, ...): I have to call many of these methods (~9) but I didn't want to have to call each individual method one by one.



So I took the easy way out and created two list list comprehensions, that use exec to call each method:



[exec("self.create%s()" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("self.compile%sPage(self)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


When I ran this code using python3 filename.py I got an error, that read:



NameError: name 'self' is not defined


Through trial and error I found that; in order to get this code to work I had to create a copy of self called instance and make the new instance variable a global variable and then call the method using ClassName.methodName(instance) instead of self.methodName():



With the working code being:



global instance; instance = self
[exec("ClassNamecreate%s(instance)" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("ClassName.compile%sPage(instance)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


Why is this? Why is the self variable undefined in exec despite it being available to the scope that exec is being called in?



Update: I'm using Python 3.6.7










share|improve this question

























  • list comprehension in exec with empty locals: NameError

    – Aran-Fey
    Nov 25 '18 at 16:50






  • 1





    Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

    – PM 2Ring
    Nov 25 '18 at 18:15














1












1








1








I have a class that contains ~20 methods, and in def __init__(self, ...): I have to call many of these methods (~9) but I didn't want to have to call each individual method one by one.



So I took the easy way out and created two list list comprehensions, that use exec to call each method:



[exec("self.create%s()" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("self.compile%sPage(self)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


When I ran this code using python3 filename.py I got an error, that read:



NameError: name 'self' is not defined


Through trial and error I found that; in order to get this code to work I had to create a copy of self called instance and make the new instance variable a global variable and then call the method using ClassName.methodName(instance) instead of self.methodName():



With the working code being:



global instance; instance = self
[exec("ClassNamecreate%s(instance)" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("ClassName.compile%sPage(instance)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


Why is this? Why is the self variable undefined in exec despite it being available to the scope that exec is being called in?



Update: I'm using Python 3.6.7










share|improve this question
















I have a class that contains ~20 methods, and in def __init__(self, ...): I have to call many of these methods (~9) but I didn't want to have to call each individual method one by one.



So I took the easy way out and created two list list comprehensions, that use exec to call each method:



[exec("self.create%s()" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("self.compile%sPage(self)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


When I ran this code using python3 filename.py I got an error, that read:



NameError: name 'self' is not defined


Through trial and error I found that; in order to get this code to work I had to create a copy of self called instance and make the new instance variable a global variable and then call the method using ClassName.methodName(instance) instead of self.methodName():



With the working code being:



global instance; instance = self
[exec("ClassNamecreate%s(instance)" % x) for x in "ArticleObjects SeriesObjects ArticleList SearchList".split(" ")]
[exec("ClassName.compile%sPage(instance)" % x) for x in "About Screenshots Search Contact Articles".split(" ")]


Why is this? Why is the self variable undefined in exec despite it being available to the scope that exec is being called in?



Update: I'm using Python 3.6.7







python class global-variables instance self






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 25 '18 at 16:29







LogicalBranch

















asked Nov 25 '18 at 16:23









LogicalBranchLogicalBranch

141117




141117













  • list comprehension in exec with empty locals: NameError

    – Aran-Fey
    Nov 25 '18 at 16:50






  • 1





    Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

    – PM 2Ring
    Nov 25 '18 at 18:15



















  • list comprehension in exec with empty locals: NameError

    – Aran-Fey
    Nov 25 '18 at 16:50






  • 1





    Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

    – PM 2Ring
    Nov 25 '18 at 18:15

















list comprehension in exec with empty locals: NameError

– Aran-Fey
Nov 25 '18 at 16:50





list comprehension in exec with empty locals: NameError

– Aran-Fey
Nov 25 '18 at 16:50




1




1





Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

– PM 2Ring
Nov 25 '18 at 18:15





Apart from the unnecessary use of exec, it's an anti-pattern to use a list comp purely for side effects. If you don't need the resulting list, then please use a proper loop.

– PM 2Ring
Nov 25 '18 at 18:15












3 Answers
3






active

oldest

votes


















4














There's lots of good suggestions here for how to avoid the exec statement (which is generally bad), but to answer your question about why this happens, it's got more to do with the list comprehension. List comprehensions create a new scope, and when you call exec without a globals or locals argument, it uses the locals() function:




Note: The default locals act as described for function locals() below




Source



Here you can see what the results of the locals() function look like from within a list comprehension:



class Sample:
def __init__(self):
k = 4
print(locals())
exec("print(locals())")
[print(locals()) for x in range(1)]
[exec("print(locals())") for x in range(1)]
Sample()


output:



{'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
{'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
{'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
{'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}


So, locals() is the same inside or outside the exec. It's the list comprehension that changes it. Only, when you're outside an exec statement, the interpreter can fall past the locals of the list comprehension and find self in the outer scope. No such luck once you call exec.






share|improve this answer


























  • Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

    – LogicalBranch
    Nov 26 '18 at 20:00



















4














Using getattr is simpler (and usually safer) than exec. Try something along these lines:



def __init__(self):
suffixes = ["ArticleObjects", "SeriesObjects", ...]
for suffix in suffixes:
method = getattr(self, "create" + suffix)
method()





share|improve this answer































    2














    I wouldn't use exec for this. While it may be the shortest version, it might also confuse both collaborators and code analysis tools. I'd use something like this instead:



    class Test:
    def __init__(self):
    for f in (self.createA, self.createB, self.createC):
    f()

    def createA(self):
    print("A")

    def createB(self):
    print("B")

    def createC(self):
    print("C")





    share|improve this answer























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53469473%2fwhy-must-a-new-global-variable-be-created-to-reference-the-current-class-instanc%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      4














      There's lots of good suggestions here for how to avoid the exec statement (which is generally bad), but to answer your question about why this happens, it's got more to do with the list comprehension. List comprehensions create a new scope, and when you call exec without a globals or locals argument, it uses the locals() function:




      Note: The default locals act as described for function locals() below




      Source



      Here you can see what the results of the locals() function look like from within a list comprehension:



      class Sample:
      def __init__(self):
      k = 4
      print(locals())
      exec("print(locals())")
      [print(locals()) for x in range(1)]
      [exec("print(locals())") for x in range(1)]
      Sample()


      output:



      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}


      So, locals() is the same inside or outside the exec. It's the list comprehension that changes it. Only, when you're outside an exec statement, the interpreter can fall past the locals of the list comprehension and find self in the outer scope. No such luck once you call exec.






      share|improve this answer


























      • Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

        – LogicalBranch
        Nov 26 '18 at 20:00
















      4














      There's lots of good suggestions here for how to avoid the exec statement (which is generally bad), but to answer your question about why this happens, it's got more to do with the list comprehension. List comprehensions create a new scope, and when you call exec without a globals or locals argument, it uses the locals() function:




      Note: The default locals act as described for function locals() below




      Source



      Here you can see what the results of the locals() function look like from within a list comprehension:



      class Sample:
      def __init__(self):
      k = 4
      print(locals())
      exec("print(locals())")
      [print(locals()) for x in range(1)]
      [exec("print(locals())") for x in range(1)]
      Sample()


      output:



      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}


      So, locals() is the same inside or outside the exec. It's the list comprehension that changes it. Only, when you're outside an exec statement, the interpreter can fall past the locals of the list comprehension and find self in the outer scope. No such luck once you call exec.






      share|improve this answer


























      • Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

        – LogicalBranch
        Nov 26 '18 at 20:00














      4












      4








      4







      There's lots of good suggestions here for how to avoid the exec statement (which is generally bad), but to answer your question about why this happens, it's got more to do with the list comprehension. List comprehensions create a new scope, and when you call exec without a globals or locals argument, it uses the locals() function:




      Note: The default locals act as described for function locals() below




      Source



      Here you can see what the results of the locals() function look like from within a list comprehension:



      class Sample:
      def __init__(self):
      k = 4
      print(locals())
      exec("print(locals())")
      [print(locals()) for x in range(1)]
      [exec("print(locals())") for x in range(1)]
      Sample()


      output:



      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}


      So, locals() is the same inside or outside the exec. It's the list comprehension that changes it. Only, when you're outside an exec statement, the interpreter can fall past the locals of the list comprehension and find self in the outer scope. No such luck once you call exec.






      share|improve this answer















      There's lots of good suggestions here for how to avoid the exec statement (which is generally bad), but to answer your question about why this happens, it's got more to do with the list comprehension. List comprehensions create a new scope, and when you call exec without a globals or locals argument, it uses the locals() function:




      Note: The default locals act as described for function locals() below




      Source



      Here you can see what the results of the locals() function look like from within a list comprehension:



      class Sample:
      def __init__(self):
      k = 4
      print(locals())
      exec("print(locals())")
      [print(locals()) for x in range(1)]
      [exec("print(locals())") for x in range(1)]
      Sample()


      output:



      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'k': 4, 'self': <__main__.Sample object at 0x00000000030295C0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}
      {'x': 0, '.0': <range_iterator object at 0x00000000030019F0>}


      So, locals() is the same inside or outside the exec. It's the list comprehension that changes it. Only, when you're outside an exec statement, the interpreter can fall past the locals of the list comprehension and find self in the outer scope. No such luck once you call exec.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Nov 25 '18 at 17:08

























      answered Nov 25 '18 at 17:01









      R. DavidsonR. Davidson

      17019




      17019













      • Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

        – LogicalBranch
        Nov 26 '18 at 20:00



















      • Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

        – LogicalBranch
        Nov 26 '18 at 20:00

















      Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

      – LogicalBranch
      Nov 26 '18 at 20:00





      Thanks for the answer, I've replaced the exec and now I know what was causing my problem.

      – LogicalBranch
      Nov 26 '18 at 20:00













      4














      Using getattr is simpler (and usually safer) than exec. Try something along these lines:



      def __init__(self):
      suffixes = ["ArticleObjects", "SeriesObjects", ...]
      for suffix in suffixes:
      method = getattr(self, "create" + suffix)
      method()





      share|improve this answer




























        4














        Using getattr is simpler (and usually safer) than exec. Try something along these lines:



        def __init__(self):
        suffixes = ["ArticleObjects", "SeriesObjects", ...]
        for suffix in suffixes:
        method = getattr(self, "create" + suffix)
        method()





        share|improve this answer


























          4












          4








          4







          Using getattr is simpler (and usually safer) than exec. Try something along these lines:



          def __init__(self):
          suffixes = ["ArticleObjects", "SeriesObjects", ...]
          for suffix in suffixes:
          method = getattr(self, "create" + suffix)
          method()





          share|improve this answer













          Using getattr is simpler (and usually safer) than exec. Try something along these lines:



          def __init__(self):
          suffixes = ["ArticleObjects", "SeriesObjects", ...]
          for suffix in suffixes:
          method = getattr(self, "create" + suffix)
          method()






          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 25 '18 at 16:29









          PiotrPiotr

          64976




          64976























              2














              I wouldn't use exec for this. While it may be the shortest version, it might also confuse both collaborators and code analysis tools. I'd use something like this instead:



              class Test:
              def __init__(self):
              for f in (self.createA, self.createB, self.createC):
              f()

              def createA(self):
              print("A")

              def createB(self):
              print("B")

              def createC(self):
              print("C")





              share|improve this answer




























                2














                I wouldn't use exec for this. While it may be the shortest version, it might also confuse both collaborators and code analysis tools. I'd use something like this instead:



                class Test:
                def __init__(self):
                for f in (self.createA, self.createB, self.createC):
                f()

                def createA(self):
                print("A")

                def createB(self):
                print("B")

                def createC(self):
                print("C")





                share|improve this answer


























                  2












                  2








                  2







                  I wouldn't use exec for this. While it may be the shortest version, it might also confuse both collaborators and code analysis tools. I'd use something like this instead:



                  class Test:
                  def __init__(self):
                  for f in (self.createA, self.createB, self.createC):
                  f()

                  def createA(self):
                  print("A")

                  def createB(self):
                  print("B")

                  def createC(self):
                  print("C")





                  share|improve this answer













                  I wouldn't use exec for this. While it may be the shortest version, it might also confuse both collaborators and code analysis tools. I'd use something like this instead:



                  class Test:
                  def __init__(self):
                  for f in (self.createA, self.createB, self.createC):
                  f()

                  def createA(self):
                  print("A")

                  def createB(self):
                  print("B")

                  def createC(self):
                  print("C")






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 25 '18 at 16:31









                  FelixFelix

                  3,2212930




                  3,2212930






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53469473%2fwhy-must-a-new-global-variable-be-created-to-reference-the-current-class-instanc%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Wiesbaden

                      Marschland

                      Dieringhausen