问题
I need to update the dictionary of a mapped trait some time after initial trait creation. How do I do this? The following code:
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
self.zap = Trait("None", new_dict)
theBar = bar()
yields:
Traceback (most recent call last):
File "tst_mapped_trait.py", line 13, in <module>
theBar = bar()
File "tst_mapped_trait.py", line 11, in __init__
self.zap = Trait("None", new_dict)
File "C:\Users\dbanas\Anaconda3\envs\pybert-dev\lib\site-packages\traits\trait_handlers.py", line 236, in error
object, name, self.full_info(object, name, value), value
traits.trait_errors.TraitError: The 'zap' trait of a bar instance must be 'None', but a value of <traits.traits.CTrait object at 0x00000000034AA9E8> <class 'traits.traits.CTrait'> was specified.```
回答1:
Okay, the following code worked:
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
# self.zap = Trait("None", new_dict)
# self.zap.update(new_dict)
# self.trait_setq(zap=Trait("None", new_dict))
self.remove_trait("zap")
self.add_trait("zap", Trait("None", new_dict))
theBar = bar()
Note: The commented out lines are things I tried, which did not work.
回答2:
I'm not sure I understand what you're after, but I can make a few recommendations:
Either
is a good choice here if you allow bothNone
andDict
.- Use dynamic initialization to create a value for a trait at runtime. It's preferred to using an
__init__
method. - If you really need an
__init__
method, you must callsuper
inside of it for Traits to work properly, e.g. `super()init(*args, **kwargs)
Here's a version of your code that works and I think solves your problem.
from traits.api import (HasTraits, Either, Dict)
class bar(HasTraits):
zap = Either(None, Dict)
def _zap_default(self):
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
return new_dict
theBar = bar()
print(theBar.zap)
And here's some feedback on the code that didn't work. The line self.zap = Trait("None", new_dict)
below doesn't work because it tries to create a Trait
object but self.zap
only accepts None
or Dict
. My recommendation is to use trait definitions only for typing, at the class-level. Within methods, use regular Python types.
from traits.api import (HasTraits, Trait)
class bar(HasTraits):
zap = Trait("None", {"None": None})
def __init__(self):
# In reality, determined programmatically at runtime.
add_dict_entries = {"One": 1}
new_dict = {"None": None}
new_dict.update(add_dict_entries)
self.zap = Trait("None", new_dict)
theBar = bar()
回答3:
Here's second attempt at an answer given the original poster's comment
If you want the type of zap
to be Dict
and only Dict
, then define it as such. You can also inline the initial value if it doesn't have to be computer at runtime:
>>> from traits.api import HasTraits, Dict
>>> class Bar(HasTraits):
... zap = Dict({5: 'e'})
...
>>> bar = Bar()
>>> bar.zap
{5: 'e'}
If it needs to be computed at runtime, then use dynamic initialization to initialize the value:
>>> class Bar(HasTraits):
... zap = Dict()
...
... def _zap_default(self):
... default = {}
... default[1] = 'a'
... return default
...
>>> bar_dynamic = Bar()
>>> bar_dynamic.zap
{1: 'a'}
Either way, the zap
attribute on the Bar
instance is a regular dictionary once the class has be instantiated (after bar = Bar()
). You should use Trait types after instantiation, only regular Python objects. Traits is there to define and enforce types. The type()
of the objects you're assigning to the typed traits (like zap
here) are regular Python types.
Here's how you'd modify zap
from outside of the class:
>>> bar.zap[2] = 'b'
>>> bar.zap
{5: 'e', 2: 'b'}
>>>
>>> bar_dynamic.zap[3] = 'c'
>>> bar_dynamic.zap
{1: 'a', 3: 'c'}
And now from inside the class, as a regular attribute on self
:
>>> class Bar(HasTraits):
... zap = Dict()
...
... def _zap_default(self):
... default = {}
... default[1] = 'a'
... return default
...
... def add_pair(self, key, value):
... self.zap[key] = value
...
>>> bar_method = Bar()
>>> bar_method.zap
{1: 'a'}
>>> bar_method.add_pair(26, 'z')
>>> bar_method.zap
{1: 'a', 26: 'z'}
来源:https://stackoverflow.com/questions/58050028/how-do-i-update-the-dictionary-of-a-mapped-trait-after-ive-already-constructed