Python: 对从字典中读取的键应用通配符匹配
Python: apply wildcard match to keys being read from dictionary
这是我在 Blender 中 运行 编写的脚本,但问题涉及其中的 Python 部分。它不是 Blender 特有的。
该脚本最初来自 this answer,它将给定的 material(键)替换为其较新的等价物(值)。
代码如下:
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]]
问题是,当场景可能不仅有 material“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-BLACK": "Sld_BLACK",
"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-BLACK": "Sld_BLACK",
"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]
您可以使用 or re.match
而不是 .split(".")[0]
,方法是将正则表达式存储为字典中的键。正如您在评论中注意到的那样,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'
这是我在 Blender 中 运行 编写的脚本,但问题涉及其中的 Python 部分。它不是 Blender 特有的。
该脚本最初来自 this answer,它将给定的 material(键)替换为其较新的等价物(值)。
代码如下:
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]]
问题是,当场景可能不仅有 material“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-BLACK": "Sld_BLACK",
"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-BLACK": "Sld_BLACK",
"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]
您可以使用 or re.match
而不是 .split(".")[0]
,方法是将正则表达式存储为字典中的键。正如您在评论中注意到的那样,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'