2

I was coding something, and there was an error in one part. But I can't find why the error occurs.

Code (Sample; similar to the error part):

class Test:
def __init__(self,a=0):
    self.x = a
    self.l = [2**x for x in range(a)]  #<--- self.l = [1,2,4,8,16]
    self.base()

def base(self):
    expr = "self.l{0} = [self.l[x]+{0} for x in range(self.x)]" #<--- when i=4, self.l4 = [5,6,8,12,20]
    for i in range(self.x):
        exec(expr.format(i))

w = Test(5)
print(w.l4)

So I thought that I get this:

[5, 6, 8, 12, 20]

BUT,

File "D:/Documents and Settings/Desktop/py/py/test2.py", line 12, in <module>
  w = Test(5)
File "D:/Documents and Settings/Desktop/py/py/test2.py", line 5, in __init__
  self.base()
File "D:/Documents and Settings/Desktop/py/py/test2.py", line 10, in base
  exec(expr.format(i))
File "<string>", line 1, in <module>
File "<string>", line 1, in <listcomp>

NameError: name 'self' is not defined

(Sorry for bad English)

friend1226
  • 23
  • 4
  • Does this answer your question? [How does exec work with locals?](https://stackoverflow.com/questions/1463306/how-does-exec-work-with-locals) – Llamax Aug 31 '21 at 16:39

2 Answers2

0

If you use a for loop instead of the list comprehension

it will works.

class Test:
    def __init__(self,a=0):
        self.x = a
        self.l = [2**x for x in range(a)]  #<--- self.l = [1,2,4,8,16]
        self.base()

    def base(self):
        # expr = "self.l{0} = [self.l[x]+{0} for x in range(self.x)]" #<--- when i=4, self.l4 = [5,6,8,12,20]
        expr = '''
self.l{0} = []
for x in range(self.x):
    self.l{0}.append(self.l[x]+{0})
'''
        for i in range(self.x):
            expr_formated = expr.format(i)
            print(expr_formated)
            exec(expr_formated)

w = Test(5)
print(w.l4)

The list comprehension is actually using lamda function, as you can see in the python document. squares = [x**2 for x in range(a)] is actually squares = list(map(lambda x: x**2, range(a))). (EDIT: recently I find that this is not accurate, see my question, they are not the same, but they work in a similar way;perhaps one can still uses the lambda to understand this if doesn't want to refer to python assembly) However, creating a function object (lamda function here) inside the exec() will results in problems. I post question here explaining why creating a function object doesn't work as expected. In short, the __closure__ of the defined lamda function is set to None, making the varible a unavailable when the lamda function is called.

If you insist on using lambda function.

There is also another solution. Please refer to my answer to previous mentioned question for more information.

class Test:
    def __init__(self,a=0):
        self.x = a
        self.l = [2**x for x in range(a)]  #<--- self.l = [1,2,4,8,16]
        self.base()

    def base(self):
        expr = """
def closure_helper_func(self):
    self.l{0} = [self.l[x]+{0} for x in range(self.x)]
closure_helper_func(self)""" #<--- when i=4, self.l4 = [5,6,8,12,20]
        for i in range(self.x):
            expr_formated = expr.format(i)
            # print(expr_formated)
            exec(expr_formated)

w = Test(5)
print(w.l4)
hellohawaii
  • 3,074
  • 6
  • 21
-2

There's no need for eval or exec here.

for i in range(self.x):
    setattr(self, "l{}".format(i), [self.l[x]+i for x in range(self.x)])

Although I don't know why you want to do this; better to keep it as a list rather than dynamically set attributes.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895