Attribute Access Machinery of New Style Objects/Classes

Attribute access of form obj.d triggers the special method type(obj).__getattribute__ to be invoked.

Thus, for objects, it is object.__getattribute__ that defines the general machinery of attribute access. The implementation in python code is as follows:

def __getattribute__(obj, name):
    cls = type(obj)
    class_attr = class_lookup(cls, name)
    # data descriptor
    if class_attr and is_data_desc(class_attr):
        return class_attr.__get__(obj, cls)

    # object attribute
    if name in obj.__dict__:
        return obj.__dict__[name]

    if class_attr:
        # non-data descriptor
        if is_desc(class_attr)
            return class_attr.__get__(obj, cls)
        # ordinary class attribute
        else:
            return class_attr

    raise AttributeError(name)

For classes, it is type.__getattribute__ that defines the general machinery of attribute access. The implementation in python code is as follows:

def __getattribute__(cls, name):
    meta_cls = type(cls)
    meta_attr = class_lookup(meta_cls, name)
    # data descriptor in metaclass
    if meta_attr and is_data_desc(meta_attr):
        return meta_attr.__get__(cls, meta_cls)

    class_attr = class_lookup(cls, name)
    if class_attr:
        # descriptor
        if is_desc(class_attr):
            return class_attr.__get__(None, cls)
        # ordinary class attribute
        else:
            return class_attr

    if meta_attr:
        # non-data descriptor in metaclass
        if is_desc(meta_attr):
            return meta_attr.__get__(cls, meta_cls)
        # ordinary meta attribute
        else:
            return meta_attr

    raise AttributeError(name)

As the cooperative base class attribute access mechanism, super has a custom __getattribute__ method. The implementation in python code is as follows:

def __getattribute__(self, name):
    # unbound super object, or class of super object is queried
    if self.__self__ is None or name == '__class__':
        return object.__getattribute__(self, name)

    starttype = self.__self_class__
    mro = iter(starttype.__mro__)
    for cls in mro:
        if cls is self.__thistype__:
            break
    # Note: mro is an iterator, so the second loop
    # picks up where the first one left off!
    for cls in mro:
        if name in cls.__dict__:
            x = cls.__dict__[name]
            if is_desc(x):
                # only pass 'obj' param if this is instance-mode super
                return x.__get__(None if self.__self__ is starttype else self.__self__, starttype)
            else:
                return x

    # fall back on the generic attribute access
    return object.__getattribute__(self, name)

References


Leave a Comment