如何使用数据类对象内的列表动态设置对象属性?

问题描述 投票:0回答:2

目前我的代码在某些方面看起来确实重复:

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 # 

使用数据类最简洁的方法是什么?

python maya python-dataclasses
2个回答
0
投票

有问题的部分似乎是这样的:

@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)

我可以看到这里出现了两件事:

  1. 由于您在方法外部迭代 _node_attrs,因此您是在数据类装饰器有机会处理该类之前对值进行操作。因此,您获得的不是 _node_attrs 的默认值,而是包装它的原始字段对象,但不是以一种让您可以通过迭代直接与该值交互的方式。
  2. 您将生成的名称分配给 globals() 而不是 locals(),这不会影响类范围,因此 dataclass 将看不到这些属性。此外,当您分配给字典条目时,类型注释不会被保存,除非您在 __annotations__ 中显式分配注释。
这可能有用:

@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
    

0
投票
这里有一点框架挑战...如果您使用

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

 完全兼容;我只是在这里将其删除,因为它仅使用一个属性并没有做太多繁重的工作。)

请注意,通过此设置,属性是“惰性”的;在收到请求之前不会计算它们。

我唯一改变的另一件事是我还删除了属性字符串的重复定义。

© www.soinside.com 2019 - 2024. All rights reserved.