NameError: name 'self' is not defined IN EXEC/EVAL
Asked Answered
B

2

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)

Bland answered 17/6, 2017 at 11:21 Comment(1)
Does this answer your question? How does exec work with locals?Heraldry
O
-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.

Oodles answered 17/6, 2017 at 11:54 Comment(0)
F
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)
Florida answered 22/5, 2022 at 6:20 Comment(0)
O
-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.

Oodles answered 17/6, 2017 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.