{"record":{"id":"230ff7c2ba1e4411","repo":"aosabook/500lines","slug":"name","errorCode":null,"errorMessage":"{name}","messagePattern":"\\{name\\}","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"objmodel/objmodel.markdown","lineNumber":588,"sourceCode":"\nThe case of ``__setattr__`` is a bit different. Since setting an attribute\nalways creates it, \\newline ``__setattr__`` is always called when setting an\nattribute.  To make sure that a ``__setattr__`` method always exists, the\n``OBJECT`` class has a definition of ``__setattr__``. This base implementation\nsimply does what setting an attribute did so far, which is write the attribute\ninto the object's dictionary. This also makes it possible for a user-defined\n``__setattr__`` to delegate to the base ``OBJECT.__setattr__`` in some cases.\n\nA test for these two special methods is the following:\n\n```python\ndef test_getattr():\n    # Python code\n    class A(object):\n        def __getattr__(self, name):\n            if name == \"fahrenheit\":\n                return self.celsius * 9. / 5. + 32\n            raise AttributeError(name)\n\n        def __setattr__(self, name, value):\n            if name == \"fahrenheit\":\n                self.celsius = (value - 32) * 5. / 9.\n            else:\n                # call the base implementation\n                object.__setattr__(self, name, value)\n    obj = A()\n    obj.celsius = 30\n    assert obj.fahrenheit == 86 # test __getattr__\n    obj.celsius = 40\n    assert obj.fahrenheit == 104\n\n    obj.fahrenheit = 86 # test __setattr__\n    assert obj.celsius == 30\n    assert obj.fahrenheit == 86\n\n    # Object model code","sourceCodeStart":570,"sourceCodeEnd":606,"githubUrl":"https://github.com/aosabook/500lines/blob/fba689d101eb5600f5c8f4d7fd79912498e950e2/objmodel/objmodel.markdown#L570-L606","documentation":"Raised inside a Python (native) __getattr__ demonstration in the objmodel chapter. The example class A defines __getattr__ only to synthesise 'fahrenheit' from 'celsius'; for every other name it re-raises AttributeError(name). This is the idiomatic Python pattern: __getattr__ must raise AttributeError for names it does not handle so that hasattr and the attribute protocol behave correctly.","triggerScenarios":"Accessing any attribute on A other than 'fahrenheit' when that attribute was never set via normal assignment/__setattr__. Because __getattr__ only intercepts missing attributes, already-set names (like celsius) resolve normally and do not reach it.","commonSituations":"Calling getattr(obj, 'some_unhandled_name') or referencing an uninitialised field; hasattr checks against an object whose __getattr__ only handles a subset of names; serialisation that probes many attribute names.","solutions":["Only read attributes the __getattr__ explicitly handles or that were set beforehand.","Extend __getattr__ to compute or default the additional names you need.","Ensure __getattr__ always re-raises AttributeError(name) for truly unknown names (do not return None).","Use getattr(obj, name, default) to supply a fallback instead of letting it raise."],"exampleFix":"# before\nclass A(object):\n    def __getattr__(self, name):\n        if name == 'fahrenheit':\n            return self.celsius * 9. / 5. + 32\n        raise AttributeError(name)\n\nA().kelvin   # -> AttributeError('kelvin')\n\n# after: handle the new computed name too\ndef __getattr__(self, name):\n    if name == 'fahrenheit':\n        return self.celsius * 9. / 5. + 32\n    if name == 'kelvin':\n        return self.celsius + 273.15\n    raise AttributeError(name)","handlingStrategy":"validation","validationCode":"ALLOWED = {'fahrenheit', 'celsius'}\nname = 'kelvin'\nif name not in ALLOWED and name not in obj.__dict__:\n    raise AttributeError(name)\ngetattr(obj, name)","typeGuard":"def handled_by_getattr(name):\n    return name == 'fahrenheit' or name in {'celsius'}","tryCatchPattern":"try:\n    val = obj.fahrenheit_equivalent\nexcept AttributeError:\n    val = None","preventionTips":["Always re-raise AttributeError(name) in __getattr__ for unhandled names.","Use getattr(obj, name, default) for optional attributes.","Document the set of names __getattr__ synthesises.","Prefer __init__ defaults for storage-backed fields over __getattr__ magic."],"tags":[],"backgroundTag":null,"analyzedSha":"fba689d101eb5600f5c8f4d7fd79912498e950e2","analyzedAt":"2026-08-13T06:26:32.792Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}