Quick merge of Barry's feedback of four weeks ago. ;-(
This commit is contained in:
parent
aaebded223
commit
449eed85f9
97
pep-0252.txt
97
pep-0252.txt
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue