
Today I learned about setattr in python
- Nested attributes cannot be set by setattr -> This creates problems when working with pytorch and replacing model weights
Example:
def replace(model: nn.Module, new_layers: dict[str, nn.Linear]) -> nn.Module:
for name, modules in model.named_modules():
if name in new_layers:
setattr(model, name, new_layers[name])
This code does not do what you think it should do. Why? Well, Python allows .
in attribute names. Here is a recent thread discussing this
To illustrate the problem, consider the following code:
class Potato:
size = 100
class Casserole:
potato = Potato()
broth = "Yum!"
def __repr__(self):
return f"Casserole(potato.size={self.potato.size}, broth={self.broth})"
>>> class Potato:
... size = 100
... class Casserole:
... potato = Potato()
... broth = "Yum!"
...
>>> stew = Casserole()
>>> getattr(stew, "potato.size")
Traceback (most recent call last):
File "<python-input-10>", line 1, in <module>
getattr(stew, "potato.size")
~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Casserole' object has no attribute 'potato.size'
>>> stew.potato.size
100
Clearly, this does not work. What’s the problem? We get an attribute error and get on with our coding lives and accept the fact that we have to do extra coding to get around getting deeply nested objects. For example, this code solves the problem (stolen solution from the link):
import functools
def deep_getattr(obj: Any, attr: str) -> Any:
return functools.reduce(getattr, attr.split('.'), obj)
However, check this out:
>>> setattr(stew, "potato.size", 9001)
>>>
No exception. Nothing went wrong? Let’s inspect the object
>>> stew.potato.size
100
>>> setattr(stew, "potato.size", 9001)
>>> stew.__dict__
{'potato.size': 9001}
>>> stew.potato.size
100
That is a silent error and it happens because having . in attribute names is
valid Python. This is, from a user-perspective of Python, not nice. It is
confusing. But now I know and you do too!