<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1256">
<meta name="Generator" content="Microsoft Exchange Server">
<!-- converted from text --><style><!-- .EmailQuote { margin-left: 1pt; padding-left: 4pt; border-left: #800000 2px solid; } --></style>
</head>
<body>
<div>
<div>
<div style="font-family:Calibri,sans-serif; font-size:11pt">I wonder whether XML RPC might be a good example? After all, it's already in the stdlib and presumably suffers from the same issue.<br>
<br>
Cheers,<br>
Steve<br>
<br>
Top-posted from my Windows Phone</div>
</div>
<div dir="ltr">
<hr>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">From:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt"><a href="mailto:ronaldoussoren@mac.com">Ronald Oussoren</a></span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">Sent:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt">ý7/ý23/ý2015 3:07</span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">To:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt"><a href="mailto:dimaqq@gmail.com">Dima Tisnek</a></span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">Cc:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt"><a href="mailto:python-dev@python.org">Python Dev</a></span><br>
<span style="font-family:Calibri,sans-serif; font-size:11pt; font-weight:bold">Subject:
</span><span style="font-family:Calibri,sans-serif; font-size:11pt">Re: [Python-Dev] PEP 447 (type.__getdescriptor__)</span><br>
<br>
</div>
</div>
<font size="2"><span style="font-size:10pt;">
<div class="PlainText"><br>
> On 23 Jul 2015, at 11:29, Dima Tisnek <dimaqq@gmail.com> wrote:<br>
> <br>
> Hey I've taken time to read the PEP, my 2c... actually 1c:<br>
> <br>
> Most important is to explain how this changes behaviour of Python programs.<br>
> <br>
> A comprehensive set of Python examples where behaviour is changed (for<br>
> better or worse) please.<br>
<br>
The behaviour of existing python code is not changed at all. Code that directly looks in the class __dict__ might be impacted, but only when running into classes with a custom __getdescriptor__ method. I’ve listed the code in the stdlib that could be affected,
but have to do a new pass of the stdlib to check if anything relevant changed since I wrote the section. I general you run into the same issues when adding a custom __getattribute__ or __getattr__ method, both of which will confuse introspection tools that
assume regular attribute lookup semantics.<br>
<br>
> <br>
> While I understand the concern of "superclasses of objects that gain<br>
> or lose attributes at runtime" on the theoretical level, please<br>
> translate that into actual Python examples.<br>
<br>
The primary use case I have for this are classes that are proxies for external systems. There may be other uses as well, but I don’t have examples of that (other than the contrived example in the PEP).<br>
<br>
The reason I wrote the PEP in the first place is PyObjC: this project defines a proxy later between Python and Objective-C, with the goal to making it possible to write programs for Mac OS X in Python while being able to make full use of Apple’s high-level
APIs. The proxy layer is mostly completely dynamic: proxies for Objective-C classes and their methods are created at runtime by inspecting the Objective-C runtime with optionally extra annotations (provided by the project) for stuff that cannot be extracted
from the runtime. <br>
<br>
That is, at runtime PyObjC creates a Python class “NSObject” that corresponds to the Objective-C class “NSObject” as defined by Apple. Every method of the Objective-C class is make available as a method on the Python proxy class as well.<br>
<br>
It is not possible to 100% reliably set up the Python proxy class for “NSObject” with all methods because Objective-C classes can grow new methods at runtime, and the introspection API that Apple provides does not have a way to detect this other than by polling.
Older versions of PyObjC did poll, but even that was not relialble enough and left a race condition:<br>
<br>
def someAction_(self, sender):<br>
self.someMethod()<br>
self.button.setTitle_(“click me”)<br>
super().someOtherMethod()<br>
<br>
The call to “someMethod” used to poll the Objective-C runtime for changes. The call through super() of someOtherMethod() does not do so because of the current semantics of super (which PEP 447 tries to change). That’s a problem because “self.button.setTitle_”
might load a bundle that adds “someOtherMethod” to one of our super classes. That sadly enough is not a theoretical concern, I’ve seen something like this in the past.<br>
<br>
Because of this PyObjC contains its own version of builtins.super which must be used with it (and is fully compatible with builtin.super for other classes).<br>
<br>
Recent versions of PyObjC no longer poll, primarily because polling is costly and because Objective-C classes tend to have fat APIs most of which is never used by any one program.<br>
<br>
What bothers me with PyObjC’s current approach is one the one hand that a custom super is inherently incompatible with any other library that might have a simular need, and on there other hand that I have to reimplement all logic in both object.__getattribute__
and super.__getattribute__ to be able to have a customisation of one small aspect of attribute lookup.<br>
<br>
Ronald<br>
<br>
> <br>
> d.<br>
> <br>
> On 22 July 2015 at 09:25, Ronald Oussoren <ronaldoussoren@mac.com> wrote:<br>
>> Hi,<br>
>> <br>
>> Another summer with another EuroPython, which means its time again to try to<br>
>> revive PEP 447…<br>
>> <br>
>> I’ve just pushes a minor update to the PEP and would like to get some<br>
>> feedback on this, arguably fairly esoteric, PEP.<br>
>> <br>
>> The PEP proposes to to replace direct access to the class __dict__ in<br>
>> object.__getattribute__ and super.__getattribute__ by calls to a new special<br>
>> method to give the metaclass more control over attribute lookup, especially<br>
>> for access using a super() object. This is needed for classes that cannot<br>
>> store (all) descriptors in the class dict for some reason, see the PEP text<br>
>> for a real-world example of that.<br>
>> <br>
>> Regards,<br>
>> <br>
>> Ronald<br>
>> <br>
>> <br>
>> The PEP text (with an outdated section with benchmarks removed):<br>
>> <br>
>> PEP: 447<br>
>> Title: Add __getdescriptor__ method to metaclass<br>
>> Version: $Revision$<br>
>> Last-Modified: $Date$<br>
>> Author: Ronald Oussoren <ronaldoussoren@mac.com><br>
>> Status: Draft<br>
>> Type: Standards Track<br>
>> Content-Type: text/x-rst<br>
>> Created: 12-Jun-2013<br>
>> Post-History: 2-Jul-2013, 15-Jul-2013, 29-Jul-2013, 22-Jul-2015<br>
>> <br>
>> <br>
>> Abstract<br>
>> ========<br>
>> <br>
>> Currently ``object.__getattribute__`` and ``super.__getattribute__`` peek<br>
>> in the ``__dict__`` of classes on the MRO for a class when looking for<br>
>> an attribute. This PEP adds an optional ``__getdescriptor__`` method to<br>
>> a metaclass that replaces this behavior and gives more control over<br>
>> attribute<br>
>> lookup, especially when using a `super`_ object.<br>
>> <br>
>> That is, the MRO walking loop in ``_PyType_Lookup`` and<br>
>> ``super.__getattribute__`` gets changed from::<br>
>> <br>
>> def lookup(mro_list, name):<br>
>> for cls in mro_list:<br>
>> if name in cls.__dict__:<br>
>> return cls.__dict__<br>
>> <br>
>> return NotFound<br>
>> <br>
>> to::<br>
>> <br>
>> def lookup(mro_list, name):<br>
>> for cls in mro_list:<br>
>> try:<br>
>> return cls.__getdescriptor__(name)<br>
>> except AttributeError:<br>
>> pass<br>
>> <br>
>> return NotFound<br>
>> <br>
>> The default implemention of ``__getdescriptor__`` looks in the class<br>
>> dictionary::<br>
>> <br>
>> class type:<br>
>> def __getdescriptor__(cls, name):<br>
>> try:<br>
>> return cls.__dict__[name]<br>
>> except KeyError:<br>
>> raise AttributeError(name) from None<br>
>> <br>
>> Rationale<br>
>> =========<br>
>> <br>
>> It is currently not possible to influence how the `super class`_ looks<br>
>> up attributes (that is, ``super.__getattribute__`` unconditionally<br>
>> peeks in the class ``__dict__``), and that can be problematic for<br>
>> dynamic classes that can grow new methods on demand.<br>
>> <br>
>> The ``__getdescriptor__`` method makes it possible to dynamically add<br>
>> attributes even when looking them up using the `super class`_.<br>
>> <br>
>> The new method affects ``object.__getattribute__`` (and<br>
>> `PyObject_GenericGetAttr`_) as well for consistency and to have a single<br>
>> place to implement dynamic attribute resolution for classes.<br>
>> <br>
>> Background<br>
>> ----------<br>
>> <br>
>> The current behavior of ``super.__getattribute__`` causes problems for<br>
>> classes that are dynamic proxies for other (non-Python) classes or types,<br>
>> an example of which is `PyObjC`_. PyObjC creates a Python class for every<br>
>> class in the Objective-C runtime, and looks up methods in the Objective-C<br>
>> runtime when they are used. This works fine for normal access, but doesn't<br>
>> work for access with `super`_ objects. Because of this PyObjC currently<br>
>> includes a custom `super`_ that must be used with its classes, as well as<br>
>> completely reimplementing `PyObject_GenericGetAttr`_ for normal attribute<br>
>> access.<br>
>> <br>
>> The API in this PEP makes it possible to remove the custom `super`_ and<br>
>> simplifies the implementation because the custom lookup behavior can be<br>
>> added in a central location.<br>
>> <br>
>> .. note::<br>
>> <br>
>> `PyObjC`_ cannot precalculate the contents of the class ``__dict__``<br>
>> because Objective-C classes can grow new methods at runtime. Furthermore<br>
>> Objective-C classes tend to contain a lot of methods while most Python<br>
>> code will only use a small subset of them, this makes precalculating<br>
>> unnecessarily expensive.<br>
>> <br>
>> <br>
>> The superclass attribute lookup hook<br>
>> ====================================<br>
>> <br>
>> Both ``super.__getattribute__`` and ``object.__getattribute__`` (or<br>
>> `PyObject_GenericGetAttr`_ and in particular ``_PyType_Lookup`` in C code)<br>
>> walk an object's MRO and currently peek in the class' ``__dict__`` to look<br>
>> up<br>
>> attributes.<br>
>> <br>
>> With this proposal both lookup methods no longer peek in the class<br>
>> ``__dict__``<br>
>> but call the special method ``__getdescriptor__``, which is a slot defined<br>
>> on the metaclass. The default implementation of that method looks<br>
>> up the name the class ``__dict__``, which means that attribute lookup is<br>
>> unchanged unless a metatype actually defines the new special method.<br>
>> <br>
>> Aside: Attribute resolution algorithm in Python<br>
>> -----------------------------------------------<br>
>> <br>
>> The attribute resolution proces as implemented by<br>
>> ``object.__getattribute__``<br>
>> (or PyObject_GenericGetAttr`` in CPython's implementation) is fairly<br>
>> straightforward, but not entirely so without reading C code.<br>
>> <br>
>> The current CPython implementation of object.__getattribute__ is basicly<br>
>> equivalent to the following (pseudo-) Python code (excluding some house<br>
>> keeping and speed tricks)::<br>
>> <br>
>> <br>
>> def _PyType_Lookup(tp, name):<br>
>> mro = tp.mro()<br>
>> assert isinstance(mro, tuple)<br>
>> <br>
>> for base in mro:<br>
>> assert isinstance(base, type)<br>
>> <br>
>> # PEP 447 will change these lines:<br>
>> try:<br>
>> return base.__dict__[name]<br>
>> except KeyError:<br>
>> pass<br>
>> <br>
>> return None<br>
>> <br>
>> <br>
>> class object:<br>
>> def __getattribute__(self, name):<br>
>> assert isinstance(name, str)<br>
>> <br>
>> tp = type(self)<br>
>> descr = _PyType_Lookup(tp, name)<br>
>> <br>
>> f = None<br>
>> if descr is not None:<br>
>> f = descr.__get__<br>
>> if f is not None and descr.__set__ is not None:<br>
>> # Data descriptor<br>
>> return f(descr, self, type(self))<br>
>> <br>
>> dict = self.__dict__<br>
>> if dict is not None:<br>
>> try:<br>
>> return self.__dict__[name]<br>
>> except KeyError:<br>
>> pass<br>
>> <br>
>> if f is not None:<br>
>> # Non-data descriptor<br>
>> return f(descr, self, type(self))<br>
>> <br>
>> if descr is not None:<br>
>> # Regular class attribute<br>
>> return descr<br>
>> <br>
>> raise AttributeError(name)<br>
>> <br>
>> <br>
>> class super:<br>
>> def __getattribute__(self, name):<br>
>> assert isinstance(name, unicode)<br>
>> <br>
>> if name != '__class__':<br>
>> starttype = self.__self_type__<br>
>> mro = startype.mro()<br>
>> <br>
>> try:<br>
>> idx = mro.index(self.__thisclass__)<br>
>> <br>
>> except ValueError:<br>
>> pass<br>
>> <br>
>> else:<br>
>> for base in mro[idx+1:]:<br>
>> # PEP 447 will change these lines:<br>
>> try:<br>
>> descr = base.__dict__[name]<br>
>> except KeyError:<br>
>> continue<br>
>> <br>
>> f = descr.__get__<br>
>> if f is not None:<br>
>> return f(descr,<br>
>> None if (self.__self__ is self.__self_type__) else self.__self__,<br>
>> starttype)<br>
>> <br>
>> else:<br>
>> return descr<br>
>> <br>
>> return object.__getattribute__(self, name)<br>
>> <br>
>> <br>
>> This PEP should change the dict lookup at the lines starting at "# PEP 447"<br>
>> with<br>
>> a method call to perform the actual lookup, making is possible to affect<br>
>> that<br>
>> lookup both for normal attribute access and access through the `super<br>
>> proxy`_.<br>
>> <br>
>> Note that specific classes can already completely override the default<br>
>> behaviour by implementing their own ``__getattribute__`` slot (with or<br>
>> without<br>
>> calling the super class implementation).<br>
>> <br>
>> <br>
>> In Python code<br>
>> --------------<br>
>> <br>
>> A meta type can define a method ``__getdescriptor__`` that is called during<br>
>> attribute resolution by both ``super.__getattribute__``<br>
>> and ``object.__getattribute``::<br>
>> <br>
>> class MetaType(type):<br>
>> def __getdescriptor__(cls, name):<br>
>> try:<br>
>> return cls.__dict__[name]<br>
>> except KeyError:<br>
>> raise AttributeError(name) from None<br>
>> <br>
>> The ``__getdescriptor__`` method has as its arguments a class (which is an<br>
>> instance of the meta type) and the name of the attribute that is looked up.<br>
>> It should return the value of the attribute without invoking descriptors,<br>
>> and should raise `AttributeError`_ when the name cannot be found.<br>
>> <br>
>> The `type`_ class provides a default implementation for<br>
>> ``__getdescriptor__``,<br>
>> that looks up the name in the class dictionary.<br>
>> <br>
>> Example usage<br>
>> .............<br>
>> <br>
>> The code below implements a silly metaclass that redirects attribute lookup<br>
>> to<br>
>> uppercase versions of names::<br>
>> <br>
>> class UpperCaseAccess (type):<br>
>> def __getdescriptor__(cls, name):<br>
>> try:<br>
>> return cls.__dict__[name.upper()]<br>
>> except KeyError:<br>
>> raise AttributeError(name) from None<br>
>> <br>
>> class SillyObject (metaclass=UpperCaseAccess):<br>
>> def m(self):<br>
>> return 42<br>
>> <br>
>> def M(self):<br>
>> return "fourtytwo"<br>
>> <br>
>> obj = SillyObject()<br>
>> assert obj.m() == "fortytwo"<br>
>> <br>
>> As mentioned earlier in this PEP a more realistic use case of this<br>
>> functionallity is a ``__getdescriptor__`` method that dynamicly populates<br>
>> the<br>
>> class ``__dict__`` based on attribute access, primarily when it is not<br>
>> possible to reliably keep the class dict in sync with its source, for<br>
>> example<br>
>> because the source used to populate ``__dict__`` is dynamic as well and does<br>
>> not have triggers that can be used to detect changes to that source.<br>
>> <br>
>> An example of that are the class bridges in PyObjC: the class bridge is a<br>
>> Python object (class) that represents an Objective-C class and conceptually<br>
>> has a Python method for every Objective-C method in the Objective-C class.<br>
>> As with Python it is possible to add new methods to an Objective-C class, or<br>
>> replace existing ones, and there are no callbacks that can be used to detect<br>
>> this.<br>
>> <br>
>> In C code<br>
>> ---------<br>
>> <br>
>> A new slot ``tp_getdescriptor`` is added to the ``PyTypeObject`` struct,<br>
>> this<br>
>> slot corresponds to the ``__getdescriptor__`` method on `type`_.<br>
>> <br>
>> The slot has the following prototype::<br>
>> <br>
>> PyObject* (*getdescriptorfunc)(PyTypeObject* cls, PyObject* name);<br>
>> <br>
>> This method should lookup *name* in the namespace of *cls*, without looking<br>
>> at<br>
>> superclasses, and should not invoke descriptors. The method returns ``NULL``<br>
>> without setting an exception when the *name* cannot be found, and returns a<br>
>> new reference otherwise (not a borrowed reference).<br>
>> <br>
>> Use of this hook by the interpreter<br>
>> -----------------------------------<br>
>> <br>
>> The new method is required for metatypes and as such is defined on `type_`.<br>
>> Both ``super.__getattribute__`` and<br>
>> ``object.__getattribute__``/`PyObject_GenericGetAttr`_<br>
>> (through ``_PyType_Lookup``) use the this ``__getdescriptor__`` method when<br>
>> walking the MRO.<br>
>> <br>
>> Other changes to the implementation<br>
>> -----------------------------------<br>
>> <br>
>> The change for `PyObject_GenericGetAttr`_ will be done by changing the<br>
>> private<br>
>> function ``_PyType_Lookup``. This currently returns a borrowed reference,<br>
>> but<br>
>> must return a new reference when the ``__getdescriptor__`` method is<br>
>> present.<br>
>> Because of this ``_PyType_Lookup`` will be renamed to<br>
>> ``_PyType_LookupName``,<br>
>> this will cause compile-time errors for all out-of-tree users of this<br>
>> private API.<br>
>> <br>
>> The attribute lookup cache in ``Objects/typeobject.c`` is disabled for<br>
>> classes<br>
>> that have a metaclass that overrides ``__getdescriptor__``, because using<br>
>> the<br>
>> cache might not be valid for such classes.<br>
>> <br>
>> Impact of this PEP on introspection<br>
>> -----------------------------------<br>
>> <br>
>> Use of the method introduced in this PEP can affect introspection of classes<br>
>> with a metaclass that uses a custom ``__getdescriptor__`` method. This<br>
>> section<br>
>> lists those changes.<br>
>> <br>
>> The items listed below are only affected by custom ``__getdescriptor__``<br>
>> methods, the default implementation for ``object`` won't cause problems<br>
>> because that still only uses the class ``__dict__`` and won't cause visible<br>
>> changes to the visible behaviour of the ``object.__getattribute__``.<br>
>> <br>
>> * ``dir`` might not show all attributes<br>
>> <br>
>> As with a custom ``__getattribute__`` method `dir()`_ might not see all<br>
>> (instance) attributes when using the ``__getdescriptor__()`` method to<br>
>> dynamicly resolve attributes.<br>
>> <br>
>> The solution for that is quite simple: classes using ``__getdescriptor__``<br>
>> should also implement `__dir__()`_ if they want full support for the<br>
>> builtin<br>
>> `dir()`_ function.<br>
>> <br>
>> * ``inspect.getattr_static`` might not show all attributes<br>
>> <br>
>> The function ``inspect.getattr_static`` intentionally does not invoke<br>
>> ``__getattribute__`` and descriptors to avoid invoking user code during<br>
>> introspection with this function. The ``__getdescriptor__`` method will<br>
>> also<br>
>> be ignored and is another way in which the result of<br>
>> ``inspect.getattr_static``<br>
>> can be different from that of ``builtin.getattr``.<br>
>> <br>
>> * ``inspect.getmembers`` and ``inspect.get_class_attrs``<br>
>> <br>
>> Both of these functions directly access the class __dict__ of classes<br>
>> along<br>
>> the MRO, and hence can be affected by a custom ``__getdescriptor__``<br>
>> method.<br>
>> <br>
>> **TODO**: I haven't fully worked out what the impact of this is, and if<br>
>> there<br>
>> are mitigations for those using either updates to these functions, or<br>
>> additional methods that users should implement to be fully compatible with<br>
>> these functions.<br>
>> <br>
>> One possible mitigation is to have a custom ``__getattribute__`` for these<br>
>> classes that fills ``__dict__`` before returning and and defers to the<br>
>> default implementation for other attributes.<br>
>> <br>
>> * Direct introspection of the class ``__dict__``<br>
>> <br>
>> Any code that directly access the class ``__dict__`` for introspection<br>
>> can be affected by a custom ``__getdescriptor__`` method.<br>
>> <br>
>> <br>
>> Performance impact<br>
>> ------------------<br>
>> <br>
>> **WARNING**: The benchmark results in this section are old, and will be<br>
>> updated<br>
>> when I've ported the patch to the current trunk. I don't expect significant<br>
>> changes to the results in this section.<br>
>> <br>
>> [snipped]<br>
>> <br>
>> <br>
>> Alternative proposals<br>
>> ---------------------<br>
>> <br>
>> ``__getattribute_super__``<br>
>> ..........................<br>
>> <br>
>> An earlier version of this PEP used the following static method on classes::<br>
>> <br>
>> def __getattribute_super__(cls, name, object, owner): pass<br>
>> <br>
>> This method performed name lookup as well as invoking descriptors and was<br>
>> necessarily limited to working only with ``super.__getattribute__``.<br>
>> <br>
>> <br>
>> Reuse ``tp_getattro``<br>
>> .....................<br>
>> <br>
>> It would be nice to avoid adding a new slot, thus keeping the API simpler<br>
>> and<br>
>> easier to understand. A comment on `Issue 18181`_ asked about reusing the<br>
>> ``tp_getattro`` slot, that is super could call the ``tp_getattro`` slot of<br>
>> all<br>
>> methods along the MRO.<br>
>> <br>
>> That won't work because ``tp_getattro`` will look in the instance<br>
>> ``__dict__`` before it tries to resolve attributes using classes in the MRO.<br>
>> This would mean that using ``tp_getattro`` instead of peeking the class<br>
>> dictionaries changes the semantics of the `super class`_.<br>
>> <br>
>> Alternate placement of the new method<br>
>> .....................................<br>
>> <br>
>> This PEP proposes to add ``__getdescriptor__`` as a method on the metaclass.<br>
>> An alternative would be to add it as a class method on the class itself<br>
>> (simular to how ``__new__`` is a `staticmethod`_ of the class and not a<br>
>> method<br>
>> of the metaclass).<br>
>> <br>
>> The two are functionally equivalent, and there's something to be said about<br>
>> not requiring the use of a meta class.<br>
>> <br>
>> <br>
>> References<br>
>> ==========<br>
>> <br>
>> * `Issue 18181`_ contains an out of date prototype implementation<br>
>> <br>
>> Copyright<br>
>> =========<br>
>> <br>
>> This document has been placed in the public domain.<br>
>> <br>
>> .. _`Issue 18181`: <a href="http://bugs.python.org/issue18181">http://bugs.python.org/issue18181</a><br>
>> <br>
>> .. _`super class`: <a href="http://docs.python.org/3/library/functions.html#super">
http://docs.python.org/3/library/functions.html#super</a><br>
>> <br>
>> .. _`super proxy`: <a href="http://docs.python.org/3/library/functions.html#super">
http://docs.python.org/3/library/functions.html#super</a><br>
>> <br>
>> .. _`super`: <a href="http://docs.python.org/3/library/functions.html#super">http://docs.python.org/3/library/functions.html#super</a><br>
>> <br>
>> .. _`dir()`: <a href="http://docs.python.org/3/library/functions.html#dir">http://docs.python.org/3/library/functions.html#dir</a><br>
>> <br>
>> .. _`staticmethod`:<br>
>> <a href="http://docs.python.org/3/library/functions.html#staticmethod">http://docs.python.org/3/library/functions.html#staticmethod</a><br>
>> <br>
>> .. _`__dir__()`:<br>
>> <a href="https://docs.python.org/3/reference/datamodel.html#object.__dir__">https://docs.python.org/3/reference/datamodel.html#object.__dir__</a><br>
>> <br>
>> .. _`NotImplemented`:<br>
>> <a href="http://docs.python.org/3/library/constants.html#NotImplemented">http://docs.python.org/3/library/constants.html#NotImplemented</a><br>
>> <br>
>> .. _`PyObject_GenericGetAttr`:<br>
>> <a href="http://docs.python.org/3/c-api/object.html#PyObject_GenericGetAttr">http://docs.python.org/3/c-api/object.html#PyObject_GenericGetAttr</a><br>
>> <br>
>> .. _`type`: <a href="http://docs.python.org/3/library/functions.html#type">http://docs.python.org/3/library/functions.html#type</a><br>
>> <br>
>> .. _`AttributeError`:<br>
>> <a href="http://docs.python.org/3/library/exceptions.html#AttributeError">http://docs.python.org/3/library/exceptions.html#AttributeError</a><br>
>> <br>
>> .. _`PyObjC`: <a href="http://pyobjc.sourceforge.net/">http://pyobjc.sourceforge.net/</a><br>
>> <br>
>> .. _`classmethod`:<br>
>> <a href="http://docs.python.org/3/library/functions.html#classmethod">http://docs.python.org/3/library/functions.html#classmethod</a><br>
>> <br>
>> _______________________________________________<br>
>> Python-Dev mailing list<br>
>> Python-Dev@python.org<br>
>> <a href="https://mail.python.org/mailman/listinfo/python-dev">https://mail.python.org/mailman/listinfo/python-dev</a><br>
>> Unsubscribe:<br>
>> <a href="https://mail.python.org/mailman/options/python-dev/dimaqq%40gmail.com">
https://mail.python.org/mailman/options/python-dev/dimaqq%40gmail.com</a><br>
>> <br>
<br>
_______________________________________________<br>
Python-Dev mailing list<br>
Python-Dev@python.org<br>
<a href="https://mail.python.org/mailman/listinfo/python-dev">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/steve.dower%40microsoft.com">
https://mail.python.org/mailman/options/python-dev/steve.dower%40microsoft.com</a><br>
</div>
</span></font>
</body>
</html>