[Python-checkins] CVS: python/nondist/peps pep-0252.txt,1.10,1.11
Guido van Rossum
gvanrossum@users.sourceforge.net
2001年7月09日 12:05:41 -0700
Update of /cvsroot/python/python/nondist/peps
In directory usw-pr-cvs1:/tmp/cvs-serv3443
Modified Files:
pep-0252.txt
Log Message:
Simplify the attribute descriptor spec: the only attributes defined
are now __name__, __doc__, __objclass__, __get__ and __set__. This is
also what's implemented now. A bunch of things (including the
precedence rules used when an attribute exists both in the instance
__dict__ and in the class __dict__) are clarified.
The C API and other stuff still need to be fleshed out.
Index: pep-0252.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0252.txt,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -r1.10 -r1.11
*** pep-0252.txt 2001年07月05日 18:57:36 1.10
--- pep-0252.txt 2001年07月09日 19:05:39 1.11
***************
*** 12,22 ****
This PEP proposes changes to the introspection API for types that
! makes them look more like classes. For example, type(x) will be
! equivalent to x.__class__ for most built-in types. When C is
! x.__class__, x.meth(a) will be equivalent to C.meth(x, a), and
! C.__dict__ contains descriptors for x's methods and other
! attributes.
! The PEP also introduces a new approach to specifying attributes,
using attribute descriptors, or descriptors for short.
Descriptors unify and generalize several different common
--- 12,22 ----
This PEP proposes changes to the introspection API for types that
! makes them look more like classes, and their instances more like
! class instances. For example, type(x) will be equivalent to
! x.__class__ for most built-in types. When C is x.__class__,
! x.meth(a) will generally be equivalent to C.meth(x, a), and
! C.__dict__ contains x's methods and other attributes.
! This PEP also introduces a new approach to specifying attributes,
using attribute descriptors, or descriptors for short.
Descriptors unify and generalize several different common
***************
*** 25,29 ****
--- 25,32 ----
generalized attribute represented by getter and setter functions.
+ Based on the generalized descriptor API, this PEP also introduces
+ a way to declare class methods and static methods.
+
Introduction
***************
*** 73,77 ****
(The last two rules together are often summarized as the
! left-to-right, depth-first rule for attribute search.)
The type-based introspection API is supported in one form or
--- 76,83 ----
(The last two rules together are often summarized as the
! left-to-right, depth-first rule for attribute search. 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.)
The type-based introspection API is supported in one form or
***************
*** 112,117 ****
processors. For example, the socket module exports the SocketType
object, but this currently doesn't tell us what methods are
! defined on socket objects. Using the class API, SocketType shows
! us exactly what the methods for socket objects are, and we can
even extract their docstrings, without creating a socket. (Since
this is a C extension module, the source-scanning approach to
--- 118,123 ----
processors. For example, the socket module exports the SocketType
object, but this currently doesn't tell us what methods are
! defined on socket objects. Using the class API, SocketType would
! show exactly what the methods for socket objects are, and we can
even extract their docstrings, without creating a socket. (Since
this is a C extension module, the source-scanning approach to
***************
*** 125,154 ****
knowable by inspection of the object's type or class, which is
accessible through obj.__class__ or type(obj). (I'm using type
! and class interchangeably, because that's the goal of the
! exercise.)
!
! (XXX static and dynamic are lousy names, because the "static"
! attributes may actually behave quite dynamically.)
! The names and values of dynamic properties are typically stored in
! a dictionary, and this dictionary is typically accessible as
! obj.__dict__. The rest of this specification is more concerned
! with discovering the names and properties of static attributes
! than with dynamic attributes.
Examples of dynamic attributes are instance variables of class
instances, module attributes, etc. Examples of static attributes
are the methods of built-in objects like lists and dictionaries,
! and the attributes of frame and code objects (c.co_code,
c.co_filename, etc.). When an object with dynamic attributes
exposes these through its __dict__ attribute, __dict__ is a static
attribute.
In the discussion below, I distinguish two kinds of objects:
regular objects (e.g. lists, ints, functions) and meta-objects.
! Meta-objects are types and classes. Meta-objects are also regular
objects, but we're mostly interested in them because they are
referenced by the __class__ attribute of regular objects (or by
! the __bases__ attribute of meta-objects).
The class introspection API consists of the following elements:
--- 131,163 ----
knowable by inspection of the object's type or class, which is
accessible through obj.__class__ or type(obj). (I'm using type
! and class interchangeably; a clumsy but descriptive term that fits
! both is "meta-object".)
! (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.)
Examples of dynamic attributes are instance variables of class
instances, module attributes, etc. Examples of static attributes
are the methods of built-in objects like lists and dictionaries,
! and the attributes of frame and code objects (f.f_code,
c.co_filename, etc.). When an object with dynamic attributes
exposes these through its __dict__ attribute, __dict__ is a static
attribute.
+ The names and values of dynamic properties are typically stored in
+ a dictionary, and this dictionary is typically accessible as
+ obj.__dict__. The rest of this specification is more concerned
+ with discovering the names and properties of static attributes
+ than with dynamic attributes; the latter are easily discovered by
+ inspection of obj.__dict__.
+
In the discussion below, I distinguish two kinds of objects:
regular objects (e.g. lists, ints, functions) and meta-objects.
! Types and classes and meta-objects. Meta-objects are also regular
objects, but we're mostly interested in them because they are
referenced by the __class__ attribute of regular objects (or by
! the __bases__ attribute of other meta-objects).
The class introspection API consists of the following elements:
***************
*** 162,170 ****
- attribute descriptors.
1. The __dict__ attribute on regular objects
A regular object may have a __dict__ attribute. If it does,
this should be a mapping (not necessarily a dictionary)
! supporting at least __getitem__, keys(), and has_key(). This
gives the dynamic attributes of the object. The keys in the
mapping give attribute names, and the corresponding values give
--- 171,183 ----
- attribute descriptors.
+ Together, these not only tell us about *all* attributes defined by
+ a meta-object, but they also help us calculate the value of a
+ specific attribute of a given object.
+
1. The __dict__ attribute on regular objects
A regular object may have a __dict__ attribute. If it does,
this should be a mapping (not necessarily a dictionary)
! supporting at least __getitem__(), keys(), and has_key(). This
gives the dynamic attributes of the object. The keys in the
mapping give attribute names, and the corresponding values give
***************
*** 179,195 ****
2. The __class__ attribute on regular objects
! A regular object may have a __class__ attributes. If it does,
! this references a meta-object. A meta-object can define static
! attributes for the regular object whose __class__ it is.
3. The __dict__ attribute on meta-objects
A meta-object may have a __dict__ attribute, of the same form
! as the __dict__ attribute for regular objects (mapping, etc).
! If it does, the keys of the meta-object's __dict__ are names of
! static attributes for the corresponding regular object. The
! values are attribute 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
--- 192,209 ----
2. The __class__ attribute on regular objects
! A regular object usually has a __class__ attribute. If it
! does, this references a meta-object. A meta-object can define
! static attributes for the regular object whose __class__ it
! is. This is normally done through the following mechanism:
3. The __dict__ attribute on meta-objects
A meta-object may have a __dict__ attribute, of the same form
! as the __dict__ attribute for regular objects (a mapping but
! not necessarily a dictionary). If it does, the keys of the
! meta-object's __dict__ are names of static attributes for the
! corresponding regular object. The values are attribute
! 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
***************
*** 208,241 ****
meta-objects, the bases. An absent __bases__ is equivalent to
an empty sequece 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
! inheritance tree, where the root of the tree is the __class__
! attribute of a regular object, and the leaves of the trees are
! meta-objects without bases. The __dict__ attributes of the
! meta-objects in the inheritance tree supply attribute
! descriptors for the regular object whose __class__ is at the
! top of the inheritance tree.
5. Precedence rules
! When two meta-objects in the inheritance tree both define an
! attribute descriptor with the same name, the left-to-right
! depth-first rule applies. (XXX define rigorously.)
When a dynamic attribute (one defined in a regular object's
__dict__) has the same name as a static attribute (one defined
! by a meta-object in the inheritance tree rooted at the regular
! object's __class__), the dynamic attribute *usually* wins, but
! for some attributes the meta-object may specify that the static
! attribute overrides the dynamic attribute.
!
! (We can't have a simples rule like "static overrides dynamic"
! or "dynamic overrides static", because some static attributes
! indeed override dynamic attributes, e.g. a key '__class__' in
! an instance's __dict__ is ignored in favor of the statically
! defined __class__ pointer, but on the other hand most keys in
! inst.__dict__ override attributes defined in inst.__class__.
! The mechanism whereby a meta-object can specify that a
! particular attribute has precedence is not yet specified.)
6. Attribute descriptors
--- 222,264 ----
meta-objects, the bases. An absent __bases__ is equivalent to
an empty sequece 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.
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.)
When a dynamic attribute (one defined in a regular object's
__dict__) has the same name as a static attribute (one defined
! by a meta-object in the inheritance graph rooted at the regular
! 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.
!
! Rationale: we can't have a simples rule like "static overrides
! dynamic" or "dynamic overrides static", because some static
! attributes indeed override dynamic attributes, e.g. a key
! '__class__' in an instance's __dict__ is ignored in favor of
! the statically defined __class__ pointer, but on the other hand
! most keys in inst.__dict__ override attributes defined in
! inst.__class__. Presence of a __set__ method on a descriptor
! indicates that this is a data descriptor. (Even read-only data
! descriptors have a __set__ method: it always raises an
! exception.) Absence of a __set__ method on a descriptor
! indicates that the descriptor isn't interested in intercepting
! assignment, and then the classic rule applies: an instance
! variable with the same name as a method hides the method until
! it is deleted.
6. Attribute descriptors
***************
*** 243,250 ****
This is where it gets interesting -- and messy. Attribute
descriptors (descriptors for short) are stored in the
! meta-object's __dict__, and have two uses: a descriptor can be
! used to get or set the corresponding attribute value on the
! (non-meta) object, and it has an additional interface that
! describes the attribute for documentation or introspection
purposes.
--- 266,274 ----
This is where it gets interesting -- and messy. Attribute
descriptors (descriptors for short) are stored in the
! meta-object's __dict__ (or in the __dict__ of one of its
! ancestors), and have two uses: a descriptor can be used to get
! or set the corresponding attribute value on the (regular,
! non-meta) object, and it has an additional interface that
! describes the attribute for documentation and introspection
purposes.
***************
*** 257,270 ****
If an object found in the meta-object's __dict__ is not an
! attribute descriptor, backward compatibility dictates
! semantics. This basically means that if it is a Python
function or an unbound method, the attribute is a method;
! otherwise, it is the default value for a data attribute.
! Backwards compatibility also dictates that (in the absence of a
! __setattr__ method) it is legal to assign to an attribute of
! type method, and that this creates a data attribute shadowing
! the method for this particular instance. However, these
! semantics are only required for backwards compatibility with
! regular classes.
The introspection API is a read-only API. We don't define the
--- 281,294 ----
If an object found in the meta-object's __dict__ is not an
! attribute descriptor, backward compatibility dictates certain
! minimal semantics. This basically means that if it is a Python
function or an unbound method, the attribute is a method;
! otherwise, it is the default value for a dynamic data
! attribute. Backwards compatibility also dictates that (in the
! absence of a __setattr__ method) it is legal to assign to an
! attribute corresponding to a method, and that this creates a
! data attribute shadowing the method for this particular
! instance. However, these semantics are only required for
! backwards compatibility with regular classes.
The introspection API is a read-only API. We don't define the
***************
*** 272,395 ****
__class__ and __bases__), nor the effect of assignment to the
items of a __dict__. Generally, such assignments should be
! considered off-limits. An extension of this PEP may define some
! semantics for some such assignments. (Especially because
! currently instances support assignment to __class__ and __dict__,
! and classes support assignment to __bases__ and __dict__.)
Specification of the attribute descriptor API
! Attribute descriptors have the following attributes. In the
examples, x is an object, C is x.__class__, x.meth() is a method,
! and x.ivar is a data attribute or instance variable.
! - name: the original attribute name. Note that because of
! aliasing and renaming, the attribute may be known under a
different name, but this is the name under which it was born.
! Example: C.meth.name == 'meth'.
! - doc: the attribute's documentation string.
! - objclass: the class that declared this attribute. The
descriptor only applies to objects that are instances of this
class (this includes instances of its subclasses). Example:
! C.meth.objclass is C.
! - kind: either "method" or "data". This distinguishes between
! methods and data attributes. The primary operation on a method
! attribute is to call it. The primary operations on a data
! attribute are to get and to set it. Example: C.meth.kind ==
! 'method'; C.ivar.kind == 'data'.
!
! - default: for optional data attributes, this gives a default or
! initial value. XXX Python has two kinds of semantics for
! referencing "absent" attributes: this may raise an
! AttributeError, or it may produce a default value stored
! somewhere in the class. There could be a flag that
! distinguishes between these two cases. Also, there could be a
! flag that tells whether it's OK to delete an attribute (and what
! happens then -- a default value takes its place, or it's truly
! gone).
!
! - attrclass: for data attributes, this can be the class of the
! attribute value, or None. If this is not None, the attribute
! value is restricted to being an instance of this class (or of a
! subclass thereof). If this is None, the attribute value is not
! constrained. For method attributes, this should normally be
! None (a class is not sufficient information to describe a method
! signature). If and when optional static typing is added to
! Python, this the meaning of this attribute may change to
! describe the type of the attribute.
!
! - signature: for methods, an object that describes the signature
! of the method. Signature objects will be described further
! below.
!
! - readonly: Boolean indicating whether assignment to this
! attribute is disallowed. This is usually true for methods.
! Example: C.meth.readonly == 1; C.ivar.readonly == 0.
!
! - get(): a function of one argument that retrieves the attribute
! value from an object. Examples: C.ivar.get(x) ~~ x.ivar;
! C.meth.get(x) ~~ x.meth.
!
! - set(): a function of two arguments that sets the attribute value
! on the object. If readonly is set, this method raises a
! TypeError exception. Example: C.ivar.set(x, y) ~~ x.ivar = y.
!
! - call(): for method descriptors, this is a function of at least
! one argument that calls the method. The first argument is the
! object whose method is called; the remaining arguments
! (including keyword arguments) are passed on to the method.
! Example: C.meth.call(x, 1, 2) ~~ x.meth(1, 2).
!
! - bind(): for method descriptiors, this is a function of one
! argument that returns a "bound method object". This in turn can
! be called exactly like the method should be called (in fact this
! is what is returned for a bound method). This is the same as
! get(). Example: C.meth.bind(x) ~~ x.meth.
!
! For convenience, __name__ and __doc__ are defined as aliases for
! name and doc. Also for convenience, calling the descriptor can do
! one of three things:
!
! - Calling a method descriptor is the same as calling its call()
! method. Example: C.meth(x, 1, 2) ~~ x.meth(1, 2).
!
! - Calling a data descriptor with one argument is the same as
! calling its get() method. Example: C.ivar(x) ~~ x.ivar.
!
! - Calling a data descriptor with two arguments is the same as
! calling its set() method. Example: C.ivar(x, y) ~~ x.ivar = y.
!
! Note that this specification does not define how to create
! specific attribute descriptors. This is up to the individual
! attribute descriptor implementations, of which there may be many.
! Specification of the signature object API
XXX
Discussion
XXX
Examples
XXX
Backwards compatibility
XXX
- Compatibility of C API
- XXX
-
Warnings and Errors
XXX
Implementation
--- 296,379 ----
__class__ and __bases__), nor the effect of assignment to the
items of a __dict__. Generally, such assignments should be
! considered off-limits. A future PEP may define some semantics for
! some such assignments. (Especially because currently instances
! support assignment to __class__ and __dict__, and classes support
! assignment to __bases__ and __dict__.)
Specification of the attribute descriptor API
! Attribute descriptors may have the following attributes. In the
examples, x is an object, C is x.__class__, x.meth() is a method,
! and x.ivar is a data attribute or instance variable. All
! attributes are optional -- a specific attribute may or may not be
! present on a given descriptor. An absent attribute means that the
! corresponding information is not available or the corresponding
! functionality is not implemented.
! - __name__: the attribute name. Because of aliasing and renaming,
! the attribute may (additionally or exclusively) be known under a
different name, but this is the name under which it was born.
! Example: C.meth.__name__ == 'meth'.
! - __doc__: the attribute's documentation string. This may be
! None.
! - __objclass__: the class that declared this attribute. The
descriptor only applies to objects that are instances of this
class (this includes instances of its subclasses). Example:
! 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. (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, class methods 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.
! Method attributes may also be callable; in this case they act as
! unbound method. Example: C.meth(C(), x) ~~ C().meth(x).
! C API
XXX
+
Discussion
XXX
+
Examples
XXX
+
Backwards compatibility
XXX
Warnings and Errors
XXX
+
Implementation
***************
*** 403,412 ****
here, see the file Lib/test/test_descr.py.
! Note: the code in this branch goes beyond this PEP; it is also
! on the way to implementing PEP 253 (Subtyping Built-in Types).
References
XXX
Copyright
--- 387,398 ----
here, see the file Lib/test/test_descr.py.
! Note: the code in this branch goes way beyond this PEP; it is also
! the experimentation area for PEP 253 (Subtyping Built-in Types).
+
References
XXX
+
Copyright