Re: [Python-Dev] PEP 246: lossless and stateless
On 2005 Jan 15, at 01:02, Glyph Lefkowitz wrote: ... Now, we have nowhere to hide PointPen's state on SegmentPen - and why were we trying to in the first place? It's a horrible breach of encapsulation. The whole *point* of adapters is to convert between *different* interfaces, not merely to rename methods on the same interface, or to add extra methods that work on the same data. To me, A common implementation technique, when you'd love to associate some extra data to an object, but can't rely on the object having a __dict__ to let you do that conveniently, is to have an auxiliary dict of bunches of extra data, keyed by object's id(). It's a bit messier, in that you have to deal with cleanup issues when the object goes away, as well as suffer an extra indirectness; but in many use cases it's quite workable. I don't see doing something like myauxdict[id(obj)] = {'foo': 'bar'} as "terribly invasive", and therefore neither do I see obj.myauxfoo = 'bar' as any more invasive -- just two implementation techniques for the same task with somewhat different tradeoffs. The task, associating extra data with obj without changing obj type's source, won't just go away. Incidentally, the realization of this equivalence was a key step in my very early acceptance of Python. In the first few days, the concept "some external code might add an attribute to obj -- encapsulation breach!" made me wary; then CLICK, the first time I had to associate extra data to an object and realized the alleged ``breach'' was just a handy implementation help for the task I needed anyway, I started feeling much better about it. Adapter use cases exist for all three structures: 1. the adapter just needs to change method names and signatures or combine existing methods of the object, no state additions; 2. the adapter needs to add some per-object state, which must be shared among different adapters which may simultaneously exist on the same object; 3. the adapter needs to add some per-adapter state, which must be distinct among different adapters which may simultaneously exist on the same object. Case [1] is simplest because you don't have to wonder whether [2] or [3] are better, which may be why it's being thought of as "best". Case [3] may be dubious when we talk about AUTOMATIC adaptation, because in [3] making and using two separate adapters has very different semantics from making just one adapter and using it twice. When you build the adapter explicitly of course you have full control and hopefully awareness of that. For example, in Model/View, clearly you want multiple views on the same model and each view may well need a few presentation data of its own; if you think of it as adaptation, it's definitely a [3]. But do we really want _automatic_ adaptation -- passing a Model to a function which expects a View, and having some kind of default presentation data be used to make a default view on it? That, I guess, is the dubious part. "different interfaces" means that the actual meaning of the operations is different - sometimes subtly, sometimes dramatically. There has to be enough information in one interface to get started on the implementation of another, but the fact that such information is necessary doesn't mean it is sufficient. It doesn't mean that there is enough information in the original object to provide a complete implementation of a different interface. If there were enough information, why not just implement all of your interfaces on the original class? In the case of our hypothetical cSegmentPen, we *already* have to modify the implementation of the original class to satisfy the needs of a "stateless" adapter. When you're modifying cSegmentPen, why not just add the methods that you wanted in the first place? Reason #1: because the author of the cSegmentPen code cannot assume he or she knows about all the interfaces to which a cSegmentPen might be adapted a la [3]. If he or she provides a __dict__, or makes cSegmentPen weakly referenceable, all [3]-like adaptation needs are covered at one (heh heh) stroke. Here's another example: I have a business logic class which lives in an object database, typically used for a web application. I convert this into a desktop application. Now, I want to adapt IBusinessThunk to IGtkUIPlug. In the process of doing so, I have to create a GTK widget, loaded out of some sort of resource file, and put it on the screen. I have to register event handlers which are associated with that adapter. OK, a typical case of model/view and thus a [3]. The issue is whether you want adaptation to be automatic or explicit, in such cases. Most of the other use-cases I can think of are like the one James mentions, where we really are using adaptation to shuffle around some method names and provide simple glossing over totally isomorphic functionality to provide backwards (or sideways, in the case of almost-identical libraries provided on di
Re: [Python-Dev] PEP 246: lossless and stateless
On 2005 Jan 15, at 02:30, Phillip J. Eby wrote: is requested. It's too bad Python doesn't have some sort of deallocation hook you could use to get notified when an object goes away. Oh well. For weakly referenceable objects, it does. Giving one to other objects would be almost isomorphic to making every object weakly referenceable, wouldn't it? Or am I missing something...? Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
Phillip J. Eby wrote: > At 07:02 PM 1/14/05 -0500, Glyph Lefkowitz wrote: > >For the sake of argument, let's say that SegmentPen is a C type, > >which does not have a __dict__, and that PointPen is a Python > >adapter for it, in a different project. > > There are multiple implementation alternatives possible here; it > isn't necessary that the state be hidden there. The point is that, > given the same SegmentPen, we want to get the same PointPen each time > we *implicitly* adapt, in order to avoid violating the "naive" > developer's mental model of what adaptation is -- i.e. an extension > of the object's state, not a new object with independent state. > > One possible alternative implementation is to use a dictionary from > object id to a 'weakref(ob),state' tuple, with the weakref set up to > remove the entry when 'ob' goes away. Adapters would then have a > pointer to their state object and a pointer to the adaptee. As long > as an adapter lives, the adaptee lives, so the state remains valid. > Or, if no adapters remain, but the adaptee still lives, then so does > the state which can be resurrected when a new adapter is requested. > It's too bad Python doesn't have some sort of deallocation hook you > could use to get notified when an object goes away. Oh well. That sounds extremely complicated as apposed to just storing the sate where it most logically belongs: on the adapter. And all that to work around a problem that I'm not convinced needs solving or even exists. At the very least *I* don't care about it in my use case. > Anyway, as you and I have both pointed out, sticky adaptation is an > important use case; when you need it, you really need it. Maybe I missed it, but was there an example posted of when "sticky adaptation" is needed? It's not at all clear to me that "sticky" behavior is the best default behavior, even with implicit adoptation. Would anyone in their right mind expect the following to return [0, 1, 2, 3, 4, 5] instead of [0, 1, 2, 0, 1, 2]? >>> from itertools import * >>> seq = range(10) >>> list(chain(islice(seq, 3), islice(seq, 3))) [0, 1, 2, 0, 1, 2] >>> Just ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
On Fri, 14 Jan 2005 20:06:22 -0500, Phillip J. Eby <[EMAIL PROTECTED]> wrote: > >My feeling here was not that people thought that stateless adapters > >were in general intrinsically better -- just when the adaptation was > >going to be done implicitly (e.g. by type declarations). > > Yes, exactly. :) In which case, given that there is no concept in PEP 246 of implicit adaptation, can we please make a clear separation of this discussion from PEP 246? (The current version of the PEP makes no mention of transitive adaptation, as optional or required behaviour, which is the only other example of implicit adaptation I can think of). I think there are the following distinct threads of discussion going on at the moment: * Details of what should be in PEP 246 * Discussions spinning off from Guido's type-declaration-as-adaptation proposal * Discussion of what counts as a "good" adapter * Philip's new generic function / ducy typing proposals Is that even close to others' understanding? Just trying to keep my brain from exploding :-) Paul. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 10:39 AM 1/15/05 +0100, Just van Rossum wrote: That sounds extremely complicated as apposed to just storing the sate where it most logically belongs: on the adapter. Oh, the state will be on the adapter all right. It's just that for type declarations, I'm saying the system should return the *same* adapter each time. And all that to work around a problem that I'm not convinced needs solving or even exists. At the very least *I* don't care about it in my use case. > Anyway, as you and I have both pointed out, sticky adaptation is an > important use case; when you need it, you really need it. Maybe I missed it, but was there an example posted of when "sticky adaptation" is needed? No; but Glyph and I have independent use cases for them. Here's one of mine: code generation from a UML or MOF model. The model classes can't contain methods or data for doing code generation, unless you want to cram every possible kind of code generation into them. The simple thing to do is to adapt them to a PythonCodeGenerator or an SQLCodeGenerator or what-have-you, and to do so stickily. (Because a code generator may need to walk over quite a bit of the structure while keeping state for different things being generated.) You *could* keep state in an external dictionary, of course, but it's much easier to use sticky adapters. It's not at all clear to me that "sticky" behavior is the best default behavior, even with implicit adoptation. Would anyone in their right mind expect the following to return [0, 1, 2, 3, 4, 5] instead of [0, 1, 2, 0, 1, 2]? >>> from itertools import * >>> seq = range(10) >>> list(chain(islice(seq, 3), islice(seq, 3))) [0, 1, 2, 0, 1, 2] >>> I don't understand why you think it would. What does islice have to do with adaptation? ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 01:20 PM 1/15/05 +, Paul Moore wrote: I think there are the following distinct threads of discussion going on at the moment: * Details of what should be in PEP 246 * Discussions spinning off from Guido's type-declaration-as-adaptation proposal My understanding was that the first needed to be considered in context of the second, since it was the second which gave an implicit blessing to the first. PEP 246 had languished in relative obscurity for a long time until Guido's blessing it for type declarations brought it back into the spotlight. So, I thought it important to frame its discussion in terms of its use for type declaration. * Discussion of what counts as a "good" adapter Alex was originally trying to add to PEP 246 some recommendations regarding "good" vs. "bad" adaptation, so this is actually part of "what should be in PEP 246" * Philip's new generic function / ducy typing proposals And of course this one is an attempt to unify everything and replace PEP 245 (not 246) with a hopefully more pythonic way of defining interfaces and adapters. I hope to define a "relatively safe" subset of PEP 246 for type declarations that can be done automatically by Python, in a way that's also conceptually compatible with COM and Java casting (possibly making Jython and IronPython's lives a little easier re: type declarations). ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
Phillip J. Eby wrote: > >It's not at all clear to me that "sticky" behavior is the best > >default behavior, even with implicit adoptation. Would anyone in > >their right mind expect the following to return [0, 1, 2, 3, 4, 5] > >instead of [0, 1, 2, 0, 1, 2]? > > > > >>> from itertools import * > > >>> seq = range(10) > > >>> list(chain(islice(seq, 3), islice(seq, 3))) > > [0, 1, 2, 0, 1, 2] > > >>> > > I don't understand why you think it would. What does islice have to > do with adaptation? islice() takes an iterator, yet I give it a sequence. It calls iter(seq), which I see as a form of adaptation (maybe you don't). Sticky adaptation would not be appropriate here, even though the adaptation is implicit. Just ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Re: Re: PEP 246: LiskovViolation as a name
"Skip Montanaro" <[EMAIL PROTECTED]> wrote in message news:[EMAIL PROTECTED] > The first example here: >http://www.compulink.co.uk/~querrid/STANDARD/lsp.htm > Looks pretty un-extreme to me. To both summarize and flesh out the square-rectangle example: Q. Is a square 'properly' a rectangle? A. Depends on 'square' and 'rectangle'. * A static, mathematical square is a static, mathematical rectangle just fine, once width and height are aliased (adapted?) to edge. The only 'behaviors' are to report size and possibly derived quantities like diagonal and area. * Similarly, a dynamic, zoomable square is a zoomable rectangle. * But a square cannot 'properly' be a fully dynamic rectangle that can mutate to a dissimilar shape, and must when just one dimension is changed -- unless shape mutation is allowed to fail or unless the square is allowed to mutate itself into a rectangle. So it seems easily possible to introduce Liskov violations when adding behavior to a general superclass. Terry J. Reedy ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 05:32 PM 1/15/05 +0100, Just van Rossum wrote: Phillip J. Eby wrote: > >It's not at all clear to me that "sticky" behavior is the best > >default behavior, even with implicit adoptation. Would anyone in > >their right mind expect the following to return [0, 1, 2, 3, 4, 5] > >instead of [0, 1, 2, 0, 1, 2]? > > > > >>> from itertools import * > > >>> seq = range(10) > > >>> list(chain(islice(seq, 3), islice(seq, 3))) > > [0, 1, 2, 0, 1, 2] > > >>> > > I don't understand why you think it would. What does islice have to > do with adaptation? islice() takes an iterator, yet I give it a sequence. No, it takes an *iterable*, both practically and according to its documentation: >>> help(itertools.islice) Help on class islice in module itertools: class islice(__builtin__.object) | islice(iterable, [start,] stop [, step]) --> islice object | | ... [snip rest] If you think about the iterator and iterable protocols a bit, you'll see that normally the adaptation goes the *other* way: you can pass an iterator to something that expects an iterable, as long as it doesn't need reiterability. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 10:35 AM 1/15/05 +0100, Alex Martelli wrote: On 2005 Jan 15, at 02:30, Phillip J. Eby wrote: is requested. It's too bad Python doesn't have some sort of deallocation hook you could use to get notified when an object goes away. Oh well. For weakly referenceable objects, it does. Giving one to other objects would be almost isomorphic to making every object weakly referenceable, wouldn't it? Or am I missing something...? I meant if there was some way to listen for a particular object's allocation, like sticking all the pointers you were interested in into a big dictionary with callbacks and having a callback run whenever an object's refcount reaches zero. It's doubtless completely impractical, however. I think we can probably live with only weak-referenceable objects being seamlessly sticky, if that's a word. :) Actually, I've just gotten to the part of the PEP where I have to deal with stateful adapters and state retention, and I think I'm going to use this terminology for the three kinds of adapters: * operations (no adapter class needed) * extenders (operations + a consistent state that conceptually adds state to the base object rather than creating an object w/separate lifetime) * "volatile", "inconsistent", or "disposable" adapters (state may be lost or multiplied if passed to different routines) The idea is to make it really easy to make any of these, but for the last category you should have to explicitly declare that you *want* volatility (or at least that you are willing to accept it, if the target type is not weak-referenceable). In this way, all three kinds of adaptation may be allowed, but it takes one extra step to create a potentially "bad" adapter. Right now, people often create volatile adapters even if what they want is an extender ("sticky adapter"), because it's more work to make a functioning extender, not because they actually want volatility. So, let's reverse that and make it easier to create extenders than it is to create volatile adapters. And, since in some cases an extender won't be possible even when it's what you want, we could go ahead and allow type declarations to make them, as long as the creator has specified that they're volatile. Meanwhile, all three kinds of adapters should avoid accidental implicit transitivity by only adapting the "original object". (Unless, again, there is some explicit choice to do otherwise.) This makes the type declaration system a straightforward extension of the COM QueryInterface and Java casting models, where an object's "true identity" is always preserved regardless of which interface you access its operations through. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
On 2005-01-15, at 18.06, Phillip J. Eby wrote: At 05:32 PM 1/15/05 +0100, Just van Rossum wrote: Phillip J. Eby wrote: > >It's not at all clear to me that "sticky" behavior is the best > >default behavior, even with implicit adoptation. Would anyone in > >their right mind expect the following to return [0, 1, 2, 3, 4, 5] > >instead of [0, 1, 2, 0, 1, 2]? > > > > >>> from itertools import * > > >>> seq = range(10) > > >>> list(chain(islice(seq, 3), islice(seq, 3))) > > [0, 1, 2, 0, 1, 2] > > >>> > > I don't understand why you think it would. What does islice have to > do with adaptation? islice() takes an iterator, yet I give it a sequence. No, it takes an *iterable*, both practically and according to its documentation: But it _does_ perform an implicit adaptation, via PyObject_GetIter. A list has no next()-method, but iter(list()) does. //Simon ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 10:48 PM 1/15/05 +0100, Simon Percivall wrote: On 2005-01-15, at 18.06, Phillip J. Eby wrote: At 05:32 PM 1/15/05 +0100, Just van Rossum wrote: Phillip J. Eby wrote: > >It's not at all clear to me that "sticky" behavior is the best > >default behavior, even with implicit adoptation. Would anyone in > >their right mind expect the following to return [0, 1, 2, 3, 4, 5] > >instead of [0, 1, 2, 0, 1, 2]? > > > > >>> from itertools import * > > >>> seq = range(10) > > >>> list(chain(islice(seq, 3), islice(seq, 3))) > > [0, 1, 2, 0, 1, 2] > > >>> > > I don't understand why you think it would. What does islice have to > do with adaptation? islice() takes an iterator, yet I give it a sequence. No, it takes an *iterable*, both practically and according to its documentation: But it _does_ perform an implicit adaptation, via PyObject_GetIter. First, that's not implicit. Second, it's not adaptation, either. PyObject_GetIter invokes the '__iter__' method of its target -- a method that is part of the *iterable* interface. It has to have something that's *already* iterable; it can't "adapt" a non-iterable into an iterable. Further, if calling a method of an interface that you already have in order to get another object that you don't is adaptation, then what *isn't* adaptation? Is it adaptation when you call 'next()' on an iterator? Are you then "adapting" the iterator to its next yielded value? No? Why not? It's a special method of the "iterator" interface, just like __iter__ is a special method of the "iterable" interface. So, I can't see how you can call one adaptation, but not the other. My conclusion: neither one is adaptation. A list has no next()-method, but iter(list()) does. But a list has an __iter__ method, so therefore it's an iterable. That's what defines an iterable: it has an __iter__ method. It would only be adaptation if lists *didn't* have an __iter__ method. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
Phillip J. Eby wrote: > >But it _does_ perform an implicit adaptation, via PyObject_GetIter. > > First, that's not implicit. Second, it's not adaptation, either. > PyObject_GetIter invokes the '__iter__' method of its target -- a > method that is part of the *iterable* interface. It has to have > something that's *already* iterable; it can't "adapt" a non-iterable > into an iterable. > > Further, if calling a method of an interface that you already have in > order to get another object that you don't is adaptation, then what > *isn't* adaptation? Is it adaptation when you call 'next()' on an > iterator? Are you then "adapting" the iterator to its next yielded > value? That's one (contrived) way of looking at it. Another is that y = iter(x) adapts the iterable protocol to the iterator protocol. I don't (yet) see why a bit of state disqualifies this from being called adaptation. > No? Why not? It's a special method of the "iterator" interface, > just like __iter__ is a special method of the "iterable" interface. The difference it that the result of .next() doesn't have a specified interface. > So, I can't see how you can call one adaptation, but not the other. > My conclusion: neither one is adaptation. Maybe... Just ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
On 2005-01-15, at 23.50, Just van Rossum wrote: Phillip J. Eby wrote: But it _does_ perform an implicit adaptation, via PyObject_GetIter. First, that's not implicit. Second, it's not adaptation, either. PyObject_GetIter invokes the '__iter__' method of its target -- a method that is part of the *iterable* interface. It has to have something that's *already* iterable; it can't "adapt" a non-iterable into an iterable. Further, if calling a method of an interface that you already have in order to get another object that you don't is adaptation, then what *isn't* adaptation? Is it adaptation when you call 'next()' on an iterator? Are you then "adapting" the iterator to its next yielded value? That's one (contrived) way of looking at it. Another is that y = iter(x) adapts the iterable protocol to the iterator protocol. Especially since an iterable can also be an object without an __iter__ method but with a __getitem__ method. Calling __iter__ might get an iterator, but calling __getitem__ does not. That seems like adaptation. No? It's still not clear to me, as this shows, exactly what counts as what in this game. //Simon ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
On Jan 15, 2005, at 6:02 PM, Simon Percivall wrote: On 2005-01-15, at 23.50, Just van Rossum wrote: Phillip J. Eby wrote: But it _does_ perform an implicit adaptation, via PyObject_GetIter. First, that's not implicit. Second, it's not adaptation, either. PyObject_GetIter invokes the '__iter__' method of its target -- a method that is part of the *iterable* interface. It has to have something that's *already* iterable; it can't "adapt" a non-iterable into an iterable. Further, if calling a method of an interface that you already have in order to get another object that you don't is adaptation, then what *isn't* adaptation? Is it adaptation when you call 'next()' on an iterator? Are you then "adapting" the iterator to its next yielded value? That's one (contrived) way of looking at it. Another is that y = iter(x) adapts the iterable protocol to the iterator protocol. Especially since an iterable can also be an object without an __iter__ method but with a __getitem__ method. Calling __iter__ might get an iterator, but calling __getitem__ does not. That seems like adaptation. No? It's still not clear to me, as this shows, exactly what counts as what in this game. I think that's wrong. To spell iter() in an adapter/interface world, I'd spell iter(obj) as: adapt(obj, IIterable).iterator() Then, list, tuple, dict objects would specify that they implement IIterable. There is a default adapter from object->IIterable which provides a .iterate() method which creates an iterator that uses __getitem__ on the adaptee. In my opinion, adapters provide a different view of an object. I can see treating list "as a" iterable, but not "as a" iterator. James ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] Exceptions *must*? be old-style classes?
Phillip J. Eby wrote (in http://mail.python.org/pipermail/python-dev/2005-January/050854.html) > * Classic class support is a must; exceptions are still required to be > classic, and even if they weren't in 2.5, backward compatibility should be > provided for at least one release. The base of the Exception hierarchy happens to be a classic class. But why are they "required" to be classic? More to the point, is this a bug, a missing feature, or just a bug in the documentation for not mentioning the restriction? You can inherit from both Exception and object. (Though it turns out you can't raise the result.) My first try with google failed to produce an explanation -- and I'm still not sure I understand, beyond "it doesn't happen to work at the moment." Neither the documentation nor the tutorial mention this restriction. http://docs.python.org/lib/module-exceptions.html http://docs.python.org/tut/node10.html#SECTION001050 I didn't find any references to this restriction in exception.c. I did find some code implying this in errors.c and ceval.c, but that wouldn't have caught my eye if I weren't specifically looking for it *after* having just read the discussion about (rejected) PEP 317. -jJ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Exceptions *must*? be old-style classes?
> The base of the Exception hierarchy happens to be a classic class. > But why are they "required" to be classic? > > More to the point, is this a bug, a missing feature, or just a bug in > the documentation for not mentioning the restriction? It's an unfortunate feature; it should be mentioned in the docs; it should also be fixed, but fixing it isn't easy (believe me, or it would have been fixed in Python 2.2). To be honest, I don't recall the exact reasons why this wasn't fixed in 2.2; I believe it has something to do with the problem of distinguishing between string and class exception, and between the various forms of raise statements. I think the main ambiguity is raise "abc", which could be considered short for raise str, "abc", but that would be incompatible with except "abc". I also think that the right way out of there is to simply hardcode a check that says that raise "abc" raises a string exception and raising any other instance raises a class exception. But there's a lot of code that has to be changed. It's been suggested that all exceptions should inherit from Exception, but this would break tons of existing code, so we shouldn't enforce that until 3.0. (Is there a PEP for this? I think there should be.) -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 11:50 PM 1/15/05 +0100, Just van Rossum wrote: Phillip J. Eby wrote: > >But it _does_ perform an implicit adaptation, via PyObject_GetIter. > > First, that's not implicit. Second, it's not adaptation, either. > PyObject_GetIter invokes the '__iter__' method of its target -- a > method that is part of the *iterable* interface. It has to have > something that's *already* iterable; it can't "adapt" a non-iterable > into an iterable. > > Further, if calling a method of an interface that you already have in > order to get another object that you don't is adaptation, then what > *isn't* adaptation? Is it adaptation when you call 'next()' on an > iterator? Are you then "adapting" the iterator to its next yielded > value? That's one (contrived) way of looking at it. Another is that y = iter(x) adapts the iterable protocol to the iterator protocol. I don't (yet) see why a bit of state disqualifies this from being called adaptation. Well, if you go by the GoF "Design Patterns" book, this is actually what's called an "Abstract Factory": "Abstract Factory: Provide an interface for creating ... related or dependent objects without specifying their concrete classes." So, 'iter()' is an abstract factory that creates an iterator without needing to specify the concrete class of iterator you want. This is a much closer fit for what's happening than the GoF description of "Adapter": "Adapter: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces." IMO, it's quite "contrived" to try and squeeze iteration into this concept, compared to simply saying that 'iter()' is an abstract factory that creates "related or dependent objects". While it has been pointed out that the GoF book is not handed down from heaven or anything, its terminology is certainly widely used to describe certain patterns of programming. If you read their full description of the adapter pattern, nothing in it is about automatically getting an adapter based on an interface. It's just about the idea of *using* an adapter that you already have, and it's strongly implied that you only use one adapter for a given source and destination that need adapting, not create lots of instances all over the place. So really, PEP 246 'adapt()' (like 'iter()') is more about the Abstract Factory pattern. It just happens in the case of PEP 246 that it's an Abstract Factory that *can* create adapters, but it's not restricted to handing out *just* adapters. It can also be used to create views, iterators, and whatever else you like. But that's precisely what makes it problematic for use as a type declaration mechanism, because you run the risk of it serving up entirely new objects that aren't just interface transformers. And of course, that's why I think that you should have to declare that you really want to use it for type declarations, if in fact it's allowed at all. Explicit use of 'adapt()', on the other hand, can safely create whatever objects you want. Oh, one other thing -- distinguishing between "adapters" and merely "related" objects allows you to distinguish whether you should adapt the object or what it wraps. A "related" object (like an iterator) is a separate object, so it's safe to adapt it to other things. An actual *adapter* is not a separate object, it's an extension of the object it wraps. So, it should not be re-adapted when adapting again; instead the underlying object should be adapted. So, while I support in principle all the use cases for "adaptation" (so-called) that have been discussed here, I think it's important to refine our terminology to distinguish between GoF "adapters" and "things you might want to create with an abstract factory", because they have different requirements and support different use cases. We have gotten a little bogged down by our comparisons of "good" and "bad" adapters; perhaps to move forward we should distinguish between "adapters" and "views", and say that an iterator is an example of a view: you may have more than one view on the same thing, and although a view depends on the thing it "views", it doesn't really "convert an interface"; it provides distinct functionality on a per-view basis. Currently, PEP 246 'adapt()' is used "in the field" to create both adapters and views, because 1) it's convenient, and 2) it can. :) However, for type declarations, I think it's important to distinguish between the two, to avoid implicit creation of additional views. A view needs to be managed within the scope that it applies to. By that, I mean for example that a 'for' loop creates an iterator view and then manages it within the scope of the loop. However, if you need the iterator to remain valid outside the 'for' loop, you may need to first call 'iter()' to get an explicit iterator you can hold on to. Similarly, if you have a file that you are
Re: [Python-Dev] Exceptions *must*? be old-style classes?
On 2005-01-16, at 02.57, Guido van Rossum wrote: It's been suggested that all exceptions should inherit from Exception, but this would break tons of existing code, so we shouldn't enforce that until 3.0. (Is there a PEP for this? I think there should be.) What would happen if Exception were made a new-style class, enforce inheritance from Exception for all new-style exceptions, and allow all old-style exceptions as before. Am I wrong in assuming that only the most esoteric exceptions inheriting from Exception would break by Exception becoming new-style? //Simon ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246: lossless and stateless
At 08:13 PM 1/15/05 -0500, James Y Knight wrote: On Jan 15, 2005, at 6:02 PM, Simon Percivall wrote: On 2005-01-15, at 23.50, Just van Rossum wrote: Phillip J. Eby wrote: But it _does_ perform an implicit adaptation, via PyObject_GetIter. First, that's not implicit. Second, it's not adaptation, either. PyObject_GetIter invokes the '__iter__' method of its target -- a method that is part of the *iterable* interface. It has to have something that's *already* iterable; it can't "adapt" a non-iterable into an iterable. Further, if calling a method of an interface that you already have in order to get another object that you don't is adaptation, then what *isn't* adaptation? Is it adaptation when you call 'next()' on an iterator? Are you then "adapting" the iterator to its next yielded value? That's one (contrived) way of looking at it. Another is that y = iter(x) adapts the iterable protocol to the iterator protocol. Especially since an iterable can also be an object without an __iter__ method but with a __getitem__ method. Calling __iter__ might get an iterator, but calling __getitem__ does not. That seems like adaptation. No? It's still not clear to me, as this shows, exactly what counts as what in this game. I think that's wrong. To spell iter() in an adapter/interface world, I'd spell iter(obj) as: adapt(obj, IIterable).iterator() Then, list, tuple, dict objects would specify that they implement IIterable. There is a default adapter from object->IIterable which provides a .iterate() method which creates an iterator that uses __getitem__ on the adaptee. In my opinion, adapters provide a different view of an object. I can see treating list "as a" iterable, but not "as a" iterator. Uh oh. I just used "view" to describe an iterator as a view on an iterable, as distinct from an adapter that adapts a sequence so that it's iterable. :) I.e., using "view" in the MVC sense where a given Model might have multiple independent Views. We really need to clean up our terminology somehow, and I may need to rewrite some parts of my PEP-in-progress. I had been using the term "volatile adapter" for what I'd written so far, but by the time I got to the part where I had to explain how to actually *make* volatile adapters, I realized that I was right before: they aren't adapters just because PEP 246 'adapt()' can be used to create them. They're just something *else* that's convenient to create with 'adapt()' besides adapters. Calling them even "volatile adapters" just confuses them with "real" adapters. On the *other* hand, maybe we should just call GoF adapters "extenders" (since they extend the base object with a new interface or extended functionality, but aren't really separate objects) and these other things like iterators and views should be called "accessories", which implies you have lots of them and although they "accessorize" an object, they are themselves individual objects. (Whereas an extender becomes conceptually "part of" the thing it extends.) It's then also clearer that it makes no sense to have a type declaration ever cause you to end up with a new accessory, as opposed to an extender that's at least figuratively always there. What do y'all think? Is that a better way to distinguish kinds of "adapters"? (I.e. extenders versus accessories) Or does somebody have better words we can use? ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Exceptions *must*? be old-style classes?
At 05:57 PM 1/15/05 -0800, Guido van Rossum wrote: It's been suggested that all exceptions should inherit from Exception, but this would break tons of existing code, so we shouldn't enforce that until 3.0. (Is there a PEP for this? I think there should be.) Couldn't we require new-style exceptions to inherit from Exception? Since there are no new-style exceptions that work now, this can't break existing code. Then, the code path is just something like: if isinstance(ob,Exception): # it's an exception, use its type else: # all the other tests done now This way, the other tests that would be ambiguous wrt new-style classes can be skipped, but non-Exception classic classes would still be handled by the existing checks. Or am I missing something? ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] PEP 246, Feedback Request
I started to edit the PEP, but found that we really don't have any consensus on a great many items. The following is a bunch of topics, and a proposed handling of those topics. A bulk of this comes from a phone chat I had with Alex this past afternoon, and other items come from my understanding of the mailing list, or prior conversations with Phillip, among others. It's a strawman. I'd really very much like feedback on each topic, preferably only one post per person summarizing your position/suggestions. I'd rather not have a run-away discussion on this post. --- - topic: a glossary overview: It seems that we are having difficulty with words that have shifting definitions. The next PEP edit will need to add a glossary that nails down some meanings of these words. Following are a few proposed terms/meanings. proposal: - protocol means any object, usually a type or class or interface, which guides the construction of an adapter - adaptee is the object which is to be adapted, the original object - adaptee-class refers to the adaptee's class - adapter refers to the result of adapting an adaptee to a protocol - factory refers to a function, f(adaptee) -> adapter, where the resulting adapter complies with a given protocol feedback: Much help is needed here; either respond to this thread with your words and definitions, or email them directly to Clark and he will use your feedback when creating the PEP's glossary. - topic: a registry mechanism overview: It has become very clear from the conversations the past few days that a registry is absolutely needed for the kind of adapt() behavior people are currently using in Zope, Twisted, and Peak. proposal: - The PEP will define a simple and flexible registry mechanism. - The registry will be a mapping from a (adaptee-class, protocol) pair to a corresponding factory. - Only one active registration per pair (see below) feedback: We welcome/encourage experiences and concreate suggestions from existing registries. Our goal is to be minimal, extensible, and sufficient. See other topics for more specific concerns before you comment on this more general topic. - topic: should 'object' be impacted by PEP 246 overview: The semantics of exceptions depend if 'object' is given a default __conform__ method (which would do isinstance), in which case, returning None in a subclass could be used to prevent Liskov violations. However, by requiring a change to 'object', it may hinder adoption or slow-down testing. proposal: - We will not ask/require changes to `object'. - Liskov violations will be managed via the registry, see below. - This is probably faster for isinstance cases? feedback: If you really think we should move isinstance() into object.__conform__, then here is your chance to make a final stand. ;) - topic: adaption stages overview: There are several stages for adaptation. It was recommended that the 'registry' be the first stop in the chain. proposal: - First, the registry is checked for a suitable adapter - Second, isinstance() is checked, the adaptee is an instance of the protocol, adaptation ends and adaptee is returned. - Third, __conform__ on the adaptee is called with the given protocol being requested. - Fourth, __adapt__ on the protocol is called, with the given adaptee. feedback: This largely dependent upon the previous topic, but if something isn't obvious (mod exceptions below), please say something. - topic: module vs built-in overview: Since we will be adding a registry, exceptions, and other items, it probably makes sense to use a module for 'adapt'. proposal: - PEP 246 will ask for a `adapt' module, with an `adapt' function. - The registry will be contained in this module, 'adapt.register' - The `adapt' module can provide commonly-used adapter factories, such as adapt.Identity. - With a standardized signature, frameworks can provide their own 'local' registry/adapt overrides. feedback: Please discuss the merits of a module approach, and if having local registries is important (or worth the added complexity). Additional suggestions on how the module should be structured are welcome. - topic: exception handling overview: How should adaption stages progress and exceptions be handled. There were problems with swallowed TypeError exceptions in the 2001 version of the PEP, type errors are not swallowed. proposal: - The 'adapt' module will define an adapt.AdaptError(TypeError). - At any stage of adaptation, if None is returned, the adaptation continues to the next stage. - Any exception other than adapt.AdaptException(TypeError) causes the adapt() call to fail, and the exception to be raised to the caller of adapt(); the 'default' value is not returned in this case. - At
Re: [Python-Dev] PEP 246, Feedback Request
At 11:04 PM 1/15/05 -0500, Clark C. Evans wrote: topic: a glossary overview: It seems that we are having difficulty with words that have shifting definitions. The next PEP edit will need to add a glossary that nails down some meanings of these words. Following are a few proposed terms/meanings. It would also be helpful to distinguish between 1-to-1 "as a" adapters, and 1-to-many "view" adapters. There isn't a really good terminology for this, but it's important at least as it relates to type declarations. - Any exception other than adapt.AdaptException(TypeError) causes the adapt() call to fail, and the exception to be raised to the caller of adapt(); the 'default' value is not returned in this case. - At any stage of adaption, if adapt.AdaptException(TypeError) is raised, then the adaptation process stops, as if None had been returned from each stage. - If all adaption stages return None, there are two cases. If the call to adapt() had a 'default' value, then this is returned; otherwise, an adapt.AdaptException is raised. -1; This allows unrelated AdaptExceptions to end up being silently caught. These need to be two different exceptions if you want to support stages being able to "veto" adaptation. Perhaps you should have a distinct VetoAdaptation error to support that use case. topic: transitivity ... proposal: ... feedback: I'm looking for warts in this plan, and verification if something like this has been done -- comments how well it works. Alternative approaches? I'll try to think some more about this one later, but I didn't see any obvious problems at first glance. topic: declaration (aka Guido's syntax) and intrinsic adaption overview: Guido would like his type declaration syntax (see blog entry) to be equivalent to a call to adapt() without any additional arguments. However, not all adapters should be created in the context of a declaration -- some should be created more explicitly. We propose a mechanism where an adapter factory can register itself as not suitable for the declaration syntax. It would be much safer to have the reverse be the default; i.e., it should take special action to declare an adapter as being *suitable* for use with type declarations. IOW, sticky intrinsic adapters should be the default, and volatile accessories should take an extra action to make them usable with type declarations. feedback: This is the simplest solution I heard on the list; the word 'intrinsic' was given by Alex. Is there a better word? Sadly, no. I've been playing with words like "extender", "mask", "personality" etc. to try and find a name for a thing you only reasonably have one of, versus things you can have many of like "accessory", "add-on", etc. topic: adaptee (aka origin) overview: There was discussion as to how to get back to the original object from an adapter. Is this in scope of PEP 246? proposal: - we specify an __adaptee__ property, to be optionally implemented by an adapter that provides a reference adaptee - the adapt.register method has an optional argument, 'adaptee', that defaults to False; if it is True, adapt() calls will stuff away into a weak-reference mapping from adapter to adaptee. - an adapt.adaptee(adaptor) function which returns the given adaptee for the adaptor; this first checks the weak-reference table, and then checks for an __adaptee_ feedback: Is this useful, worth the complexity? This is tied directly to intrinsicness and stickyness. If you are intrinsic, you *must* have __adaptee__, so that adapt can re-adapt you safely. If you are intrinsic, you *must* be stateless or sticky. (Stateless can be considered an empty special case of "sticky") So, you might be able to combine a lot of these options to make the interface cleaner. Think of it this way: if the adapter is intrinsic, it's just a "personality" of the underlying object. So you don't want to re-adapt a personality, instead you re-adapt the "original object". But for a non-intrinsic adapter, the adapter is an independent object only incidentally related to the original adaptee, so it is now an "original object" of its own. topic: sticky overview: Sticky adapters, that is, ones where there is only one instance per adaptee is a common use case. Should the registry of PEP 246 provide this feature? Ideally, yes. proposal: - the adapt.register method has an optional argument, 'sticky', that defaults to False Make it default to whatever the 'intrinsic' setting is, because the only time you don't care for an intrinsic adapter is if the adapter is completely stateless. Or, better yet, call it 'volatile' or something and default to False. (I.e, you have to be say you're willing to have it volatile.) If you get all of these features, it's going to come mighty close to the functiona
[Python-Dev] "Monkey Typing" pre-PEP, partial draft
I just attempted to post the Monkey Typing draft pre-PEP, but it bounced due to being just barely over the size limit for the list. :) So, I'm just posting the preamble and abstract here for now, and a link to a Wiki page with the full text. I hope the moderator will approve the actual posting soon so that replies can quote from the text. === original message === This is only a partial first draft, but the Motivation section nonetheless attempts to briefly summarize huge portions of the various discussions regarding adaptation, and to coin a hopefully more useful terminology than some of our older working adjectives like "sticky" and "stateless" and such. And the specification gets as far as defining a simple decorator-based syntax for creating operational (prev. "stateless") and extension (prev. "per-object stateful") adapters. I stopped when I got to the API for declaring volatile (prev. per-adapter stateful) adapters, and for enabling them to be used with type declarations, because Clark's post on his revisions-in-progress seem to indicate that this can probably be handled within the scope of PEP 246 itself. As such, this PEP should then be viewed more as an attempt to formulate how "intrinsic" adapters can be defined in Python code, without the need to manually create adapter classes for the majority of type-compatibility and "extension" use cases. In other words, the implementation described herein could probably become part of the front-end for the PEP 246 adapter registry. Feedback and corrections (e.g. if I've repeated myself somewhere, spelling, etc.) would be greatly appreciated. This uses ReST markup heavily, so if you'd prefer to read an HTML version, please see: http://peak.telecommunity.com/DevCenter/MonkeyTyping But I'd prefer that corrections/discussion quote the relevant section so I know what parts you're talking about. Also, if you find a place where a more concrete example would be helpful, please consider submitting one that I can add. Thanks! PEP: XXX Title: "Monkey Typing" for Agile Type Declarations Version: $Revision: X.XX $ Last-Modified: $Date: 2003/09/22 04:51:50 $ Author: Phillip J. Eby <[EMAIL PROTECTED]> Status: Draft Type: Standards Track Python-Version: 2.5 Content-Type: text/x-rst Created: 15-Jan-2005 Post-History: 15-Jan-2005 Abstract Python has always had "duck typing": a way of implicitly defining types by the methods an object provides. The name comes from the saying, "if it walks like a duck and quacks like a duck, it must *be* a duck". Duck typing has enormous practical benefits for small and prototype systems. For very large frameworks, however, or applications that comprise multiple frameworks, some limitations of duck typing can begin to show. This PEP proposes an extension to "duck typing" called "monkey typing", that preserves most of the benefits of duck typing, while adding new features to enhance inter-library and inter-framework compatibility. The name comes from the saying, "Monkey see, monkey do", because monkey typing works by stating how one object type may *mimic* specific behaviors of another object type. Monkey typing can also potentially form the basis for more sophisticated type analysis and improved program performance, as it is essentially a simplified form of concepts that are also found in languages like Dylan and Haskell. It is also a straightforward extension of Java casting and COM's QueryInterface, which should make it easier to represent those type systems' behaviors within Python as well. [see the web page above for the remaining text] ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com