目前我的代码在某些方面看起来确实重复:
from dataclasses import dataclass, field
@dataclass
class Locator:
locator_attr: str
transform: str = field(init=False)
position: tuple = field(init=False)
point_on_curve: str = field(init=False)
normal_attr: str = field(init=False)
normal: tuple = field(init=False)
normalizedNormal_attr: str = field(init=False)
normalizedNormal: tuple = field(init=False)
tangent_attr: str = field(init=False)
tangent: tuple = field(init=False)
normalizedTangent_attr: str = field(init=False)
normalizedTangent: tuple = field(init=False)
def __post_init__(self) -> None:
self.transform = cmds.listConnections(f"{self.locator_attr}.locatorTransform")[0]
self.position = cmds.xform(self.transform, q=True, t=True, ws=True)
self.point_on_curve = cmds.listConnections(f"{self.locator_attr}.pointOnCurve")[0]
self.normal_attr = f"{self.point_on_curve}.normal"
self.normal = cmds.getAttr(f"{self.point_on_curve}.normal")[0]
self.normalizedNormal_attr = f"{self.point_on_curve}.normalizedNormal"
self.normalizedNormal = cmds.getAttr(f"{self.point_on_curve}.normalizedNormal")[0]
self.tangent_attr = f"{self.point_on_curve}.tangent"
self.tangent = cmds.getAttr(f"{self.point_on_curve}.tangent")[0]
self.normalizedTangent_attr = f"{self.point_on_curve}.normalizedTangent"
self.normalizedTangent = cmds.getAttr(f"{self.point_on_curve}.normalizedTangent")[0]
本节相同
normal_attr: str = field(init=False)
normal: tuple = field(init=False)
normalizedNormal_attr: str = field(init=False)
normalizedNormal: tuple = field(init=False)
tangent_attr: str = field(init=False)
tangent: tuple = field(init=False)
normalizedTangent_attr: str = field(init=False)
normalizedTangent: tuple = field(init=False)
还有
self.normal_attr = f"{self.point_on_curve}.normal"
self.normal = cmds.getAttr(f"{self.point_on_curve}.normal")[0]
self.normalizedNormal_attr = f"{self.point_on_curve}.normalizedNormal"
self.normalizedNormal = cmds.getAttr(f"{self.point_on_curve}.normalizedNormal")[
0
]
self.tangent_attr = f"{self.point_on_curve}.tangent"
self.tangent = cmds.getAttr(f"{self.point_on_curve}.tangent")[0]
self.normalizedTangent_attr = f"{self.point_on_curve}.normalizedTangent"
self.normalizedTangent = cmds.getAttr(
f"{self.point_on_curve}.normalizedTangent"
)[0]
术语 ['normal','normalizedNormal','tangent','normalizedTangent'] 被重复,如果我想添加新属性,我需要修改很多行。
我尝试在类中创建一个列表,因此我只能通过将属性添加到列表中来添加属性:
_node_attrs:list = field(default_factory=['normal','normalizedNormal','tangent','normalizedTangent'])
from dataclasses import dataclass, field
@dataclass
class Locator:
locator_attr: str
transform: str = field(init=False)
position: tuple = field(init=False)
point_on_curve: str = field(init=False)
_node_attrs:list = ['normal','normalizedNormal','tangent','normalizedTangent']
for _attrname in _node_attrs:
globals()[f'{_attrname}_attr']: str = field(init=False)
globals()[f'{_attrname}']: tuple = field(init=False)
def __post_init__(self) -> None:
self.transform = cmds.listConnections(f"{self.locator_attr}.locatorTransform")[0]
self.position = cmds.xform(self.transform, q=True, t=True, ws=True)
self.point_on_curve = cmds.listConnections(f"{self.locator_attr}.pointOnCurve")[
0
]
如果列表是一个字段,我收到错误:
# Error: 'Field' object is not iterable
# Traceback (most recent call last):
# File "<maya console>", line 3, in <module>
# File "<maya console>", line 10, in Locator
# TypeError: 'Field' object is not iterable #
但万一我改成一个简单的列表,我得到了
# Error: mutable default <class 'list'> for field _node_attrs is not allowed: use default_factory
# Traceback (most recent call last):
# File "<maya console>", line 3, in <module>
# File "C:\Program Files\Autodesk\Maya2022\Python37\lib\dataclasses.py", line 1010, in dataclass
# return wrap(_cls)
# File "C:\Program Files\Autodesk\Maya2022\Python37\lib\dataclasses.py", line 1002, in wrap
# return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen)
# File "C:\Program Files\Autodesk\Maya2022\Python37\lib\dataclasses.py", line 850, in _process_class
# for name, type in cls_annotations.items()]
# File "C:\Program Files\Autodesk\Maya2022\Python37\lib\dataclasses.py", line 850, in <listcomp>
# for name, type in cls_annotations.items()]
# File "C:\Program Files\Autodesk\Maya2022\Python37\lib\dataclasses.py", line 733, in _get_field
# raise ValueError(f'mutable default {type(f.default)} for field '
# ValueError: mutable default <class 'list'> for field _node_attrs is not allowed: use default_factory #
使用数据类最简洁的方法是什么?
有问题的部分似乎是这样的:
@dataclass
class Locator:
# snip
_node_attrs:list = field(default_factory=['normal','normalizedNormal','tangent','normalizedTangent'])
for _attrname in _node_attrs:
globals()[f'{_attrname}_attr']: str = field(init=False)
globals()[f'{_attrname}']: tuple = field(init=False)
我可以看到这里出现了两件事:
@dataclass
class Locator:
# snip
__annotations__ = {}
_node_attrs = ['normal','normalizedNormal','tangent','normalizedTangent']
for _attrname in _node_attrs:
locals()[f'{_attrname}_attr'] = field(init=False)
locals()[_attrname] = field(init=False)
__annotations__[f'{_attrname}_attr'] = str
__annotations__[_attrname] = tuple
# remove variables you don't want in the class scope
del _attrname
del _node_attrs
properties 消除重复 — 或者,像在代码中一样,只计算一次,缓存属性?
from functools import cached_property
class Locator:
def __init__(self, locator_attr: str):
self.locator_attr = locator_attr
@cached_property
def transform() -> str:
return cmds.listConnections(f"{self.locator_attr}.locatorTransform")[0]
@cached_property
def position() -> tuple:
return cmds.xform(self.transform, q=True, t=True, ws=True)
@cached_property
def point_on_curve() -> str:
return cmds.listConnections(f"{self.locator_attr}.pointOnCurve")[0]
@cached_property
def normal() -> tuple:
return cmds.getAttr(self.normal_attr)[0]
@cached_property
def normalizedNormal_attr() -> str:
return f"{self.point_on_curve}.normalizedNormal"
@cached_property
def normalizedNormal() -> tuple:
return cmds.getAttr(self.normalizedNormal_attr)[0]
@cached_property
def tangent_attr() -> str:
return f"{self.point_on_curve}.tangent"
@cached_property
def tangent() -> tuple:
return cmds.getAttr(self.tangent_attr)[0]
@cached_property
def normalizedTangent_attr() -> str:
return f"{self.point_on_curve}.normalizedTangent"
@cached_property
def normalizedTangent() -> tuple:
return cmds.getAttr(self.normalizedTangent_attr)[0]
通过这种方式,您仍然可以访问,例如,my_locator.position
,您可以保留类型注释,不需要任何技巧,并且它是在一次位置定义的。(顺便说一句,这仍然与
dataclass
完全兼容;我只是在这里将其删除,因为它仅使用一个属性并没有做太多繁重的工作。)请注意,通过此设置,属性是“惰性”的;在收到请求之前不会计算它们。
我唯一改变的另一件事是我还删除了属性字符串的重复定义。