Question presented to candidate:
"When you write obj.name in Python, where does the value actually come from? Walk me through the order Python checks things in, and explain why an instance attribute can hide a method but cannot hide a property."
What a strong answer should cover:
obj.namecallstype(obj).__getattribute__(obj, "name"); for ordinary classes that isobject.__getattribute__, and__getattr__is called only if it raisesAttributeError.- The precedence: a data descriptor found on the class (defines
__set__or__delete__, such asproperty) wins; then the instance__dict__; then a non-data descriptor or plain class attribute found by walkingtype(obj).__mro__; then__getattr__; elseAttributeError. - Why methods can be shadowed: a function is a non-data descriptor (only
__get__), so an instance-dict entry with the same name wins;propertyis a data descriptor, so it does not. - Assignment is different from reading:
obj.x = vgoes to a data descriptor's__set__if one exists, otherwise straight intoobj.__dict__(which is whyself.count += 1on a class attribute creates an instance copy). - Implicit special-method lookups (
len(obj),a + b) go to the type and skip the instance dict and__getattr__;__slots__replaces the instance dict with descriptors on the class.
Clarifying questions expected:
- Do you want normal instance lookup only, or also lookup on a class object (which uses
type.__getattribute__and the metaclass)?
Code / implementation expected: Optional β a small class showing a data descriptor beating the instance dict and the instance dict beating a method.