Attributebase对象匹配
matchable的Python项目详细描述
相配的
Python中基于属性的对象匹配
安装
从PyPI:
pip install matchable
使用
matchable
是围绕两个对象match
和Spec
构建的:
match
用于创建模式并将它们链接到用户指定的值。在Spec
包含一个模式列表,这些模式可以与任意对象匹配,以便组合它们的链接值。在
例如:
^{pr2}${{{cd7}与一个大于cd6}的属性相匹配。在
match(dict)['key'] == 0
此模式将所有dict(或其子类)与一个项'key'
匹配,该项比较等于0
。在
此类模式可用于通过Spec
类创建规格:
spec = Spec.from_patterns({
match(object).attr > 0: 'foo',
match(dict)['key'] == 0: 'bar',
})
等级库可用于将其他对象与其图案相匹配,并组合相应的值:
>>> spec.match({'key': 0})
'bar'
>>> class Test:
... attr = 1
...
>>> spec.match(Test())
'foo'
如果没有与给定对象匹配的条件,则引发异常:
>>> spec.match({'key': 5})
[...]
matchable.exceptions.NoMatchError: No matching conditions found for object {'key': 5}
如果多个条件与给定对象匹配,则将组合它们的相应值(参见下面的段落 对于“合并”在不同情况下的含义):
>>> class Test(dict):
... attr = 1
...
>>> spec.match(Test(key=0))
'bar'
这里,对于字符串,“组合”意味着只需选择最后看到的值 (有关自定义此行为的信息,请参阅下一节)。 但是,对于字典,标准行为是更新其内容:
>>> spec = Spec.from_patterns({
... match(object).attr > 0: dict(x='foo'),
... match(dict)['key'] == 0: dict(y='bar'),
... })
>>> spec.match(Test(key=0))
{'x': 'foo', 'y': 'bar'}
这对于将多个部分配置合并为一个配置非常有用。在
注册自定义组合
上一节的示例显示,对于string,“last seen wins”策略 用于组合两个值。另一种可能是连接值。 我们可以通过以下方式实现:
from matchable.spec import Wrapper, WRAPPER_TYPES
class Concatenate(Wrapper):
def __or__(self, other):
return Concatenate(self.obj + other.obj)
WRAPPER_TYPES[str] = Concatenate
基本上,我们需要为WRAPPER_TYPES
中的相应类型提供一个自定义的Wrapper
类。
由于各个值是通过lhs | rhs
组合的,所以这个包装器需要做的就是实现
__or__
协议,并在那里执行组合逻辑。这里self.obj
访问包装好的对象
(本例中为字符串)。需要注意的是,在创建之前,包装器需要注册
spec因为一旦创建了spec,它就不会更改其值的包装类型。在
现在我们可以创建等级库并匹配一些对象:
spec = Spec.from_patterns({
match(dict)['x'] > 0: 'foo',
match(dict)['x'] > 1: 'bar',
})
>>> spec.match({'x': 1})
'foo'
>>> spec.match({'x': 2})
'foobar'
模式、匹配和类层次结构
模式可以引用某些类型的对象的属性(match(obj).x
)或项(match(obj)['x']
)
或者直接指向类型(或者match(tp)
或者只是tp
)。在
在匹配过程中,如果应用了多个模式,则基于属性的模式优先于基于类型的模式。
事实上,所有匹配模式的列表都是按照基于类型的模式放在左边的方式进行排序的,
按照匹配对象的反转顺序MRO,
然后是基于属性的模式,在规范创建期间按其外观顺序排列。
然后从左到右更新相应的值,其中r.h.s.值更新l.h.s.值(即lhs | rhs
)。
例如,对于一个字符串列表,由于它们更新为“最后看到的胜利”,最右边的值将是结果。
对于一组集合的等价物是s1 | s2 | s3
,即它们建立一个联合。在
Spec.match
方法还支持一个typewise
关键字参数,该参数可用于更改模式的排序
这样,基于属性的模式只优先于其关联的基于类型的模式,否则匹配的
对象的反向MRO是受尊重的。一、 对于typewise=True
基于子类型的模式优先于基于属性的模式
一个祖先的模式,而对于typewise=False
(默认值),则相反(因为这里基于类型的模式是
一直排到左边)。在
下图显示了类层次结构以及不同匹配风格的优先顺序。在
示例
假设一个se应该以某种方式可视化的对象序列。这些对象是:
from dataclasses import dataclass
@dataclass
class Plant:
height: float
@dataclass
class Flower(Plant):
n_petals: int
@dataclass
class Tree(Plant):
pass
plants = [ # some random numbers
Flower(height=4.0, n_petals=5),
Flower(height=3.5, n_petals=7),
Flower(height=6.8, n_petals=4),
Tree(height=104.6),
Flower(height=1.8, n_petals=9),
Tree(height=187.2),
Tree(height=121.9),
Flower(height=2.2, n_petals=5),
]
假设我们要将这些植物对象形象化为具有不同样式的矩形来表示它们的属性。 我们可以通过创建一个表示各种矩形配置的规范来实现(注意,如果 规范中有多个匹配项,dict将合并为一个):
config = Spec.from_patterns({
match(Plant): dict(linestyle='-', linewidth=2, facecolor='none'),
match(Flower): dict(edgecolor='orange'),
match(Flower).height < 2.0: dict(hatch='/'),
match(Flower).n_petals >= 7: dict(facecolor='#ff7f0e33'),
match(Tree): dict(edgecolor='green'),
match(Tree).height > 160: dict(linestyle='--'),
})
然后我们可以添加矩形面片,如下所示:
fig, ax = plt.subplots()
for i, plant in enumerate(plants):
ax.add_patch(Rectangle((i, 0), 0.5, 1, **config.match(plant)))
这给了我们以下的情节:
- 项目
标签: