OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

How to dynamically set object attributes using a lists inside a dataclass object

  • Thread starter Thread starter Vitor Augusto Mariano Silva
  • Start date Start date
V

Vitor Augusto Mariano Silva

Guest
Currently I have this code that looks really repetitive in some areas:

Code:
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]

Same in the section

Code:
    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)

And

Code:
        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]

The terms ['normal','normalizedNormal','tangent','normalizedTangent'] are repeated and in case I wanted to add a new attribute I would need to modify a lot of lines.

I tried creating a list inside the class, so I could add an attribute only by adding it to the list:

_node_attrs:list = field(default_factory=['normal','normalizedNormal','tangent','normalizedTangent'])

Code:
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
        ]

If the list is a Field I got the error:

Code:
# 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 #

but in case I change to a simple list I got

Code:
# 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 #

Whats the cleanest way to do it using dataclass?
<p>Currently I have this code that looks really repetitive in some areas:</p>
<pre class="lang-py prettyprint-override"><code>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]
</code></pre>
<p>Same in the section</p>
<pre><code> 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)
</code></pre>
<p>And</p>
<pre class="lang-py prettyprint-override"><code> 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]
</code></pre>
<p>The terms ['normal','normalizedNormal','tangent','normalizedTangent'] are repeated and in case I wanted to add a new attribute I would need to modify a lot of lines.</p>
<p>I tried creating a list inside the class, so I could add an attribute only by adding it to the list:</p>
<p><code>_node_attrs:list = field(default_factory=['normal','normalizedNormal','tangent','normalizedTangent'])</code></p>
<pre class="lang-py prettyprint-override"><code>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
]
</code></pre>
<p>If the list is a Field I got the error:</p>
<pre class="lang-py prettyprint-override"><code># 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 #
</code></pre>
<p>but in case I change to a simple list I got</p>
<pre class="lang-py prettyprint-override"><code># 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 #
</code></pre>
<p>Whats the cleanest way to do it using dataclass?</p>
 

Latest posts

I
Replies
0
Views
1
impact christian
I
Top