0

I'm looking for a more pythonic way to update a field within a NamedTuple (from typing). I get field name and value during runtime from a textfile und therefore used exec, but I believe, there must be a better way:

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-

from typing import NamedTuple

class Template (NamedTuple):
    number : int = 0
    name : str = "^NAME^"

oneInstance = Template()
print(oneInstance)
# If I know the variable name during development, I can do this:
oneInstance = oneInstance._replace(number = 77)
# I get this from a file during runtime:
para = {'name' : 'Jones'}
mykey = 'name'
# Therefore, I used exec:
ExpToEval = "oneInstance = oneInstance._replace(" + mykey + " = para[mykey])"
exec(ExpToEval) # How can I do this in a more pythonic (and secure) way?
print(oneInstance)

I guess, from 3.7 on, I could solve this issue with dataclasses, but I need it for 3.6

JDi
  • 131
  • 1
  • 1
  • 5
  • You can not update a namedtuple. It is immutable. Have a look at dataclasses. – MrLeeh Sep 28 '18 at 09:54
  • Formally, you are right and I used the wrong wording. Practically, `oneInstance = oneInstance._replace(number = 77)` works for me like an update, and for `ExpToEval = "oneInstance = oneInstance._replace(" + mykey + " = para[mykey])" exec(ExpToEval)` I'm looking for a better solution. – JDi Sep 28 '18 at 12:28
  • I've rephrased my question using dataclasses, question title is "Update a field in a dataclass with a field name known only during runtime" – JDi Sep 28 '18 at 13:36

1 Answers1

1

Using _replace on namedtuples can not be made "pythonic" whatsoever. Namedtuples are meant to be immutable. If you use a namedtuple other developers will expect that you do not intent to alter your data.

A pythonic approach is indeed the dataclass. You can use dataclasses in Python3.6 as well. Just use the dataclasses backport from PyPi.

Then the whole thing gets really readable and you can use getattrand setattr to address properties by name easily:

from dataclasses import dataclass

@dataclass
class Template:
    number: int = 0
    name: str = "^Name^"

t = Template()

# use setattr and getattr to access a property by a runtime-defined name  
setattr(t, "name", "foo")
print(getattr(t, "name"))

This will result in

foo
MrLeeh
  • 5,321
  • 6
  • 33
  • 51