简体   繁体   English

Enthought traits.api TraitList() 似乎不支持 item_validator

[英]Enthought traits.api TraitList() doesn't seem to support an item_validator

In some applications, I've found that Enthought Traits.api is a helpful addition to support static variable types in python.在某些应用程序中,我发现Enthought Traits.api对支持 python 中的 static 变量类型很有帮助。

I'm trying to use the TraitList() item_validator keyword, but using the item_validator keyword threw an error... I tried this...我正在尝试使用TraitList() item_validator关键字,但使用item_validator关键字会引发错误...我试过这个...

from traits.api import HasTraits, HasRequiredTraits, TraitList, TraitError
from traits.api import Regex, Enum

def garage_item_validator(item):
    """Validate item adjectives and reject pink or floral items"""
    try:
        if isinstance(item, Tool):
            if item.adjective!="pink" or item.adjective!="floral":
                return item
            else:
                raise ValueError()
    except ValueError():
        raise TraitError(f"Cannot put {item} in the Garage()")

class Tool(HasRequiredTraits):
    name = Regex(regex=r"[Ww]rench|[Ll]awnmower", required=True)
    adjective = Enum(*["brown", "rusty", "pink", "floral"], required=True)

    def __init__(self, name, adjective):
        self.name = name
        self.adjective = adjective

    def __repr__(self):
        return """<Tool: {}>""".format(self.name)

class Garage(HasTraits):
    things = TraitList(Tool, item_validator=garage_item_validator)  # <---- TraitList() doesn't work

    def __init__(self):
        self.things = list()

if __name__=="__main__":
    my_garage = Garage()
    my_garage.things.append(Tool("Lawnmower", "brown"))
    my_garage.things.append(Tool("wrench", "pink"))
    print(my_garage)

This throws: TypeError: __init__() got an unexpected keyword argument 'item_validator' although the TraitList docs clearly say item_validator is supported.这会抛出: TypeError: __init__() got an unexpected keyword argument 'item_validator'尽管TraitList 文档明确表示支持item_validator

I also tried to use a traits.api List() , but it just silently ignores the item_validator keyword.我还尝试使用traits.api List() ,但它只是默默地忽略了item_validator关键字。

What should I use to validate the contents of a traits list?我应该使用什么来验证特征列表的内容?

TraitList isn't a Trait , but rather a subclass of list that performs validation and fires events. TraitList不是Trait ,而是执行验证和触发事件的list的子类。 It is used internally by the List trait:它由List特征在内部使用:

>>> from traits.api import HasStrictTraits, List, Int
>>> class Example(HasStrictTraits):
...     x = List(Int) 
... 
>>> example = Example()
>>> example.x
[]
>>> type(example.x)
<class 'traits.trait_list_object.TraitListObject'>
>>> type(example.x).mro()
[<class 'traits.trait_list_object.TraitListObject'>, <class 'traits.trait_list_object.TraitList'>, <class 'list'>, <class 'object'>]

and it gets its item_validator set by the List trait:并通过List特征获取其item_validator设置:

>>> example.x.item_validator
<bound method TraitListObject._item_validator of []>

So although there is no hook to change this validator, it does use the validator from the Trait used as the trait argument the List ( Int in the above example, so the list will only hold integer items).因此,尽管没有钩子来更改此验证器,但它确实使用了 Trait 中的验证器,用作Listtrait参数(在上例中为Int ,因此列表将仅包含 integer 项)。

>>> example.x.append("five")
Traceback (most recent call last):
...
traits.trait_errors.TraitError: Each element of the 'x' trait of an Example instance must be an integer, but a value of 'five' <class 'str'> was specified.

So you can achieve your goal by writing a custom trait that validates the way that you want.因此,您可以通过编写一个验证您想要的方式的自定义特征来实现您的目标。

In your example you want the items to be instances of Tool with certain properties for the adjective , so an Instance trait is a good starting point:在您的示例中,您希望这些项目是Tool具有adjective某些属性的实例,因此 Instance 特征是一个很好的起点:

from traits.api import BaseInstance, HasTraits, List

class GarageTool(BaseInstance):
    
    def __init__(self, **metadata):
        super().__init__(klass=Tool, allow_none=False, **metadata)
        
    def validate(self, object, name, value):
        # validate it is an instance of Tool
        value = super().validate(object, name, value)
        if value.adjective in {"pink", "floral"}:
            self.error(object, name, value)
        return value
    
    def info(self):
        # give better error messages
        return "a Tool which is neither pink nor floral"

class Garage(HasTraits):
    things = List(GarageTool())
    
if __name__ == "__main__":
    my_garage = Garage()
    my_garage.things.append(Tool("Lawnmower", "brown"))

    # now, pink correctly fails in garage_item_validator()
    my_garage.things.append(Tool("wrench", "pink")) # <-- pink fails

which gives an error as desired:根据需要给出错误:

TraitError: Each element of the 'things' trait of a Garage instance must be a Tool which is neither pink nor floral, but a value of <__main__.Tool object at 0x7f899ad89830> <class '__main__.Tool'> was specified.

I made a mistake in my previous attempts...我在之前的尝试中犯了一个错误......

This is wrong... Tool should be Tool() ...这是错误的... Tool应该是Tool() ...

class Garage(HasTraits):
    things = TraitList(Tool, item_validator=garage_item_validator)

This is correct...这是对的...

class Garage(HasTraits):
    things = TraitList(Tool(), item_validator=garage_item_validator)

Additionally, I found that I can manually assign self.things.item_validator after initializing the Garage()此外,我发现我可以在初始化Garage()之后手动分配self.things.item_validator

This also runs correctly...这也可以正确运行...

from traits.api import HasTraits, HasRequiredTraits, TraitList, TraitError
from traits.api import Regex, List, Enum, Either

def garage_item_validator(item):
    try:
        if isinstance(item, Tool):
            if item.adjective!="pink" and item.adjective!="floral":
                return item
            else:
                raise ValueError(item.adjective, item.name)
        else:
            raise ValueError(item.adjective, item.name)
    except Exception:
        raise ValueError(item.adjective, item.name)

class Tool(HasRequiredTraits):
    name = Regex(regex=r"[Ww]rench|[Ll]awnmower", required=True)
    adjective = Enum(*["brown", "rusty", "pink", "floral"])

    def __init__(self, name=None, adjective=None):
        self.name = name
        self.adjective = adjective

    def __repr__(self):
        return """<Tool: {}>""".format(self.name)

class Garage(HasTraits):
    things = TraitList(Tool())

    def __init__(self):
        self.things = list()
        self.things.item_validator = garage_item_validator
    #   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #   Manually assign item_validator attr **after initializing self.things**

if __name__=="__main__":
    my_garage = Garage()
    my_garage.things.append(Tool("Lawnmower", "brown"))

    # now, pink correctly fails in garage_item_validator()
    my_garage.things.append(Tool("wrench", "pink")) # <-- pink fails

    print(my_garage)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM