Quick merge of Barry's feedback of four weeks ago. ;-(

This commit is contained in:
Guido van Rossum 2001-09-08 12:42:48 +00:00
parent aaebded223
commit 449eed85f9
1 changed files with 61 additions and 38 deletions

View File

@ -93,7 +93,7 @@ Introspection APIs
f.__members__ lists the names of f's statically defined
attributes).
Some caution must be exercised: some objects don't list theire
Some caution must be exercised: some objects don't list their
"intrinsic" attributes (like __dict__ and __doc__) in __members__,
while others do; sometimes attribute names occur both in
__members__ or __methods__ and as keys in __dict__, in which case
@ -136,7 +136,10 @@ Specification of the class-based introspection API
(XXX static and dynamic are not great terms to use here, because
"static" attributes may actually behave quite dynamically, and
because they have nothing to do with static class members in C++
or Java.)
or Java. Barry suggests to use immutable and mutable instead, but
those words already have precise and different meanings in
slightly different contexts, so I think that would still be
confusing.)
Examples of dynamic attributes are instance variables of class
instances, module attributes, etc. Examples of static attributes
@ -206,7 +209,7 @@ Specification of the class-based introspection API
descriptors; we'll explain these later. An unbound method is a
special case of an attribute descriptor.
Becase a meta-object is also a regular object, the items in a
Because a meta-object is also a regular object, the items in a
meta-object's __dict__ correspond to attributes of the
meta-object; however, some transformation may be applied, and
bases (see below) may define additional dynamic attributes. In
@ -223,20 +226,29 @@ Specification of the class-based introspection API
an empty sequence of bases. There must never be a cycle in the
relationship between meta-objects defined by __bases__
attributes; in other words, the __bases__ attributes define an
directed acyclic graph. (It is not necessarily a tree, since
multiple classes can have the same base class.) The __dict__
attributes of the meta-objects in the inheritance graph supply
attribute descriptors for the regular object whose __class__ is
at the top of the inheritance graph.
directed acyclic graph, with arcs pointing from derived
meta-objects to their base meta-objects. (It is not
necessarily a tree, since multiple classes can have the same
base class.) The __dict__ attributes of a meta-object in the
inheritance graph supply attribute descriptors for the regular
object whose __class__ attribute points to the root of the
inheritance tree (which is not the same as the root of the
inheritance hierarchy -- rather more the opposite, at the
bottom given how inheritance trees are typically drawn).
Descriptors are first searched in the dictionary of the root
meta-object, then in its bases, according to a precedence rule
(see the next paragraph).
5. Precedence rules
When two meta-objects in the inheritance graph for a given
regular object both define an attribute descriptor with the
same name, the left-to-right depth-first rule applies. (This
is the classic Python attribute lookup rule. Note that PEP 253
will propose to change the attribute lookup order, and if
accepted, this PEP will follow suit.)
same name, the search order is up to the meta-object. This
allows different meta-objects to define different search
orders. In particular, classic classes use the old
left-to-right depth-first rule, while new-style classes use a
more advanced rule (see the section on method resolution order
in PEP 253).
When a dynamic attribute (one defined in a regular object's
__dict__) has the same name as a static attribute (one defined
@ -244,7 +256,10 @@ Specification of the class-based introspection API
object's __class__), the static attribute has precedence if it
is a descriptor that defines a __set__ method (see below);
otherwise (if there is no __set__ method) the dynamic attribute
has precedence.
has precedence. In other words, for data attributes (those
with a __set__ method), the static definition overrides the
dynamic definition, but for other attributes, dynamic overrides
static.
Rationale: we can't have a simple rule like "static overrides
dynamic" or "dynamic overrides static", because some static
@ -325,27 +340,25 @@ Specification of the attribute descriptor API
C.meth.__objclass__ is C.
- __get__(): a function callable with one or two arguments that
retrieves the attribute value from an object. With one
argument, X, this either (for data attributes) retrieves the
attribute value from X or (for method attributes) binds the
attribute to X (returning some form of "bound" object that
receives an implied first argument of X when called). With two
arguments, X and T, T must be a meta-object that restricts the
type of X. X must either be an instance of T (in which the
effect is the same as when T is omitted), or None. When X is
None, this should be a method descriptor, and the result is an
*unbound* method restricted to objects whose type is (a
descendent of) T. Such an unbound method is a descriptor
itself. For methods, this is called a "binding" operation, even
if X==None. Exactly what is returned by the binding operation
depends on the semantics of the descriptor; for example, static
methods and class methods (see below) ignore the instance and
bind to the type instead.
retrieves the attribute value from an object. This is also
referred to as a "binding" operation, because it may return a
"bound method" object in the case of method descriptors. The
first argument, X, is the object from which the attribute must
be retrieved or to which it must be bound. When X is None, the
optional second argument, T, should be meta-object and the
binding operation may return an *unbound* method restricted to
instances of T. When both X and T are specified, X should be an
instance of T. Exactly what is returned by the binding
operation depends on the semantics of the descriptor; for
example, static methods and class methods (see below) ignore the
instance and bind to the type instead.
- __set__(): a function of two arguments that sets the attribute
value on the object. If the attribute is read-only, this method
raises a TypeError exception. (Not an AttributeError!)
Example: C.ivar.set(x, y) ~~ x.ivar = y.
may raise a TypeError or AttributeError exception (both are
allowed, because both are historically found for undefined or
unsettable attributes). Example:
C.ivar.set(x, y) ~~ x.ivar = y.
Static methods and class methods
@ -377,6 +390,11 @@ Static methods and class methods
function objects would have created a bound method object for
'c.foo' and an unbound method object for 'C.foo'.
(XXX Barry suggests to use "sharedmethod" instead of
"staticmethod", because the word statis is being overloaded in so
many ways already. But I'm not sure if shared conveys the right
meaning.)
Class methods use a similar pattern to declare methods that
receive an implicit first argument that is the *class* for which
they are invoked. This has no C++ or Java equivalent, and is not
@ -431,10 +449,15 @@ Static methods and class methods
In this example, the call to C.foo() from E.foo() will see class C
as its first argument, not class E. This is to be expected, since
the call specifies the class C. But it stresses the difference
between these class methods and methods defined in metaclasses
(where an upcall to a metamethod would pass the target class as an
explicit first argument). If you don't understand this, don't
worry, you're not alone.
between these class methods and methods defined in metaclasses,
where an upcall to a metamethod would pass the target class as an
explicit first argument. (If you don't understand this, don't
worry, you're not alone.) Note that calling cls.foo(y) would be a
mistake -- it would cause infinite recursion. Also note that you
can't specify an explicit 'cls' argument to a class method. If
you want this (e.g. the __new__ method in PEP 253 requires this),
use a static method with a class as its explicit first argument
instead.
C API
@ -567,9 +590,9 @@ C API
This requires a classification of descriptors as data and
nondata descriptors. The current implementation quite sensibly
classifies member and getset descriptors as data (even if they
are read-only!) and member descriptors as nondata.
are read-only!) and method descriptors as nondata.
Non-descriptors (like function pointers or plain values) are
also classified as non-data.
also classified as non-data (!).
- This scheme has one drawback: in what I assume to be the most
common case, referencing an instance variable stored in the
@ -604,7 +627,7 @@ Examples
>>> [].__methods__
['append', 'count', 'extend', 'index', 'insert', 'pop',
'remove', 'reverse', 'sort']
>>>
>>>
Under the new proposal, the __methods__ attribute no longer exists: