def __setattr__(selfattrvalue)print('setattrif attr ='name'attr '_nameself __dict__[attrvalue on [obj any valuedef __delattr__(selfattr)print('delattrif attr ='name'attr '_namedel self __dict__[attron [del obj anybob person('bob smith'print(bob namebob name 'robert smithprint(bob namedel bob name print('-'* sue person('sue jones'print(sue name#print(person name __doc__set internal name avoid looping here avoid looping here too but much less common bob has managed attribute runs __getattr__ runs __setattr__ runs __delattr__ sue inherits property too no equivalent here notice that the attribute assignment in the __init__ constructor triggers __setattr__ too--this method catches every attribute assignmenteven those anywhere within the class itself when this code is runthe same output is producedbut this time it' the result of python' normal operator overloading mechanism and our attribute interception methodsc:\codepy - getattr-person py set_name getname bob smith setname getname robert smith delname set_name getname sue jones also note thatunlike with properties and descriptorsthere' no direct notion of specifying documentation for our attribute heremanaged attributes exist within the code of our interception methodsnot as distinct objects using __getattribute__ to achieve exactly the same results with __getattribute__replace __getattr__ in the example with the followingbecause it catches all attribute fetchesthis version must managed attributes