0

The problem is that running exec() on a variable with the name of a named argument does not work in normal operation of the program, but setting a break point and executing the line of code in question at the same location seems to work.

Reproduction of the issue follows:

"""

    LEGACY CODE, CANNOT CHANGE
    vvvvvvvvvvv

"""

from collections import namedtuple


class A_Object(namedtuple('A_object', 'parameter_a' ' parameter_b')):

    def __new__(cls, *args, **kwargs):
        init = super(A_Object, cls).__new__(cls, parameter_a=[], parameter_b=[])
        return init

    """
    
        ^^^^^^^^^^^
        LEGACY CODE, CANNOT CHANGE
    
    """

    def _remove_all_data(self):
        for field in self._fields:
            print("removing field:", field)

            # This does not work in the normal execution of the program,
            # but if you set a break point and execute the same command in the python console ( in Pycharm ) it works.
            exec('self = self._replace({0}=[])'.format(field))

        return self


def driver():
    a = A_Object()

    a.parameter_a.append('a')
    a.parameter_a.append('b')
    a.parameter_a.append('c')

    a.parameter_b.append(1)
    a.parameter_b.append(2)
    a.parameter_b.append(3)

    # Try to remove all data from properties.
    a = a._remove_all_data()

    tmp = a._asdict()

    for k in tmp.keys():
        for i in range(0, len(tmp[k])):
            print('{}[{}]='.format(k,i), tmp[k][i])


if __name__ == '__main__':
    driver()

This minimum reproducible example will show that running the program will output the contents of parameter_a, and parameter_b ( That is the issue ). The desired outcome would be parameter_a, and parameter_b to be empty list. If you put a break point on the exec('self = self._replace({0}=[])'.format(field)) line and during debug open up the python console in pycharm and run that line of code "exec('self = self._replace({0}=[])'.format(field))" in the console. it will clear the parameter_a, and paramter_b ( giving the desired output ).

4
  • you cannot modify local variables with exec/eval. You shouldn't be using exec here in the first place Commented Dec 31, 2020 at 21:20
  • But clearly that is not exactly the case if I can use exec to modify local variables in the python console. And I agree NOT using exec/eval in this case is best practice. Commented Dec 31, 2020 at 21:24
  • 1
    No, you cannot, in Python 3. A debugger console is different from a normal environment (it's actually using exec already...) Commented Dec 31, 2020 at 21:24
  • Thank you, I did not know that. Commented Dec 31, 2020 at 21:26

1 Answer 1

2

You don't need exec for this: if you want a dynamically-named keyword argument, use the **kwargs syntax. You might as well also replace all the fields at once instead of calling _replace multiple times in a loop:

def _remove_all_data(self):
    return self._replace(**{ f: [] for f in self._fields })
Sign up to request clarification or add additional context in comments.

3 Comments

I forgot the ** when I first tired kwargs pass in. This solved for the desire output/outcome. Thank you. Before I mark this as the answer do you know why exec has that odd behavior of not working in this program?
@Theisen did you read the documentation for exec? it warns you that modifications to local variables will not work.
It's not odd behaviour - it's the expected behaviour. exec doesn't change the values of local variables like self. As the docs for the exec function advise: "Pass an explicit locals dictionary if you need to see effects of the code on locals after function exec() returns."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.