[Python-checkins] peps: Added content. First real version of this PEP

georg.brandl python-checkins at python.org
Wed Mar 23 21:22:47 CET 2011


http://hg.python.org/peps/rev/b4938632c5c2
changeset: 28:b4938632c5c2
user: Paul Prescod <prescod at prescod.net>
date: Fri Jul 21 21:14:26 2000 +0000
summary:
 Added content. First real version of this PEP
files:
 pep-0213.txt | 191 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 191 insertions(+), 0 deletions(-)
diff --git a/pep-0213.txt b/pep-0213.txt
--- a/pep-0213.txt
+++ b/pep-0213.txt
@@ -6,6 +6,197 @@
 Status: Incomplete
 
 
+Introduction
+
+ It is possible (and even relatively common) in Python code and 
+ in extension modules to "trap" when an instance's client code 
+ attempts to set an attribute and execute code instead. In other 
+ words it is possible to allow users to use attribute assignment/
+ retrieval/deletion syntax even though the underlying implementation 
+ is doing some computation rather than directly modifying a
+ binding.
+
+ This PEP describes a feature that makes it easier, more efficient
+ and safer to implement these handlers for Python instances.
+
+Justification
+
+ Scenario 1:
+
+ You have a deployed class that works on an attribute named
+ "stdout". After a while, you think it would be better to
+ check that stdout is really an object with a "write" method
+ at the moment of assignment. Rather than change to a
+ setstdout method (which would be incompatible with deployed
+ code) you would rather trap the assignment and check the
+ object's type.
+
+ Scenario 2:
+
+ You want to be as compatible as possible with an object 
+ model that has a concept of attribute assignment. It could
+ be the W3C Document Object Model or a particular COM 
+ interface (e.g. the PowerPoint interface). In that case
+ you may well want attributes in the model to show up as
+ attributes in the Python interface, even though the 
+ underlying implementation may not use attributes at all.
+
+ Scenario 3:
+
+ A user wants to make an attribute read-only.
+
+ In short, this feature allows programmers to separate the 
+ interface of their module from the underlying implementation
+ for whatever purpose. Again, this is not a new feature but
+ merely a new syntax for an existing convention.
+
+Current Solution
+
+ To make some attributes read-only:
+
+ class foo:
+ def __setattr__( self, name, val ):
+ if name=="readonlyattr":
+ raise TypeError
+ elif name=="readonlyattr2":
+ raise TypeError
+ ...
+ else:
+ self.__dict__["name"]=val
+
+ This has the following problems:
+
+ 1. The creator of the method must be intimately aware of whether
+ somewhere else in the class hiearchy __setattr__ has also been
+ trapped for any particular purpose. If so, she must specifically
+ call that method rather than assigning to the dictionary. There
+ are many different reasons to overload __setattr__ so there is a
+ decent potential for clashes. For instance object database
+ implementations often overload setattr for an entirely unrelated
+ purpose.
+
+ 2. The string-based switch statement forces all attribute handlers 
+ to be specified in one place in the code. They may then dispatch
+ to task-specific methods (for modularity) but this could cause
+ performance problems.
+
+ 3. Logic for the setting, getting and deleting must live in 
+ __getattr__, __setattr__ and __delattr__. Once again, this can be
+ mitigated through an extra level of method call but this is 
+ inefficient.
+
+Proposed Syntax
+ 
+ Special methods should declare themselves with declarations of the
+ following form:
+
+ class x:
+ def __attr_XXX__(self, op, val ):
+ if op=="get":
+ return someComputedValue(self.internal)
+ elif op=="set":
+ self.internal=someComputedValue(val)
+ elif op=="del":
+ del self.internal
+
+ Client code looks like this:
+
+ fooval=x.foo
+ x.foo=fooval+5
+ del x.foo
+
+ Semantics
+ 
+ Attribute references of all three kinds should call the method.
+ The op parameter can be "get"/"set"/"del". Of course this string
+ will be interned so the actual checks for the string will be
+ very fast.
+ 
+ It is disallowed to actually have an attribute named XXX in the
+ same instance as a method named __attr_XXX__.
+
+ An implementation of __attr_XXX__ takes precedence over an
+ implementation of __getattr__ based on the principle that
+ __getattr__ is supposed to be invoked only after finding an
+ appropriate attribute has failed.
+
+ An implementation of __attr_XXX__ takes precedence over an
+ implementation of __setattr__ in order to be consistent. The
+ opposite choice seems fairly feasible also, however. The same
+ goes for __del_y__.
+
+Proposed Implementation
+ 
+ There is a new object type called an attribute access handler. 
+ Objects of this type have the following attributes:
+
+ name (e.g. XXX, not __attr__XXX__
+ method (pointer to a method object
+ 
+ In PyClass_New, methods of
+ the appropriate form will be detected and converted into objects
+ (just like unbound method objects). If there are any attribute access
+ handlers in an instance at all, a flag is set. Let's call 
+ it "I_have_computed_attributes" for now. Derived classes inherit
+ the flag from base classes. Instances inherit the flag from
+ classes.
+ 
+ A get proceeds as usual until just before the object is returned.
+ In addition to the current check whether the returned object is a
+ method it would also check whether a returned object is an access
+ handler. If so, it would invoke the getter method and return
+ the value. To remove an attribute access handler you could directly
+ fiddle with the dictionary.
+ 
+ A set proceeds by checking the "I_have_computed_attributes" flag. If
+ it is not set, everything proceeds as it does today. If it is set
+ then we must do a dictionary get on the requested object name. If it
+ returns an attribute access handler then we call the setter function
+ with the value. If it returns any other object then we discard the
+ result and continue as we do today. Note that having an attribute
+ access handler will mildly affect attribute "setting" performance for
+ all sets on a particular instance, but no more so than today, using
+ __setattr__. Gets are more efficient than they are today with
+ __getattr__.
+ 
+ The I_have_computed_attributes flag is intended to eliminate the
+ performance degradation of an extra "get" per "set" for objects not
+ using this feature. Checking this flag should have miniscule
+ performance implications for all objects.
+
+ The implementation of delete is analogous to the implementation
+ of set.
+
+Caveats
+
+ 1. You might note that I have not proposed any logic to keep
+ the I_have_computed_attributes flag up to date as attributes
+ are added and removed from the instance's dictionary. This is
+ consistent with current Python. If you add a __setattr__ method
+ to an object after it is in use, that method will not behave as
+ it would if it were available at "compile" time. The dynamism is
+ arguably not worth the extra implementation effort. This snippet
+ demonstrates the current behavior:
+
+	>>> def prn(*args):print args
+	>>> class a:
+	... __setattr__=prn
+ >>> a().foo=5
+	(<__main__.a instance at 882890>, 'foo', 5)
+
+	>>> class b: pass 
+	>>> bi=b()
+	>>> bi.__setattr__=prn
+	>>> b.foo=5
+
+ 2. Assignment to __dict__["XXX"] can overwrite the attribute
+	access handler for __attr_XXX__. Typically the access handlers will
+ store information away in private __XXX variables
+
+ 3. An attribute access handler that attempts to call setattr or getattr
+ on the object itself can cause an infinite loop (as with __getattr__)
+ Once again, the solution is to use a special (typically private) 
+ variable such as __XXX.
 
 Local Variables:
 mode: indented-text
-- 
Repository URL: http://hg.python.org/peps


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /