Re: [Python-Dev] Decimal & returning NotImplemented (or not)

2005-03-02 Thread Nick Craig-Wood
On Tue, Mar 01, 2005 at 05:55:50PM -0500, Neil Schemenauer wrote:
> On Tue, Mar 01, 2005 at 11:45:43PM +1000, Nick Coghlan wrote:
> > Interesting. In that case, my other suggestion was to have raising 
> > NotImplementedError from a special method be the equivalent of returning 
> > NotImplemented (which makes life much easier for a class like Decimal which 
> > has an internal method for doing the type conversion).
> 
> NotImplementedError has nothing to do with the NotImplemented
> singleton.  It's unfortunate about the naming.  IMO, Decimal should
> be returning NotImplemented instead of raising TypeError.

That seems very un-pythonic to me - return an error?

If you look at the patched Decimal module you'll see all sorts of
contortions necessary to accomodate it, wheras if it could just raise
NotImplementedError or NotImplementedTypeError it would make the code
a lot cleaner.

I have to say when I read the python language docs, I assumed there
was a mistake in them and they meant to say "raise
NotImplementedError" instead of "return NotImplemented".

Why is it like that?  And could it be changed (Nick Coghlan's proposal
seems good to me)?

-- 
Nick Craig-Wood <[EMAIL PROTECTED]> -- http://www.craig-wood.com/nick
___
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] Decimal & returning NotImplemented (or not)

2005-03-02 Thread Nick Coghlan
Neil Schemenauer wrote:
 IMO, Decimal should
be returning NotImplemented instead of raising TypeError.
I agree, but I'm also interested in the fact that nobody commented on this 
before Python 2.4 went out. I read the reference page on numeric coercion more 
times than I care to count, and still didn't register that there might be an 
issue with raising the TypeError - and that's only me. Relative to people like 
Raymond and Facundo, I spent comparatively little time working on Decimal :)

I think the problem arose because the natural thing to do in Python code is to 
push the type inspection for operations into a common method, and have that 
method raise TypeError if the other argument isn't acceptable.

Trying to convert that exception to a 'NotImplemented' return value is a pain 
(it's less of a pain in C, since you're already checking for error returns 
instead of using exceptions). The error return idiom is really unnatural in 
Python code. Mixing the error return for the special methods with raising an 
exception for non-special methods is exceedingly ugly (particularly for Decimal, 
since operations through Context objects should *always* raise a TypeError for 
bad arguments. If the special methods return NotImplemented instead of raising 
an exception, then Context has to convert those returns to TypeErrors).

Additionally, returning NotImplemented means that direct calls to special 
methods may not raise an exception in response to a bad argument. Compare:

Py> 1 .__add__("")
NotImplemented
Py> "".__add__(1)
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects
Hmm, perhaps a decorator would help here. Something like:
class OperatorTypeError(TypeError): pass
class operatormethod(object):
def __init__(self, method):
self._method = method
self._obj = None
def __get__(self, obj, type=None):
self._obj = obj
return self
def __call__(*args, **kwds):
self = args[0]
obj = self._obj
try:
if obj is None:
return self._method(*args[1:], **kwds)
return self._method(obj, *args[1:], **kwds)
except OperatorTypeError:
return NotImplemented
Then do:
Py> class C:
... def _add(self, other):
... raise OperatorTypeError
... __add__ = operatormethod(_add)
... __radd__ = __add__
...
Py> C()._add(1)
Traceback (most recent call last):
  File "", line 1, in ?
  File "", line 3, in _add
__main__.OperatorTypeError
Py> C().__add__(1)
NotImplemented
Py> C() + 1
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: unsupported operand type(s) for +: 'instance' and 'int'
Py> 1 + C()
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
That makes it easy to write natural Python code (raising a specific subclass of 
type error), while still returning NotImplemented so the operator coercion works 
correctly.

>  That
> could be considered a bug but I'd file it under new functionality
> for the purposes of backporting (i.e. fix it in 2.5 only).
Given that I'm already ambivalent about changing this for 2.4, it won't take 
much to convince me that this should be left to 2.5.

Particularly if we actually try to find a way to make it easier to 'do the right 
thing', rather than just changing Decimal.

Cheers,
Nick.
--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---
http://boredomandlaziness.skystorm.net
___
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] Decimal & returning NotImplemented (or not)

2005-03-02 Thread Nick Coghlan
Nick Craig-Wood wrote:
Why is it like that?
Speed, I assume - if it was exception based, then *every* operation effectively 
gets wrapped in a try/except block. Yikes!

Also, for C implementations, the error return is fairly natural. It's only when 
implementing operations in Python that it bites.

 And could it be changed (Nick Coghlan's proposal
seems good to me)?
Take a look at my latest suggestion using OperatorTypeError and operatormethod. 
(which makes it easy to put the try/catch block in place if you want it, while 
still leaving it out for the common case).

Cheers,
Nick.
--
Nick Coghlan   |   [EMAIL PROTECTED]   |   Brisbane, Australia
---
http://boredomandlaziness.skystorm.net
___
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] Decimal & returning NotImplemented (or not)

2005-03-02 Thread Michael Hudson
Nick Coghlan <[EMAIL PROTECTED]> writes:

> Neil Schemenauer wrote:
>>  IMO, Decimal should
>> be returning NotImplemented instead of raising TypeError.
>
> I agree, but I'm also interested in the fact that nobody commented on
> this before Python 2.4 went out. I read the reference page on numeric
> coercion more times than I care to count, and still didn't register
> that there might be an issue with raising the TypeError - and that's
> only me. Relative to people like Raymond and Facundo, I spent
> comparatively little time working on Decimal :)
>
> I think the problem arose because the natural thing to do in Python
> code is to push the type inspection for operations into a common
> method, and have that method raise TypeError if the other argument
> isn't acceptable.
>
> Trying to convert that exception to a 'NotImplemented' return value is
> a pain (it's less of a pain in C, since you're already checking for
> error returns instead of using exceptions). 

It seems to me that the most straightforward thing would be for
_convert_other to return NotImplemented, or to split it into two
functions, one that returns NotImplemented and one that raises and use
the former in __methods__.  This would mean a certain amount of

   other = _convert_other_ni(other)
   if other is NotImplemented: return other

but this seems mild in comparisons to the contortions the module
already performs in the name of correctness.

Cheers,
mwh

PS: this beahviour seems odd already:

>>> decimal.getcontext().abs(1)
Traceback (most recent call last):
  File "", line 1, in ?
  File "/Users/mwh/Source/python/dist/src/Lib/decimal.py", line 2321, in abs
return a.__abs__(context=self)
TypeError: wrapper __abs__ doesn't take keyword arguments

couldn't/shouldn't the context methods do a _convert_other (of the
erroring out kind) of their arguments?

-- 
  Gullible editorial staff continues to post links to any and all
  articles that vaguely criticize Linux in any way.
 -- Reason #4 for quitting slashdot today, from
http://www.cs.washington.edu/homes/klee/misc/slashdot.html
___
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] assigning a custom mapping type to __dict__

2005-03-02 Thread Steven Bethard
I think the behavior when supplying a custom mapping type to __dict__
is a little confusing.  I'd like to supply a patch, but I'm not sure
what the preferred solution is.

Let me explain the problem a little first.

For a custom mapping type that does not inherit from dict, Python
gives a nice error message when it's assigned to __dict__:

py> class M(object):
... def __getitem__(self, key):
... return 42
... 
py> class O(object):
...pass
...   
py> o = O()
py> o.__dict__ = M()
Traceback (most recent call last):
  File "", line 1, in ?
TypeError: __dict__ must be set to a dictionary

However, a subclass of dict will still be accepted, but has some
confusing behavior -- a redefined __getitem__ is not actually invoked:

py> class M(dict):
... def __getitem__(self, key):
... return 42
... 
py> class O(object):
...pass
... 
py> o = O()
py> o.__dict__ = M()
py> o.a
Traceback (most recent call last):
  File "", line 1, in ?
AttributeError: 'O' object has no attribute 'a'

By overriding __getattribute__ in the object's class, you can get the
expected behavior:

py> class M(dict):
... def __getitem__(self, key):
... return 42
... 
py> class O(object):
... def __getattribute__(self, name):
... getattribute = super(O, self).__getattribute__
... if not name.startswith('__') or not name.endswith('__'):
... try:
... return getattribute('__dict__')[name]
... except KeyError:
... raise AttributeError(name)
... return getattribute(name)
... 
py> o = O()
py> o.__dict__ = M()
py> o.a
42
py> o.a = 1
py> o.a
42

So, I see this as a problem, mainly because an error (assiging a
mapping to __dict__ and expecting its methods to be called) passes
silently.  I guess the first question is, do others agree this is also
an problem?

If so, I see a few solutions, and I'd like some input on which is most
desirable.

(1)  Raise an exception when a subclass of dict is assigned to
__dict__.  This is probably the simplest solution (I assume it's
basically just changing a PyDict_Check to a PyDict_CheckExact), and it
keeps the error from passing silently, but it is
backwards-incompatible.  (Classes like my last versions of M and O
above will be broken.)

(2) Allow sublcasses of dict to be assigned to __dict__, but change
the implementation to call the subclass methods, not the dict methods.
 I don't know exactly what needs to be changed to make this work.

(3) Allow any mapping type to be assigned to __dict__, and change the
implementation to use Py_Mapping* calls instead.

I don't know the implementation well enough to know the consequences
of the last two, so I'm hoping for some feedback here.

Thanks!

Steve
-- 
You can wordify anything if you just verb it.
--- Bucky Katt, Get Fuzzy
___
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: OK, time to retire

2005-03-02 Thread Scott David Daniels
Brett C. wrote:
I have decided that I am going to stop doing the python-dev Summaries
> after PyCon; the Summary covering the last half of March 2005 will be
> it for me.
I (as well as most, I'd guess) have enjoyed your voice in the summaries.
Thanks for a great series of summaries.  Perhaps your final summary
could be a personal view of PyCon for those of us unable to get there.
If you make no more contribution to Python than you have so far, you
will have done us a great service.
Hip-hip-hooray-ly y'rs
--Scott David Daniels
[EMAIL PROTECTED]
___
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] Decimal & returning NotImplemented (or not)

2005-03-02 Thread Guido van Rossum
> Nick Craig-Wood wrote:
> > Why is it like that?

Nick Coghlan replied:
> Speed, I assume - if it was exception based, then *every* operation 
> effectively
> gets wrapped in a try/except block. Yikes!

No, the reason is that if we did this with exceptions, it would be
liable to mask errors; an exception does not necessarily originate
immediately with the code you invoked, it could have been raised by
something else that was invoked by that code. The special value
NotImplemented must pretty much originate directly in the invoked
code, since the implementation guarantees that e.g. a+b can never
*return* NotImplemented: if a.__add__(b) and b.__radd__(a) both return
NotImplemented, TypeError is raised.

(Then why is StopIteration an exception instead of a special value,
you may ask? That's because we didn't want to have a value that you
could stick in a list to stop iteration over the list prematurely,
which would cause problems similar to null bytes in C strings. For
binary operators this isn't a problem, since binary operators (at
least by convention) always return an object that itself supports the
same binary operator that produced it, and NotImplemented itself
doesn't implement any binary operators.)

> Also, for C implementations, the error return is fairly natural. It's only 
> when
> implementing operations in Python that it bites.

You went on to great lengths later about how it's less natural in
Python to return an error value, but I disagree. I've written a lot of
code like this and it's quite natural to write things like this:

def __add__(self, other):
if not isinstance(other, ThisClass):
return NotImplemented
return ...implementation of self + other...

If you want to factor out the type check, it makes just as much sense
to define a Boolean function:

def __add__(self, other):
if not self.acceptableArgument(other):
return NotImplemented
...etc...

I'm guessing this wasn't caught before 2.4 was released because most
people reviewing the code were more interested in correct computations
than into correct integration in the Python framework.

-- 
--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