Hướng dẫn dùng python getattribute python

Currently

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2 and
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 peek in the
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 of classes on the MRO for a class when looking for an attribute. This PEP adds an optional
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method to a metaclass that replaces this behavior and gives more control over attribute lookup, especially when using a super object.

That is, the MRO walking loop in

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
6 and
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 gets changed from:

def lookup[mro_list, name]:
    for cls in mro_list:
        if name in cls.__dict__:
            return cls.__dict__

    return NotFound

to:

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound

The default implementation of

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 looks in the class dictionary:

class type:
   def __getdescriptor__[cls, name]:
       try:
           return cls.__dict__[name]
       except KeyError:
           raise AttributeError[name] from None

PEP Status

This PEP is deferred until someone has time to update this PEP and push it forward.

Rationale

It is currently not possible to influence how the super class looks up attributes [that is,

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 unconditionally peeks in the class
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4], and that can be problematic for dynamic classes that can grow new methods on demand, for example dynamic proxy classes.

The

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method makes it possible to dynamically add attributes even when looking them up using the super class.

The new method affects

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2 [and PyObject_GenericGetAttr] as well for consistency and to have a single place to implement dynamic attribute resolution for classes.

Background

The current behavior of

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 causes problems for classes that are dynamic proxies for other [non-Python] classes or types, an example of which is PyObjC. PyObjC creates a Python class for every class in the Objective-C runtime, and looks up methods in the Objective-C runtime when they are used. This works fine for normal access, but doesn’t work for access with super objects. Because of this PyObjC currently includes a custom super that must be used with its classes, as well as completely reimplementing PyObject_GenericGetAttr for normal attribute access.

The API in this PEP makes it possible to remove the custom super and simplifies the implementation because the custom lookup behavior can be added in a central location.

Note

PyObjC cannot precalculate the contents of the class

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 because Objective-C classes can grow new methods at runtime. Furthermore, Objective-C classes tend to contain a lot of methods while most Python code will only use a small subset of them, this makes precalculating unnecessarily expensive.

The superclass attribute lookup hook

Both

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 and
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2 [or PyObject_GenericGetAttr and in particular
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
6 in C code] walk an object’s MRO and currently peek in the class’
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 to look up attributes.

With this proposal both lookup methods no longer peek in the class

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 but call the special method
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5, which is a slot defined on the metaclass. The default implementation of that method looks up the name the class
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4, which means that attribute lookup is unchanged unless a metatype actually defines the new special method.

Aside: Attribute resolution algorithm in Python

The attribute resolution process as implemented by

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2 [or
def _PyType_Lookup[tp, name]:
    mro = tp.mro[]
    assert isinstance[mro, tuple]

    for base in mro:
       assert isinstance[base, type]

       # PEP 447 will change these lines:
       try:
           return base.__dict__[name]
       except KeyError:
           pass

    return None


class object:
    def __getattribute__[self, name]:
        assert isinstance[name, str]

        tp = type[self]
        descr = _PyType_Lookup[tp, name]

        f = None
        if descr is not None:
            f = descr.__get__
            if f is not None and descr.__set__ is not None:
                # Data descriptor
                return f[descr, self, type[self]]

        dict = self.__dict__
        if dict is not None:
            try:
                return self.__dict__[name]
            except KeyError:
                pass

        if f is not None:
            # Non-data descriptor
            return f[descr, self, type[self]]

        if descr is not None:
            # Regular class attribute
            return descr

        raise AttributeError[name]


class super:
    def __getattribute__[self, name]:
       assert isinstance[name, unicode]

       if name != '__class__':
           starttype = self.__self_type__
           mro = startype.mro[]

           try:
               idx = mro.index[self.__thisclass__]

           except ValueError:
               pass

           else:
               for base in mro[idx+1:]:
                   # PEP 447 will change these lines:
                   try:
                       descr = base.__dict__[name]
                   except KeyError:
                       continue

                   f = descr.__get__
                   if f is not None:
                       return f[descr,
                           None if [self.__self__ is self.__self_type__] else self.__self__,
                           starttype]

                   else:
                       return descr

       return object.__getattribute__[self, name]
3 in CPython’s implementation] is fairly straightforward, but not entirely so without reading C code.

The current CPython implementation of object.__getattribute__ is basically equivalent to the following [pseudo-] Python code [excluding some house keeping and speed tricks]:

def _PyType_Lookup[tp, name]:
    mro = tp.mro[]
    assert isinstance[mro, tuple]

    for base in mro:
       assert isinstance[base, type]

       # PEP 447 will change these lines:
       try:
           return base.__dict__[name]
       except KeyError:
           pass

    return None


class object:
    def __getattribute__[self, name]:
        assert isinstance[name, str]

        tp = type[self]
        descr = _PyType_Lookup[tp, name]

        f = None
        if descr is not None:
            f = descr.__get__
            if f is not None and descr.__set__ is not None:
                # Data descriptor
                return f[descr, self, type[self]]

        dict = self.__dict__
        if dict is not None:
            try:
                return self.__dict__[name]
            except KeyError:
                pass

        if f is not None:
            # Non-data descriptor
            return f[descr, self, type[self]]

        if descr is not None:
            # Regular class attribute
            return descr

        raise AttributeError[name]


class super:
    def __getattribute__[self, name]:
       assert isinstance[name, unicode]

       if name != '__class__':
           starttype = self.__self_type__
           mro = startype.mro[]

           try:
               idx = mro.index[self.__thisclass__]

           except ValueError:
               pass

           else:
               for base in mro[idx+1:]:
                   # PEP 447 will change these lines:
                   try:
                       descr = base.__dict__[name]
                   except KeyError:
                       continue

                   f = descr.__get__
                   if f is not None:
                       return f[descr,
                           None if [self.__self__ is self.__self_type__] else self.__self__,
                           starttype]

                   else:
                       return descr

       return object.__getattribute__[self, name]

This PEP should change the dict lookup at the lines starting at “# PEP 447” with a method call to perform the actual lookup, making is possible to affect that lookup both for normal attribute access and access through the super proxy.

Note that specific classes can already completely override the default behaviour by implementing their own

def _PyType_Lookup[tp, name]:
    mro = tp.mro[]
    assert isinstance[mro, tuple]

    for base in mro:
       assert isinstance[base, type]

       # PEP 447 will change these lines:
       try:
           return base.__dict__[name]
       except KeyError:
           pass

    return None


class object:
    def __getattribute__[self, name]:
        assert isinstance[name, str]

        tp = type[self]
        descr = _PyType_Lookup[tp, name]

        f = None
        if descr is not None:
            f = descr.__get__
            if f is not None and descr.__set__ is not None:
                # Data descriptor
                return f[descr, self, type[self]]

        dict = self.__dict__
        if dict is not None:
            try:
                return self.__dict__[name]
            except KeyError:
                pass

        if f is not None:
            # Non-data descriptor
            return f[descr, self, type[self]]

        if descr is not None:
            # Regular class attribute
            return descr

        raise AttributeError[name]


class super:
    def __getattribute__[self, name]:
       assert isinstance[name, unicode]

       if name != '__class__':
           starttype = self.__self_type__
           mro = startype.mro[]

           try:
               idx = mro.index[self.__thisclass__]

           except ValueError:
               pass

           else:
               for base in mro[idx+1:]:
                   # PEP 447 will change these lines:
                   try:
                       descr = base.__dict__[name]
                   except KeyError:
                       continue

                   f = descr.__get__
                   if f is not None:
                       return f[descr,
                           None if [self.__self__ is self.__self_type__] else self.__self__,
                           starttype]

                   else:
                       return descr

       return object.__getattribute__[self, name]
4 slot [with or without calling the super class implementation].

In Python code

A meta type can define a method

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 that is called during attribute resolution by both
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 and
def _PyType_Lookup[tp, name]:
    mro = tp.mro[]
    assert isinstance[mro, tuple]

    for base in mro:
       assert isinstance[base, type]

       # PEP 447 will change these lines:
       try:
           return base.__dict__[name]
       except KeyError:
           pass

    return None


class object:
    def __getattribute__[self, name]:
        assert isinstance[name, str]

        tp = type[self]
        descr = _PyType_Lookup[tp, name]

        f = None
        if descr is not None:
            f = descr.__get__
            if f is not None and descr.__set__ is not None:
                # Data descriptor
                return f[descr, self, type[self]]

        dict = self.__dict__
        if dict is not None:
            try:
                return self.__dict__[name]
            except KeyError:
                pass

        if f is not None:
            # Non-data descriptor
            return f[descr, self, type[self]]

        if descr is not None:
            # Regular class attribute
            return descr

        raise AttributeError[name]


class super:
    def __getattribute__[self, name]:
       assert isinstance[name, unicode]

       if name != '__class__':
           starttype = self.__self_type__
           mro = startype.mro[]

           try:
               idx = mro.index[self.__thisclass__]

           except ValueError:
               pass

           else:
               for base in mro[idx+1:]:
                   # PEP 447 will change these lines:
                   try:
                       descr = base.__dict__[name]
                   except KeyError:
                       continue

                   f = descr.__get__
                   if f is not None:
                       return f[descr,
                           None if [self.__self__ is self.__self_type__] else self.__self__,
                           starttype]

                   else:
                       return descr

       return object.__getattribute__[self, name]
7:

class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None

The

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method has as its arguments a class [which is an instance of the meta type] and the name of the attribute that is looked up. It should return the value of the attribute without invoking descriptors, and should raise AttributeError when the name cannot be found.

The type class provides a default implementation for

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5, that looks up the name in the class dictionary.

Example usage

The code below implements a silly metaclass that redirects attribute lookup to uppercase versions of names:

class UpperCaseAccess [type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name.upper[]]
        except KeyError:
            raise AttributeError[name] from None

class SillyObject [metaclass=UpperCaseAccess]:
    def m[self]:
        return 42

    def M[self]:
        return "fortytwo"

obj = SillyObject[]
assert obj.m[] == "fortytwo"

As mentioned earlier in this PEP a more realistic use case of this functionality is a

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method that dynamically populates the class
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 based on attribute access, primarily when it is not possible to reliably keep the class dict in sync with its source, for example because the source used to populate
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 is dynamic as well and does not have triggers that can be used to detect changes to that source.

An example of that are the class bridges in PyObjC: the class bridge is a Python object [class] that represents an Objective-C class and conceptually has a Python method for every Objective-C method in the Objective-C class. As with Python it is possible to add new methods to an Objective-C class, or replace existing ones, and there are no callbacks that can be used to detect this.

In C code

A new type flag

class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
3 with value
class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
4 that indicates that the new slot is present and to be used.

A new slot

class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
5 is added to the
class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
6 struct, this slot corresponds to the
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method on type.

The slot has the following prototype:

PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];

This method should lookup name in the namespace of cls, without looking at superclasses, and should not invoke descriptors. The method returns

class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
8 without setting an exception when the name cannot be found, and returns a new reference otherwise [not a borrowed reference].

Classes with a

class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
5 slot must add
class MetaType[type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name]
        except KeyError:
            raise AttributeError[name] from None
3 to
class UpperCaseAccess [type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name.upper[]]
        except KeyError:
            raise AttributeError[name] from None

class SillyObject [metaclass=UpperCaseAccess]:
    def m[self]:
        return 42

    def M[self]:
        return "fortytwo"

obj = SillyObject[]
assert obj.m[] == "fortytwo"
1 to indicate that new slot must be used.

Use of this hook by the interpreter

The new method is required for metatypes and as such is defined on type_. Both

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3 and
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2/PyObject_GenericGetAttr [through
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
6] use the this
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method when walking the MRO.

Other changes to the implementation

The change for PyObject_GenericGetAttr will be done by changing the private function

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
6. This currently returns a borrowed reference, but must return a new reference when the
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method is present. Because of this
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
6 will be renamed to
class UpperCaseAccess [type]:
    def __getdescriptor__[cls, name]:
        try:
            return cls.__dict__[name.upper[]]
        except KeyError:
            raise AttributeError[name] from None

class SillyObject [metaclass=UpperCaseAccess]:
    def m[self]:
        return 42

    def M[self]:
        return "fortytwo"

obj = SillyObject[]
assert obj.m[] == "fortytwo"
9, this will cause compile-time errors for all out-of-tree users of this private API.

For the same reason

PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];
0 is renamed to
PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];
1. A number of other functions in typeobject.c with the same issue do not get an updated name because they are private to that file.

The attribute lookup cache in

PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];
2 is disabled for classes that have a metaclass that overrides
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5, because using the cache might not be valid for such classes.

Impact of this PEP on introspection

Use of the method introduced in this PEP can affect introspection of classes with a metaclass that uses a custom

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method. This section lists those changes.

The items listed below are only affected by custom

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 methods, the default implementation for
PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];
6 won’t cause problems because that still only uses the class
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 and won’t cause visible changes to the visible behaviour of the
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
2.

  • PyObject* [*getdescriptorfunc][PyTypeObject* cls, PyObject* name];
    
    9 might not show all attributes

    As with a custom

    def _PyType_Lookup[tp, name]:
        mro = tp.mro[]
        assert isinstance[mro, tuple]
    
        for base in mro:
           assert isinstance[base, type]
    
           # PEP 447 will change these lines:
           try:
               return base.__dict__[name]
           except KeyError:
               pass
    
        return None
    
    
    class object:
        def __getattribute__[self, name]:
            assert isinstance[name, str]
    
            tp = type[self]
            descr = _PyType_Lookup[tp, name]
    
            f = None
            if descr is not None:
                f = descr.__get__
                if f is not None and descr.__set__ is not None:
                    # Data descriptor
                    return f[descr, self, type[self]]
    
            dict = self.__dict__
            if dict is not None:
                try:
                    return self.__dict__[name]
                except KeyError:
                    pass
    
            if f is not None:
                # Non-data descriptor
                return f[descr, self, type[self]]
    
            if descr is not None:
                # Regular class attribute
                return descr
    
            raise AttributeError[name]
    
    
    class super:
        def __getattribute__[self, name]:
           assert isinstance[name, unicode]
    
           if name != '__class__':
               starttype = self.__self_type__
               mro = startype.mro[]
    
               try:
                   idx = mro.index[self.__thisclass__]
    
               except ValueError:
                   pass
    
               else:
                   for base in mro[idx+1:]:
                       # PEP 447 will change these lines:
                       try:
                           descr = base.__dict__[name]
                       except KeyError:
                           continue
    
                       f = descr.__get__
                       if f is not None:
                           return f[descr,
                               None if [self.__self__ is self.__self_type__] else self.__self__,
                               starttype]
    
                       else:
                           return descr
    
           return object.__getattribute__[self, name]
    
    4 method dir[] might not see all [instance] attributes when using the
    -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    1 method to dynamically resolve attributes.

    The solution for that is quite simple: classes using

    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    5 should also implement __dir__[] if they want full support for the builtin dir[] function.

  • -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    3 might not show all attributes

    The function

    -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    3 intentionally does not invoke
    def _PyType_Lookup[tp, name]:
        mro = tp.mro[]
        assert isinstance[mro, tuple]
    
        for base in mro:
           assert isinstance[base, type]
    
           # PEP 447 will change these lines:
           try:
               return base.__dict__[name]
           except KeyError:
               pass
    
        return None
    
    
    class object:
        def __getattribute__[self, name]:
            assert isinstance[name, str]
    
            tp = type[self]
            descr = _PyType_Lookup[tp, name]
    
            f = None
            if descr is not None:
                f = descr.__get__
                if f is not None and descr.__set__ is not None:
                    # Data descriptor
                    return f[descr, self, type[self]]
    
            dict = self.__dict__
            if dict is not None:
                try:
                    return self.__dict__[name]
                except KeyError:
                    pass
    
            if f is not None:
                # Non-data descriptor
                return f[descr, self, type[self]]
    
            if descr is not None:
                # Regular class attribute
                return descr
    
            raise AttributeError[name]
    
    
    class super:
        def __getattribute__[self, name]:
           assert isinstance[name, unicode]
    
           if name != '__class__':
               starttype = self.__self_type__
               mro = startype.mro[]
    
               try:
                   idx = mro.index[self.__thisclass__]
    
               except ValueError:
                   pass
    
               else:
                   for base in mro[idx+1:]:
                       # PEP 447 will change these lines:
                       try:
                           descr = base.__dict__[name]
                       except KeyError:
                           continue
    
                       f = descr.__get__
                       if f is not None:
                           return f[descr,
                               None if [self.__self__ is self.__self_type__] else self.__self__,
                               starttype]
    
                       else:
                           return descr
    
           return object.__getattribute__[self, name]
    
    4 and descriptors to avoid invoking user code during introspection with this function. The
    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    5 method will also be ignored and is another way in which the result of
    -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    3 can be different from that of
    -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    8.

  • -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    9 and
    Report on Linux fangorn.local 2.6.32-358.114.1.openstack.el6.x86_64 #1 SMP Wed Jul 3 02:11:25 EDT 2013 x86_64 x86_64
    Total CPU cores: 8
    
    ### call_method_slots ###
    Min: 0.304120 -> 0.282791: 1.08x faster
    Avg: 0.304394 -> 0.282906: 1.08x faster
    Significant [t=2329.92]
    Stddev: 0.00016 -> 0.00004: 4.1814x smaller
    
    ### call_simple ###
    Min: 0.249268 -> 0.221175: 1.13x faster
    Avg: 0.249789 -> 0.221387: 1.13x faster
    Significant [t=2770.11]
    Stddev: 0.00012 -> 0.00013: 1.1101x larger
    
    ### django_v2 ###
    Min: 0.632590 -> 0.601519: 1.05x faster
    Avg: 0.635085 -> 0.602653: 1.05x faster
    Significant [t=321.32]
    Stddev: 0.00087 -> 0.00051: 1.6933x smaller
    
    ### fannkuch ###
    Min: 1.033181 -> 0.999779: 1.03x faster
    Avg: 1.036457 -> 1.001840: 1.03x faster
    Significant [t=260.31]
    Stddev: 0.00113 -> 0.00070: 1.6112x smaller
    
    ### go ###
    Min: 0.526714 -> 0.544428: 1.03x slower
    Avg: 0.529649 -> 0.547626: 1.03x slower
    Significant [t=-93.32]
    Stddev: 0.00136 -> 0.00136: 1.0028x smaller
    
    ### iterative_count ###
    Min: 0.109748 -> 0.116513: 1.06x slower
    Avg: 0.109816 -> 0.117202: 1.07x slower
    Significant [t=-357.08]
    Stddev: 0.00008 -> 0.00019: 2.3664x larger
    
    ### json_dump_v2 ###
    Min: 2.554462 -> 2.609141: 1.02x slower
    Avg: 2.564472 -> 2.620013: 1.02x slower
    Significant [t=-76.93]
    Stddev: 0.00538 -> 0.00481: 1.1194x smaller
    
    ### meteor_contest ###
    Min: 0.196336 -> 0.191925: 1.02x faster
    Avg: 0.196878 -> 0.192698: 1.02x faster
    Significant [t=61.86]
    Stddev: 0.00053 -> 0.00041: 1.2925x smaller
    
    ### nbody ###
    Min: 0.228039 -> 0.235551: 1.03x slower
    Avg: 0.228857 -> 0.236052: 1.03x slower
    Significant [t=-54.15]
    Stddev: 0.00130 -> 0.00029: 4.4810x smaller
    
    ### pathlib ###
    Min: 0.108501 -> 0.105339: 1.03x faster
    Avg: 0.109084 -> 0.105619: 1.03x faster
    Significant [t=311.08]
    Stddev: 0.00022 -> 0.00011: 1.9314x smaller
    
    ### regex_effbot ###
    Min: 0.057905 -> 0.056447: 1.03x faster
    Avg: 0.058055 -> 0.056760: 1.02x faster
    Significant [t=79.22]
    Stddev: 0.00006 -> 0.00015: 2.7741x larger
    
    ### silent_logging ###
    Min: 0.070810 -> 0.072436: 1.02x slower
    Avg: 0.070899 -> 0.072609: 1.02x slower
    Significant [t=-191.59]
    Stddev: 0.00004 -> 0.00008: 2.2640x larger
    
    ### spectral_norm ###
    Min: 0.290255 -> 0.299286: 1.03x slower
    Avg: 0.290335 -> 0.299541: 1.03x slower
    Significant [t=-572.10]
    Stddev: 0.00005 -> 0.00015: 2.8547x larger
    
    ### threaded_count ###
    Min: 0.107215 -> 0.115206: 1.07x slower
    Avg: 0.107488 -> 0.115996: 1.08x slower
    Significant [t=-109.39]
    Stddev: 0.00016 -> 0.00076: 4.8665x larger
    
    The following not significant results are hidden, use -v to show them:
    call_method, call_method_unknown, chaos, fastpickle, fastunpickle, float, formatted_logging, hexiom2, json_load, normal_startup, nqueens, pidigits, raytrace, regex_compile, regex_v8, richards, simple_logging, startup_nosite, telco, unpack_sequence.
    
    0

    Both of these functions directly access the class __dict__ of classes along the MRO, and hence can be affected by a custom

    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    5 method.

    Code with a custom

    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    5 method that want to play nice with these methods also needs to ensure that the
    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    4 is set up correctly when that is accessed directly by Python code.

    Note that

    -------------------------------------------------------------------------------
    PYBENCH 2.1
    -------------------------------------------------------------------------------
    * using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
    * disabled garbage collection
    * system check interval set to maximum: 2147483647
    * using timer: time.perf_counter
    * timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]
    
    -------------------------------------------------------------------------------
    Benchmark: pep447.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default-pep447/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 14:09:12 [#default]
           Unicode:        UCS4
    
    
    -------------------------------------------------------------------------------
    Comparing with: default.pybench
    -------------------------------------------------------------------------------
    
        Rounds: 10
        Warp:   10
        Timer:  time.perf_counter
    
        Machine Details:
           Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
           Processor:      x86_64
    
        Python:
           Implementation: CPython
           Executable:     /tmp/default/bin/python3
           Version:        3.4.0a0
           Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
           Bits:           64bit
           Build:          Jul 29 2013 13:01:34 [#default]
           Unicode:        UCS4
    
    
    Test                             minimum run-time        average  run-time
                                     this    other   diff    this    other   diff
    -------------------------------------------------------------------------------
              BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
               BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                     CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
             CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
                   CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
            CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                      CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                    CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
        ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                     ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
                   CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
                CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
           CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                      DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
                 DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
               DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
                DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                          ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                        IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                       ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                    NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
          NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
              NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
           NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
               PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
                 PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                         Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                      SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
               SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
             SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
           SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
            SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
             SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
          SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
           SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
          SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
            SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
              SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                        SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                       SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
             SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
          SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                    StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
                  StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                     StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                         TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                        TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                    TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                      TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                       WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
                   WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
    -------------------------------------------------------------------------------
    Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%
    
    [this=pep447.pybench, other=default.pybench]
    
    9 is used by
    Report on Linux fangorn.local 2.6.32-358.114.1.openstack.el6.x86_64 #1 SMP Wed Jul 3 02:11:25 EDT 2013 x86_64 x86_64
    Total CPU cores: 8
    
    ### call_method_slots ###
    Min: 0.304120 -> 0.282791: 1.08x faster
    Avg: 0.304394 -> 0.282906: 1.08x faster
    Significant [t=2329.92]
    Stddev: 0.00016 -> 0.00004: 4.1814x smaller
    
    ### call_simple ###
    Min: 0.249268 -> 0.221175: 1.13x faster
    Avg: 0.249789 -> 0.221387: 1.13x faster
    Significant [t=2770.11]
    Stddev: 0.00012 -> 0.00013: 1.1101x larger
    
    ### django_v2 ###
    Min: 0.632590 -> 0.601519: 1.05x faster
    Avg: 0.635085 -> 0.602653: 1.05x faster
    Significant [t=321.32]
    Stddev: 0.00087 -> 0.00051: 1.6933x smaller
    
    ### fannkuch ###
    Min: 1.033181 -> 0.999779: 1.03x faster
    Avg: 1.036457 -> 1.001840: 1.03x faster
    Significant [t=260.31]
    Stddev: 0.00113 -> 0.00070: 1.6112x smaller
    
    ### go ###
    Min: 0.526714 -> 0.544428: 1.03x slower
    Avg: 0.529649 -> 0.547626: 1.03x slower
    Significant [t=-93.32]
    Stddev: 0.00136 -> 0.00136: 1.0028x smaller
    
    ### iterative_count ###
    Min: 0.109748 -> 0.116513: 1.06x slower
    Avg: 0.109816 -> 0.117202: 1.07x slower
    Significant [t=-357.08]
    Stddev: 0.00008 -> 0.00019: 2.3664x larger
    
    ### json_dump_v2 ###
    Min: 2.554462 -> 2.609141: 1.02x slower
    Avg: 2.564472 -> 2.620013: 1.02x slower
    Significant [t=-76.93]
    Stddev: 0.00538 -> 0.00481: 1.1194x smaller
    
    ### meteor_contest ###
    Min: 0.196336 -> 0.191925: 1.02x faster
    Avg: 0.196878 -> 0.192698: 1.02x faster
    Significant [t=61.86]
    Stddev: 0.00053 -> 0.00041: 1.2925x smaller
    
    ### nbody ###
    Min: 0.228039 -> 0.235551: 1.03x slower
    Avg: 0.228857 -> 0.236052: 1.03x slower
    Significant [t=-54.15]
    Stddev: 0.00130 -> 0.00029: 4.4810x smaller
    
    ### pathlib ###
    Min: 0.108501 -> 0.105339: 1.03x faster
    Avg: 0.109084 -> 0.105619: 1.03x faster
    Significant [t=311.08]
    Stddev: 0.00022 -> 0.00011: 1.9314x smaller
    
    ### regex_effbot ###
    Min: 0.057905 -> 0.056447: 1.03x faster
    Avg: 0.058055 -> 0.056760: 1.02x faster
    Significant [t=79.22]
    Stddev: 0.00006 -> 0.00015: 2.7741x larger
    
    ### silent_logging ###
    Min: 0.070810 -> 0.072436: 1.02x slower
    Avg: 0.070899 -> 0.072609: 1.02x slower
    Significant [t=-191.59]
    Stddev: 0.00004 -> 0.00008: 2.2640x larger
    
    ### spectral_norm ###
    Min: 0.290255 -> 0.299286: 1.03x slower
    Avg: 0.290335 -> 0.299541: 1.03x slower
    Significant [t=-572.10]
    Stddev: 0.00005 -> 0.00015: 2.8547x larger
    
    ### threaded_count ###
    Min: 0.107215 -> 0.115206: 1.07x slower
    Avg: 0.107488 -> 0.115996: 1.08x slower
    Significant [t=-109.39]
    Stddev: 0.00016 -> 0.00076: 4.8665x larger
    
    The following not significant results are hidden, use -v to show them:
    call_method, call_method_unknown, chaos, fastpickle, fastunpickle, float, formatted_logging, hexiom2, json_load, normal_startup, nqueens, pidigits, raytrace, regex_compile, regex_v8, richards, simple_logging, startup_nosite, telco, unpack_sequence.
    
    5 and hence this can affect runtime documentation introspection.

  • Direct introspection of the class
    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    4

    Any code that directly access the class

    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    4 for introspection can be affected by a custom
    def lookup[mro_list, name]:
        for cls in mro_list:
            try:
                return cls.__getdescriptor__[name]
            except AttributeError:
                pass
    
        return NotFound
    
    5 method, see the previous item.

Performance impact

WARNING: The benchmark results in this section are old, and will be updated when I’ve ported the patch to the current trunk. I don’t expect significant changes to the results in this section.

Micro benchmarks

Issue 18181 has a micro benchmark as one of its attachments [pep447-micro-bench.py] that specifically tests the speed of attribute lookup, both directly and through super.

Note that attribute lookup with deep class hierarchies is significantly slower when using a custom

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 method. This is because the attribute lookup cache for CPython cannot be used when having this method.

Pybench

The pybench output below compares an implementation of this PEP with the regular source tree, both based on changeset a5681f50bae2, run on an idle machine and Core i7 processor running Centos 6.4.

Even though the machine was idle there were clear differences between runs, I’ve seen difference in “minimum time” vary from -0.1% to +1.5%, with similar [but slightly smaller] differences in the “average time” difference.

-------------------------------------------------------------------------------
PYBENCH 2.1
-------------------------------------------------------------------------------
* using CPython 3.4.0a0 [default, Jul 29 2013, 13:01:34] [GCC 4.4.7 20120313 [Red Hat 4.4.7-3]]
* disabled garbage collection
* system check interval set to maximum: 2147483647
* using timer: time.perf_counter
* timer: resolution=1e-09, implementation=clock_gettime[CLOCK_MONOTONIC]

-------------------------------------------------------------------------------
Benchmark: pep447.pybench
-------------------------------------------------------------------------------

    Rounds: 10
    Warp:   10
    Timer:  time.perf_counter

    Machine Details:
       Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
       Processor:      x86_64

    Python:
       Implementation: CPython
       Executable:     /tmp/default-pep447/bin/python3
       Version:        3.4.0a0
       Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
       Bits:           64bit
       Build:          Jul 29 2013 14:09:12 [#default]
       Unicode:        UCS4


-------------------------------------------------------------------------------
Comparing with: default.pybench
-------------------------------------------------------------------------------

    Rounds: 10
    Warp:   10
    Timer:  time.perf_counter

    Machine Details:
       Platform ID:    Linux-2.6.32-358.114.1.openstack.el6.x86_64-x86_64-with-centos-6.4-Final
       Processor:      x86_64

    Python:
       Implementation: CPython
       Executable:     /tmp/default/bin/python3
       Version:        3.4.0a0
       Compiler:       GCC 4.4.7 20120313 [Red Hat 4.4.7-3]
       Bits:           64bit
       Build:          Jul 29 2013 13:01:34 [#default]
       Unicode:        UCS4


Test                             minimum run-time        average  run-time
                                 this    other   diff    this    other   diff
-------------------------------------------------------------------------------
          BuiltinFunctionCalls:    45ms    44ms   +1.3%    45ms    44ms   +1.3%
           BuiltinMethodLookup:    26ms    27ms   -2.4%    27ms    27ms   -2.2%
                 CompareFloats:    33ms    34ms   -0.7%    33ms    34ms   -1.1%
         CompareFloatsIntegers:    66ms    67ms   -0.9%    66ms    67ms   -0.8%
               CompareIntegers:    51ms    50ms   +0.9%    51ms    50ms   +0.8%
        CompareInternedStrings:    34ms    33ms   +0.4%    34ms    34ms   -0.4%
                  CompareLongs:    29ms    29ms   -0.1%    29ms    29ms   -0.0%
                CompareStrings:    43ms    44ms   -1.8%    44ms    44ms   -1.8%
    ComplexPythonFunctionCalls:    44ms    42ms   +3.9%    44ms    42ms   +4.1%
                 ConcatStrings:    33ms    33ms   -0.4%    33ms    33ms   -1.0%
               CreateInstances:    47ms    48ms   -2.9%    47ms    49ms   -3.4%
            CreateNewInstances:    35ms    36ms   -2.5%    36ms    36ms   -2.5%
       CreateStringsWithConcat:    69ms    70ms   -0.7%    69ms    70ms   -0.9%
                  DictCreation:    52ms    50ms   +3.1%    52ms    50ms   +3.0%
             DictWithFloatKeys:    40ms    44ms  -10.1%    43ms    45ms   -5.8%
           DictWithIntegerKeys:    32ms    36ms  -11.2%    35ms    37ms   -4.6%
            DictWithStringKeys:    29ms    34ms  -15.7%    35ms    40ms  -11.0%
                      ForLoops:    30ms    29ms   +2.2%    30ms    29ms   +2.2%
                    IfThenElse:    38ms    41ms   -6.7%    38ms    41ms   -6.9%
                   ListSlicing:    36ms    36ms   -0.7%    36ms    37ms   -1.3%
                NestedForLoops:    43ms    45ms   -3.1%    43ms    45ms   -3.2%
      NestedListComprehensions:    39ms    40ms   -1.7%    39ms    40ms   -2.1%
          NormalClassAttribute:    86ms    82ms   +5.1%    86ms    82ms   +5.0%
       NormalInstanceAttribute:    42ms    42ms   +0.3%    42ms    42ms   +0.0%
           PythonFunctionCalls:    39ms    38ms   +3.5%    39ms    38ms   +2.8%
             PythonMethodCalls:    51ms    49ms   +3.0%    51ms    50ms   +2.8%
                     Recursion:    67ms    68ms   -1.4%    67ms    68ms   -1.4%
                  SecondImport:    41ms    36ms  +12.5%    41ms    36ms  +12.6%
           SecondPackageImport:    45ms    40ms  +13.1%    45ms    40ms  +13.2%
         SecondSubmoduleImport:    92ms    95ms   -2.4%    95ms    98ms   -3.6%
       SimpleComplexArithmetic:    28ms    28ms   -0.1%    28ms    28ms   -0.2%
        SimpleDictManipulation:    57ms    57ms   -1.0%    57ms    58ms   -1.0%
         SimpleFloatArithmetic:    29ms    28ms   +4.7%    29ms    28ms   +4.9%
      SimpleIntFloatArithmetic:    37ms    41ms   -8.5%    37ms    41ms   -8.7%
       SimpleIntegerArithmetic:    37ms    41ms   -9.4%    37ms    42ms  -10.2%
      SimpleListComprehensions:    33ms    33ms   -1.9%    33ms    34ms   -2.9%
        SimpleListManipulation:    28ms    30ms   -4.3%    29ms    30ms   -4.1%
          SimpleLongArithmetic:    26ms    26ms   +0.5%    26ms    26ms   +0.5%
                    SmallLists:    40ms    40ms   +0.1%    40ms    40ms   +0.1%
                   SmallTuples:    46ms    47ms   -2.4%    46ms    48ms   -3.0%
         SpecialClassAttribute:   126ms   120ms   +4.7%   126ms   121ms   +4.4%
      SpecialInstanceAttribute:    42ms    42ms   +0.6%    42ms    42ms   +0.8%
                StringMappings:    94ms    91ms   +3.9%    94ms    91ms   +3.8%
              StringPredicates:    48ms    49ms   -1.7%    48ms    49ms   -2.1%
                 StringSlicing:    45ms    45ms   +1.4%    46ms    45ms   +1.5%
                     TryExcept:    23ms    22ms   +4.9%    23ms    22ms   +4.8%
                    TryFinally:    32ms    32ms   -0.1%    32ms    32ms   +0.1%
                TryRaiseExcept:    17ms    17ms   +0.9%    17ms    17ms   +0.5%
                  TupleSlicing:    49ms    48ms   +1.1%    49ms    49ms   +1.0%
                   WithFinally:    48ms    47ms   +2.3%    48ms    47ms   +2.4%
               WithRaiseExcept:    45ms    44ms   +0.8%    45ms    45ms   +0.5%
-------------------------------------------------------------------------------
Totals:                          2284ms  2287ms   -0.1%  2306ms  2308ms   -0.1%

[this=pep447.pybench, other=default.pybench]

A run of the benchmark suite [with option “-b 2n3”] also seems to indicate that the performance impact is minimal:

Report on Linux fangorn.local 2.6.32-358.114.1.openstack.el6.x86_64 #1 SMP Wed Jul 3 02:11:25 EDT 2013 x86_64 x86_64
Total CPU cores: 8

### call_method_slots ###
Min: 0.304120 -> 0.282791: 1.08x faster
Avg: 0.304394 -> 0.282906: 1.08x faster
Significant [t=2329.92]
Stddev: 0.00016 -> 0.00004: 4.1814x smaller

### call_simple ###
Min: 0.249268 -> 0.221175: 1.13x faster
Avg: 0.249789 -> 0.221387: 1.13x faster
Significant [t=2770.11]
Stddev: 0.00012 -> 0.00013: 1.1101x larger

### django_v2 ###
Min: 0.632590 -> 0.601519: 1.05x faster
Avg: 0.635085 -> 0.602653: 1.05x faster
Significant [t=321.32]
Stddev: 0.00087 -> 0.00051: 1.6933x smaller

### fannkuch ###
Min: 1.033181 -> 0.999779: 1.03x faster
Avg: 1.036457 -> 1.001840: 1.03x faster
Significant [t=260.31]
Stddev: 0.00113 -> 0.00070: 1.6112x smaller

### go ###
Min: 0.526714 -> 0.544428: 1.03x slower
Avg: 0.529649 -> 0.547626: 1.03x slower
Significant [t=-93.32]
Stddev: 0.00136 -> 0.00136: 1.0028x smaller

### iterative_count ###
Min: 0.109748 -> 0.116513: 1.06x slower
Avg: 0.109816 -> 0.117202: 1.07x slower
Significant [t=-357.08]
Stddev: 0.00008 -> 0.00019: 2.3664x larger

### json_dump_v2 ###
Min: 2.554462 -> 2.609141: 1.02x slower
Avg: 2.564472 -> 2.620013: 1.02x slower
Significant [t=-76.93]
Stddev: 0.00538 -> 0.00481: 1.1194x smaller

### meteor_contest ###
Min: 0.196336 -> 0.191925: 1.02x faster
Avg: 0.196878 -> 0.192698: 1.02x faster
Significant [t=61.86]
Stddev: 0.00053 -> 0.00041: 1.2925x smaller

### nbody ###
Min: 0.228039 -> 0.235551: 1.03x slower
Avg: 0.228857 -> 0.236052: 1.03x slower
Significant [t=-54.15]
Stddev: 0.00130 -> 0.00029: 4.4810x smaller

### pathlib ###
Min: 0.108501 -> 0.105339: 1.03x faster
Avg: 0.109084 -> 0.105619: 1.03x faster
Significant [t=311.08]
Stddev: 0.00022 -> 0.00011: 1.9314x smaller

### regex_effbot ###
Min: 0.057905 -> 0.056447: 1.03x faster
Avg: 0.058055 -> 0.056760: 1.02x faster
Significant [t=79.22]
Stddev: 0.00006 -> 0.00015: 2.7741x larger

### silent_logging ###
Min: 0.070810 -> 0.072436: 1.02x slower
Avg: 0.070899 -> 0.072609: 1.02x slower
Significant [t=-191.59]
Stddev: 0.00004 -> 0.00008: 2.2640x larger

### spectral_norm ###
Min: 0.290255 -> 0.299286: 1.03x slower
Avg: 0.290335 -> 0.299541: 1.03x slower
Significant [t=-572.10]
Stddev: 0.00005 -> 0.00015: 2.8547x larger

### threaded_count ###
Min: 0.107215 -> 0.115206: 1.07x slower
Avg: 0.107488 -> 0.115996: 1.08x slower
Significant [t=-109.39]
Stddev: 0.00016 -> 0.00076: 4.8665x larger

The following not significant results are hidden, use -v to show them:
call_method, call_method_unknown, chaos, fastpickle, fastunpickle, float, formatted_logging, hexiom2, json_load, normal_startup, nqueens, pidigits, raytrace, regex_compile, regex_v8, richards, simple_logging, startup_nosite, telco, unpack_sequence.

Alternative proposals

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
0

An earlier version of this PEP used the following static method on classes:

def __getattribute_super__[cls, name, object, owner]: pass

This method performed name lookup as well as invoking descriptors and was necessarily limited to working only with

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
3.

Reuse
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
1

It would be nice to avoid adding a new slot, thus keeping the API simpler and easier to understand. A comment on Issue 18181 asked about reusing the

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
1 slot, that is super could call the
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
1 slot of all methods along the MRO.

That won’t work because

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
1 will look in the instance
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
4 before it tries to resolve attributes using classes in the MRO. This would mean that using
def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
1 instead of peeking the class dictionaries changes the semantics of the super class.

Alternative placement of the new method

This PEP proposes to add

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5 as a method on the metaclass. An alternative would be to add it as a class method on the class itself [similar to how
def __getattribute_super__[cls, name, object, owner]: pass
9 is a staticmethod of the class and not a method of the metaclass].

The advantage of using a method on the metaclass is that will give an error when two classes on the MRO have different metaclasses that may have different behaviors for

def lookup[mro_list, name]:
    for cls in mro_list:
        try:
            return cls.__getdescriptor__[name]
        except AttributeError:
            pass

    return NotFound
5. With a normal classmethod that problem would pass undetected while it might cause subtle errors when running the code.

History

  • 23-Jul-2015: Added type flag
    class MetaType[type]:
        def __getdescriptor__[cls, name]:
            try:
                return cls.__dict__[name]
            except KeyError:
                raise AttributeError[name] from None
    
    3 after talking with Guido.

    The new flag is primarily useful to avoid crashing when loading an extension for an older version of CPython and could have positive speed implications as well.

Chủ Đề