How do I update the dictionary of a mapped trait, after I've already constructed it?

ぐ巨炮叔叔 提交于 2020-01-24 01:08:25

问题


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:

  1. Either is a good choice here if you allow both None and Dict.
  2. Use dynamic initialization to create a value for a trait at runtime. It's preferred to using an __init__ method.
  3. If you really need an __init__ method, you must call super 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!