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
|
f.__members__ lists the names of f's statically defined
|
||||||
attributes).
|
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__,
|
"intrinsic" attributes (like __dict__ and __doc__) in __members__,
|
||||||
while others do; sometimes attribute names occur both in
|
while others do; sometimes attribute names occur both in
|
||||||
__members__ or __methods__ and as keys in __dict__, in which case
|
__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
|
(XXX static and dynamic are not great terms to use here, because
|
||||||
"static" attributes may actually behave quite dynamically, and
|
"static" attributes may actually behave quite dynamically, and
|
||||||
because they have nothing to do with static class members in C++
|
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
|
Examples of dynamic attributes are instance variables of class
|
||||||
instances, module attributes, etc. Examples of static attributes
|
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
|
descriptors; we'll explain these later. An unbound method is a
|
||||||
special case of an attribute descriptor.
|
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's __dict__ correspond to attributes of the
|
||||||
meta-object; however, some transformation may be applied, and
|
meta-object; however, some transformation may be applied, and
|
||||||
bases (see below) may define additional dynamic attributes. In
|
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
|
an empty sequence of bases. There must never be a cycle in the
|
||||||
relationship between meta-objects defined by __bases__
|
relationship between meta-objects defined by __bases__
|
||||||
attributes; in other words, the __bases__ attributes define an
|
attributes; in other words, the __bases__ attributes define an
|
||||||
directed acyclic graph. (It is not necessarily a tree, since
|
directed acyclic graph, with arcs pointing from derived
|
||||||
multiple classes can have the same base class.) The __dict__
|
meta-objects to their base meta-objects. (It is not
|
||||||
attributes of the meta-objects in the inheritance graph supply
|
necessarily a tree, since multiple classes can have the same
|
||||||
attribute descriptors for the regular object whose __class__ is
|
base class.) The __dict__ attributes of a meta-object in the
|
||||||
at the top of the inheritance graph.
|
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
|
5. Precedence rules
|
||||||
|
|
||||||
When two meta-objects in the inheritance graph for a given
|
When two meta-objects in the inheritance graph for a given
|
||||||
regular object both define an attribute descriptor with the
|
regular object both define an attribute descriptor with the
|
||||||
same name, the left-to-right depth-first rule applies. (This
|
same name, the search order is up to the meta-object. This
|
||||||
is the classic Python attribute lookup rule. Note that PEP 253
|
allows different meta-objects to define different search
|
||||||
will propose to change the attribute lookup order, and if
|
orders. In particular, classic classes use the old
|
||||||
accepted, this PEP will follow suit.)
|
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
|
When a dynamic attribute (one defined in a regular object's
|
||||||
__dict__) has the same name as a static attribute (one defined
|
__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
|
object's __class__), the static attribute has precedence if it
|
||||||
is a descriptor that defines a __set__ method (see below);
|
is a descriptor that defines a __set__ method (see below);
|
||||||
otherwise (if there is no __set__ method) the dynamic attribute
|
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
|
Rationale: we can't have a simple rule like "static overrides
|
||||||
dynamic" or "dynamic overrides static", because some static
|
dynamic" or "dynamic overrides static", because some static
|
||||||
|
@ -325,27 +340,25 @@ Specification of the attribute descriptor API
|
||||||
C.meth.__objclass__ is C.
|
C.meth.__objclass__ is C.
|
||||||
|
|
||||||
- __get__(): a function callable with one or two arguments that
|
- __get__(): a function callable with one or two arguments that
|
||||||
retrieves the attribute value from an object. With one
|
retrieves the attribute value from an object. This is also
|
||||||
argument, X, this either (for data attributes) retrieves the
|
referred to as a "binding" operation, because it may return a
|
||||||
attribute value from X or (for method attributes) binds the
|
"bound method" object in the case of method descriptors. The
|
||||||
attribute to X (returning some form of "bound" object that
|
first argument, X, is the object from which the attribute must
|
||||||
receives an implied first argument of X when called). With two
|
be retrieved or to which it must be bound. When X is None, the
|
||||||
arguments, X and T, T must be a meta-object that restricts the
|
optional second argument, T, should be meta-object and the
|
||||||
type of X. X must either be an instance of T (in which the
|
binding operation may return an *unbound* method restricted to
|
||||||
effect is the same as when T is omitted), or None. When X is
|
instances of T. When both X and T are specified, X should be an
|
||||||
None, this should be a method descriptor, and the result is an
|
instance of T. Exactly what is returned by the binding
|
||||||
*unbound* method restricted to objects whose type is (a
|
operation depends on the semantics of the descriptor; for
|
||||||
descendent of) T. Such an unbound method is a descriptor
|
example, static methods and class methods (see below) ignore the
|
||||||
itself. For methods, this is called a "binding" operation, even
|
instance and bind to the type instead.
|
||||||
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.
|
|
||||||
|
|
||||||
- __set__(): a function of two arguments that sets the attribute
|
- __set__(): a function of two arguments that sets the attribute
|
||||||
value on the object. If the attribute is read-only, this method
|
value on the object. If the attribute is read-only, this method
|
||||||
raises a TypeError exception. (Not an AttributeError!)
|
may raise a TypeError or AttributeError exception (both are
|
||||||
Example: C.ivar.set(x, y) ~~ x.ivar = y.
|
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
|
Static methods and class methods
|
||||||
|
@ -377,6 +390,11 @@ Static methods and class methods
|
||||||
function objects would have created a bound method object for
|
function objects would have created a bound method object for
|
||||||
'c.foo' and an unbound method object for 'C.foo'.
|
'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
|
Class methods use a similar pattern to declare methods that
|
||||||
receive an implicit first argument that is the *class* for which
|
receive an implicit first argument that is the *class* for which
|
||||||
they are invoked. This has no C++ or Java equivalent, and is not
|
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
|
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
|
as its first argument, not class E. This is to be expected, since
|
||||||
the call specifies the class C. But it stresses the difference
|
the call specifies the class C. But it stresses the difference
|
||||||
between these class methods and methods defined in metaclasses
|
between these class methods and methods defined in metaclasses,
|
||||||
(where an upcall to a metamethod would pass the target class as an
|
where an upcall to a metamethod would pass the target class as an
|
||||||
explicit first argument). If you don't understand this, don't
|
explicit first argument. (If you don't understand this, don't
|
||||||
worry, you're not alone.
|
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
|
C API
|
||||||
|
@ -567,9 +590,9 @@ C API
|
||||||
This requires a classification of descriptors as data and
|
This requires a classification of descriptors as data and
|
||||||
nondata descriptors. The current implementation quite sensibly
|
nondata descriptors. The current implementation quite sensibly
|
||||||
classifies member and getset descriptors as data (even if they
|
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
|
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
|
- This scheme has one drawback: in what I assume to be the most
|
||||||
common case, referencing an instance variable stored in the
|
common case, referencing an instance variable stored in the
|
||||||
|
|
Loading…
Reference in New Issue