如何解决Python:将通配符匹配应用于从字典中读取的键
这是针对我在Blender中运行的脚本的,但问题与它的Python部分有关。它不是特定于Blender的。
该脚本最初来自this answer,并且用较新的等效内容(值)替换了给定的材料(键)。
这是代码:
import bpy
objects = bpy.context.selected_objects
mat_dict = {
"SOLID-WHITE": "Sld_WHITE","SOLID-BLACK": "Sld_BLACK","SOLID-BLUE": "Sld_BLUE"
}
for obj in objects:
for slot in obj.material_slots:
slot.material = bpy.data.materials[mat_dict[slot.material.name]]
障碍在于,当场景不仅包含材质为“ SOLID-WHITE”的对象而且包含“ SOLID-WHITE.001”,“ SOLID-WHITE.002”等时,如何处理重复项。
我在看this answer时遇到一个有关Python中通配符的问题,看来fnmatch
可能非常适合此任务。
我尝试将fnmatch
处理到代码的最后一行。我也尝试过用它包装字典键(非常了解,我知道)。这两种方法均无效。
如何在每个字典键上进行通配符匹配?
例如,某个对象是否具有“ SOLID-WHITE”或“ SOLID-WHITE” -dot-some-number,它仍将替换为“ Sld_WHITE”吗?
解决方法
有两种方法可以解决此问题。 您可以制作匹配模糊名称的智能词典。或者,您可以更改用于查找颜色的键。
这是使用fnmatch的第一种方法的示例。
当颜色包含数字时,此方法会将查找时间复杂度从O(1)更改为O(n)。此方法使用__missing__
方法扩展了UserDict。如果在字典中找不到键,则会调用__missing__
方法。它将使用fnmatch将每个键与给定的键进行比较。
from collections import UserDict
import fnmatch
import bpy
objects = bpy.context.selected_objects
class Colors(UserDict):
def __missing__(self,key):
for color in self.keys():
if fnmatch.fnmatch(key,color + "*"):
return self[color]
raise KeyError(f"could not match {key}")
mat_dict = Colors({
"SOLID-WHITE": "Sld_WHITE","SOLID-BLACK": "Sld_BLACK","SOLID-BLUE": "Sld_BLUE"
})
for obj in objects:
for slot in obj.material_slots:
slot.material = bpy.data.materials[mat_dict[slot.material.name]]
这是使用正则表达式的第二种方法的示例。
import re
import bpy
objects = bpy.context.selected_objects
mat_dict = {
"SOLID-WHITE": "Sld_WHITE","SOLID-BLUE": "Sld_BLUE"
}
pattern = re.compile(r"([A-Z\-]+)(?:\.\d+)?")
# matches any number of capital letters and dashes
# can be followed by a dot followed by any number of digits
# this pattern can match the following strings
# ["AAAAA","----","AA-AA.00005"]
for obj in objects:
for slot in obj.material_slots:
match = pattern.fullmatch(slot.material.name)
if match:
slot.material = bpy.data.materials[mat_dict[match.group(1)]]
else:
slot.material = bpy.data.materials[mat_dict[slot.material.name]]
,
我对Blender毫无头绪,所以不确定是否能正确解决问题,但是以下内容呢?
mat_dict = {
"SOLID-WHITE": "Sld_WHITE","SOLID-BLUE": "Sld_BLUE"
}
def get_new_material(old_material):
for k,v in mat_dict.items():
# .split(".")[0] extracts the part to the left of the dot (if there is one)
if old_material.split(".")[0] == k:
return v
return old_material
for obj in objects:
for slot in obj.material_slots:
new_material = get_new_material(slot.material.name)
slot.material = bpy.data.materials[new_material]
通过将正则表达式存储为字典中的键,可以代替.split(".")[0]
或re.match
来使用。正如您在评论中所注意到的,startswith
可能匹配得太多,fnmatch
也会如此。
上述功能的作用示例:
In [3]: get_new_material("SOLID-WHITE.001")
Out[3]: 'Sld_WHITE'
In [4]: get_new_material("SOLID-WHITE")
Out[4]: 'Sld_WHITE'
In [5]: get_new_material("SOLID-BLACK")
Out[5]: 'Sld_BLACK'
In [6]: get_new_material("test")
Out[6]: 'test'
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。