Some aspects of the Python design are remarkably clever, while others leave me perplexed. Here's an example of the latter: Why does len() give an error when applied to an int or float? len() should always return something; in particular, when applied to a scalar, it should return a value of 1. Of course, I can define my own function like this:
def mylen(x): if isinstance(x,int) or isinstance(x,float): return 1 return len(x)
> Some aspects of the Python design are remarkably clever, while others leave > me perplexed. Here's an example of the latter: Why does len() give an error > when applied to an int or float? len() should always return something; in > particular, when applied to a scalar, it should return a value of 1. Of > course, I can define my own function like this:
> def mylen(x): > if isinstance(x,int) or isinstance(x,float): return 1 > return len(x)
> But, this shouldn't be necessary.
Can you show some example of where that is actually making a piece of code more elegant?
> Some aspects of the Python design are remarkably clever, while others leave > me perplexed. Here's an example of the latter: Why does len() give an error > when applied to an int or float? len() should always return something; in > particular, when applied to a scalar, it should return a value of 1. Of > course, I can define my own function like this:
> def mylen(x): > if isinstance(x,int) or isinstance(x,float): return 1 > return len(x)
> But, this shouldn't be necessary.
The problem is that redefining len()/length/size that way would violate several principles of Python's design (The "Zen" of Python - http://www.python.org/dev/peps/pep-0020/).
Specifically: - Explicit is better than implicit. - Special cases aren't special enough to break the rules. - Errors should never pass silently. - In the face of ambiguity, refuse the temptation to guess.
If you'd explain the situation that prompts you to find this redefinition necessary, I'm sure someone can suggest a better approach.
> Some aspects of the Python design are remarkably clever, while others > leave me perplexed. Here's an example of the latter: Why does len() give > an error when applied to an int or float? len() should always return > something; in particular, when applied to a scalar, it should return a > value of 1. Of course, I can define my own function like this:
> def mylen(x): > if isinstance(x,int) or isinstance(x,float): return 1 > return len(x)
> But, this shouldn't be necessary.
Python should not blur the distinction between vectors an scalars like that. Instead of trying to be clever you should pass a vector with a single item and send mylen() to /dev/null.
On a general note, I think one of Python's strengths is that it consistently /avoids/ this kind of cleverness.
On 2009-07-24, Dr. Phillip M. Feldman <pfeld...@verizon.net> wrote:
> Some aspects of the Python design are remarkably clever, while > others leave me perplexed. Here's an example of the latter: > Why does len() give an error when applied to an int or float? > len() should always return something; in particular, when > applied to a scalar, it should return a value of 1.
If len(7) returned a value of 1, then wouldn't one expect 7[0] to be valid? It isn't, so you'd then have to redefine all types so that they are sequences that can be indexed. Sounds like a big mess to me...
[Are there types for which len() returns a value that can't be indexed?]
-- Grant Edwards grante Yow! It's the RINSE CYCLE!! at They've ALL IGNORED the visi.com RINSE CYCLE!!
On Fri, 24 Jul 2009 14:57:02 +0100, Grant Edwards <invalid@invalid> wrote: > On 2009-07-24, Dr. Phillip M. Feldman <pfeld...@verizon.net> wrote:
>> Some aspects of the Python design are remarkably clever, while >> others leave me perplexed. Here's an example of the latter: >> Why does len() give an error when applied to an int or float? >> len() should always return something; in particular, when >> applied to a scalar, it should return a value of 1.
> If len(7) returned a value of 1, then wouldn't one expect 7[0] > to be valid? It isn't, so you'd then have to redefine all > types so that they are sequences that can be indexed. Sounds > like a big mess to me...
> [Are there types for which len() returns a value that can't be > indexed?]
Dictionaries.
Which doesn't make your point less valid. In fact I'd go so far as to argue that what len() gives you is the number of items in a container, so len(7) should return 0.
-- Rhodri James *-* Wildebeest Herder to the Masses
On Jul 24, 3:11 pm, "Rhodri James" <rho...@wildebst.demon.co.uk> wrote:
> Which doesn't make your point less valid. In fact I'd go so > far as to argue that what len() gives you is the number of > items in a container, so len(7) should return 0.
Nah. 7 contains three bits, so len(7) should *clearly* return 3.
Mark Dickinson wrote: > On Jul 24, 3:11 pm, "Rhodri James" <rho...@wildebst.demon.co.uk> > wrote:
>>Which doesn't make your point less valid. In fact I'd go so >>far as to argue that what len() gives you is the number of >>items in a container, so len(7) should return 0.
> Nah. 7 contains three bits, so len(7) should *clearly* return 3.
and len("7") must return 8, by the same token... but wait!
> On Jul 24, 3:11 pm, "Rhodri James" <rho...@wildebst.demon.co.uk> > wrote: >> Which doesn't make your point less valid. In fact I'd go so >> far as to argue that what len() gives you is the number of >> items in a container, so len(7) should return 0.
> Nah. 7 contains three bits, so len(7) should *clearly* return 3.
But it contains a minimum of 32 bits! And why are you treating ones as special over zeros? I thought the times of BitRacism were finally over...
>>>>> "Rhodri James" <rho...@wildebst.demon.co.uk> (RJ) wrote: >RJ> On Fri, 24 Jul 2009 14:57:02 +0100, Grant Edwards <invalid@invalid> wrote: >>> On 2009-07-24, Dr. Phillip M. Feldman <pfeld...@verizon.net> wrote:
>>>> Some aspects of the Python design are remarkably clever, while >>>> others leave me perplexed. Here's an example of the latter: >>>> Why does len() give an error when applied to an int or float? >>>> len() should always return something; in particular, when >>>> applied to a scalar, it should return a value of 1.
>>> If len(7) returned a value of 1, then wouldn't one expect 7[0] >>> to be valid? It isn't, so you'd then have to redefine all >>> types so that they are sequences that can be indexed. Sounds >>> like a big mess to me...
>>> [Are there types for which len() returns a value that can't be >>> indexed?]
>RJ> Dictionaries. >RJ> Which doesn't make your point less valid. In fact I'd go so >RJ> far as to argue that what len() gives you is the number of >RJ> items in a container, so len(7) should return 0.
But len(7) could as well be defined as 3, 1, 32, or 64 (depending on the implementation). Therefore it doesn't make much sense. -- Piet van Oostrum <p...@cs.uu.nl> URL: http://pietvanoostrum.com [PGP 8DAE142BE17999C4] Private email: p...@vanoostrum.org
>> RJ> On Fri, 24 Jul 2009 14:57:02 +0100, Grant Edwards <invalid@invalid> >> wrote: >>>> On 2009-07-24, Dr. Phillip M. Feldman <pfeld...@verizon.net> wrote:
>>>>> Some aspects of the Python design are remarkably clever, while >>>>> others leave me perplexed. Here's an example of the latter: >>>>> Why does len() give an error when applied to an int or float? >>>>> len() should always return something; in particular, when >>>>> applied to a scalar, it should return a value of 1.
>>>> If len(7) returned a value of 1, then wouldn't one expect 7[0] >>>> to be valid? It isn't, so you'd then have to redefine all >>>> types so that they are sequences that can be indexed. Sounds >>>> like a big mess to me...
>>>> [Are there types for which len() returns a value that can't be >>>> indexed?]
>> RJ> Dictionaries.
>> RJ> Which doesn't make your point less valid. In fact I'd go so >> RJ> far as to argue that what len() gives you is the number of >> RJ> items in a container, so len(7) should return 0.
> But len(7) could as well be defined as 3, 1, 32, or 64 (depending on the > implementation). Therefore it doesn't make much sense.
Quite.
-- Rhodri James *-* Wildebeest Herder to the Masses
> On Thu, Jul 23, 2009 at 11:35 PM, Dr. Phillip M. > Feldman<pfeld...@verizon.net> wrote:
>> Some aspects of the Python design are remarkably clever, while others >> leave >> me perplexed. Here's an example of the latter: Why does len() give an >> error >> when applied to an int or float? len() should always return something; in >> particular, when applied to a scalar, it should return a value of 1. Of >> course, I can define my own function like this:
>> def mylen(x): >> if isinstance(x,int) or isinstance(x,float): return 1 >> return len(x)
>> But, this shouldn't be necessary.
> The problem is that redefining len()/length/size that way would > violate several principles of Python's design (The "Zen" of Python - > http://www.python.org/dev/peps/pep-0020/).
> Specifically: > - Explicit is better than implicit. > - Special cases aren't special enough to break the rules. > - Errors should never pass silently. > - In the face of ambiguity, refuse the temptation to guess.
> If you'd explain the situation that prompts you to find this > redefinition necessary, I'm sure someone can suggest a better > approach. On Fri, Jul 24, 2009 at 8:58 AM, Phillip M. Feldman<pfeld...@verizon.net> wrote: > I've read the "Zen of Python", but most of these aphorisms are vague and > could be understood differently by different readers. In particular, I > don't understand the statement that "explicit is better than implicit". > Some examples of this would be helpful.
> I've been converting Matlab codes to Python. In Matlab, a scalar is just a > one-by-one matrix and has a length of 1. This convention seems no less > arbitrary to me than Python's convention that the concept of length is not > applicable to ints and floats. My workaround was to write the following > function:
> def is_scalar(x): > """Return True if x is an instance of int, float, or complex. > Otherwise, return False. Note: If x is a length-1 list or array > containing an int, float, or complex value, False is returned.""" > if isinstance(x,int) or isinstance(x,float) or isinstance(x,complex): > return True > return False
> The application is the following: In various types of scientific > applications, one operates on a list of measurements. If there is only a > single measurement, it is reasonable to allow the calling program to pass a > scalar without wrapping it up into a list or array.
You could use Python's extended call syntax when defining your function:
Phillip M. Feldman wrote: > I've been converting Matlab codes to Python. In Matlab, a scalar is > just a one-by-one matrix and has a length of 1. This convention seems > no less arbitrary to me than Python's convention that the concept of > length is not applicable to ints and floats.
Multiplication of a vector/matrix by a scalar always defined and commutative. Multiplication of a vector/matrix by a 1x1 matrix is not always even defined. So not having scalars in a matrix package strikes me as a bit odd.
> My workaround was to write
> the following function:
> def is_scalar(x): > """Return True if x is an instance of int, float, or complex. > Otherwise, return False. Note: If x is a length-1 list or array > containing an int, float, or complex value, False is returned.""" > if isinstance(x,int) or isinstance(x,float) or isinstance(x,complex):
Better: if isinstance(x, (int, float, complex)):
but you forgot decimals and fractions and any other possible number modules.
In 3.1, >>> from numbers import Number >>> from decimal import Decimal >>> from fractions import Fraction >>> for x in 1, 1.0, (1+0j), Decimal(1), Fraction(1,1): isinstance(x, Number)
True True True True True
and the same for any other module that registers a class as a Number
> return True > return False
> The application is the following: In various types of scientific > applications, one operates on a list of measurements. If there is only > a single measurement, it is reasonable to allow the calling program to > pass a scalar without wrapping it up into a list or array.
In article <mailman.3674.1248461573.8015.python-l...@python.org>, Terry Reedy <tjre...@udel.edu> wrote:
> Better: if isinstance(x, (int, float, complex)):
I never noticed this before, but it seems odd that the second argument to isinstance() should be a tuple. Using the normal arguments made about tuples vs. lists, it seems like a list would be the right data structure here. I suppose a set would be even more right, but (I'm pretty sure) isinstance() predates sets.
On Fri, 24 Jul 2009 00:02:28 -0700, Chris Rebert wrote: > On Thu, Jul 23, 2009 at 11:35 PM, Dr. Phillip M. > Feldman<pfeld...@verizon.net> wrote:
>> Some aspects of the Python design are remarkably clever, while others >> leave me perplexed. Here's an example of the latter: Why does len() >> give an error when applied to an int or float? len() should always >> return something; in particular, when applied to a scalar, it should >> return a value of 1. Of course, I can define my own function like this:
>> def mylen(x): >> if isinstance(x,int) or isinstance(x,float): return 1 return len(x)
>> But, this shouldn't be necessary.
> The problem is that redefining len()/length/size that way would violate > several principles of Python's design (The "Zen" of Python - > http://www.python.org/dev/peps/pep-0020/).
> Specifically: > - Explicit is better than implicit. > - Special cases aren't special enough to break the rules. > - Errors should never pass silently. > - In the face of ambiguity, refuse the temptation to guess.
Chris, I'm curious why you think that these Zen are relevant to the OP's complaint.
Re explicit vs implicit, len(42) is just as explicit as len([42, 23]).
Arguably (I wouldn't argue this, but some people might) ints aren't "special enough" to break the rule that len(obj) should always return something.
(I don't actually agree, but some people might be able to produce a coherent argument why len() should apply equally to all objects.)
Re errors passing silently, the OP doesn't believe that len(42) should be an error, so that's not relevant.
And there's nothing ambiguous about len(42).
I agree with the current Python behaviour, but I don't think there's anything in the Zen to support it. As far as I know, there is no programming language which treats scalars like ints as if they were vectors of length 1, which makes Python's choice to make ints unlengthed a no-brainer.
On Fri, 24 Jul 2009 15:03:29 -0400, Roy Smith wrote: > In article <mailman.3674.1248461573.8015.python-l...@python.org>, > Terry Reedy <tjre...@udel.edu> wrote:
>> Better: if isinstance(x, (int, float, complex)):
> I never noticed this before, but it seems odd that the second argument > to isinstance() should be a tuple. Using the normal arguments made > about tuples vs. lists, it seems like a list would be the right data > structure here.
What would be the point of using a list? You're never going to sort it, or append items to it, or otherwise mutate it. You build it, pass it to a function which doesn't modify it in any fashion, then it gets garbage collected.
> I suppose a set would be even more right, but (I'm > pretty sure) isinstance() predates sets.
Traceback (innermost last): File "<stdin>", line 1, in ? NameError: set
> I'm curious why a tuple was chosen.
Tuples are smaller and faster to build than lists -- they're the most lightweight sequence type in Python. You don't need all the extra functionality of lists, so why go to the time and effort of building a list?
D'Aprano<st...@remove-this-cybersource.com.au> wrote: > On Fri, 24 Jul 2009 00:02:28 -0700, Chris Rebert wrote:
>> On Thu, Jul 23, 2009 at 11:35 PM, Dr. Phillip M. >> Feldman<pfeld...@verizon.net> wrote:
>>> Some aspects of the Python design are remarkably clever, while others >>> leave me perplexed. Here's an example of the latter: Why does len() >>> give an error when applied to an int or float? len() should always >>> return something; in particular, when applied to a scalar, it should >>> return a value of 1. Of course, I can define my own function like this:
>>> def mylen(x): >>> if isinstance(x,int) or isinstance(x,float): return 1 return len(x)
>>> But, this shouldn't be necessary.
>> The problem is that redefining len()/length/size that way would violate >> several principles of Python's design (The "Zen" of Python - >> http://www.python.org/dev/peps/pep-0020/).
>> Specifically: >> - Explicit is better than implicit. >> - Special cases aren't special enough to break the rules. >> - Errors should never pass silently. >> - In the face of ambiguity, refuse the temptation to guess.
> Chris, I'm curious why you think that these Zen are relevant to the OP's > complaint.
To explain in more detail:
> Re explicit vs implicit, len(42) is just as explicit as len([42, 23]).
If you want a collection (something that has length), then one should explicitly create one, not implicitly have a singleton value act like it's a pseudo-collection. I admit I'm somewhat conflating this principle with the anti-ambiguity principle, but the two are related, imho.
> Arguably (I wouldn't argue this, but some people might) ints aren't > "special enough" to break the rule that len(obj) should always return > something.
Except that's not the current rule. The current rule is that it's defined only for collections. One would instead have to argue why ints are special enough to have len() defined despite not being collections. I think the point made by Grant Edwards is instructive. len(x) = 1 typically implies list(x)[0] and similar should be valid. Altering the behavior would invalidate that theorem and cause quite a bit of code upheaval, all just to save the OP from typing one pair of []s.
> (I don't actually agree, but some people might be able to produce a > coherent argument why len() should apply equally to all objects.)
Well, yes, this /whole/ "argument" is entirely academic; the behavior is extremely unlikely to change, we're just trying to give ex post facto rationales for pedagogical purposes. :)
> Re errors passing silently, the OP doesn't believe that len(42) should be > an error, so that's not relevant.
True, it would not directly silence an error, but defining len() on scalars would tend towards obscuring errors in code that incorrectly treats scalars as collections.
> And there's nothing ambiguous about len(42).
Really? What is its value then? I think arguments of varying quality can be made for: 1 - as the OP and those from array programming languages would suggest 2 - the number of decimal digits in 42, if one was feeling Perlish 6 - the minimum number of bits necessary to represent 42 in binary 32 (or 64, depending on your CPU) - the number of bits necessary to represent an int (obviously breaks down a bit with arbitrary-magnitude ints) undefined - i.e. it causes an error, the current behavior; asking for the length of a non-collection is "nonsensical" or "absurd"
> I agree with the current Python behaviour, but I don't think there's > anything in the Zen to support it. As far as I know, there is no
The problem and strength of the Zen is that it's all about how you interpret it. :-)
> In article <0279f596$0$5185$c3e8...@news.astraweb.com>, > Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> wrote:
>> On Fri, 24 Jul 2009 16:50:03 +0200, superpollo wrote:
>>>> Nah. 7 contains three bits, so len(7) should *clearly* return 3. >>> and len("7") must return 8, by the same token... but wait!
>>> >>> len("7") >>> 1
>>> my python installation must me outdated ;-) >> No no no, you're obviously using an awesome version of Python that can >> compress single-character strings to a single bit!
> Compressing strings to a single bit is easy. It's the uncompressing that's > tricky.
I assume you mean ord("7")%2?
First one to correctly decompress the value 0 into an ASCII character wins the title of the world's most capable hacker :p
Marcus Wanner wrote: > On 7/24/2009 3:04 PM, Roy Smith wrote: >> In article <0279f596$0$5185$c3e8...@news.astraweb.com>, >> Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> wrote:
>>> On Fri, 24 Jul 2009 16:50:03 +0200, superpollo wrote:
>>>>> Nah. 7 contains three bits, so len(7) should *clearly* return 3. >>>> and len("7") must return 8, by the same token... but wait!
>>>> >>> len("7") >>>> 1
>>>> my python installation must me outdated ;-) >>> No no no, you're obviously using an awesome version of Python that >>> can compress single-character strings to a single bit!
>> Compressing strings to a single bit is easy. It's the uncompressing >> that's tricky. > I assume you mean ord("7")%2?
> First one to correctly decompress the value 0 into an ASCII character > wins the title of the world's most capable hacker :p
> Marcus
asciichar = chr(len(0)) if the OP's wishes come true?
> Marcus Wanner wrote: >> On 7/24/2009 3:04 PM, Roy Smith wrote: >>> In article <0279f596$0$5185$c3e8...@news.astraweb.com>, >>> Steven D'Aprano <st...@REMOVE-THIS-cybersource.com.au> wrote:
>>>> On Fri, 24 Jul 2009 16:50:03 +0200, superpollo wrote:
>>>>>> Nah. 7 contains three bits, so len(7) should *clearly* return 3. >>>>> and len("7") must return 8, by the same token... but wait!
>>>>> >>> len("7") >>>>> 1
>>>>> my python installation must me outdated ;-) >>>> No no no, you're obviously using an awesome version of Python that >>>> can compress single-character strings to a single bit!
>>> Compressing strings to a single bit is easy. It's the uncompressing >>> that's tricky. >> I assume you mean ord("7")%2?
>> First one to correctly decompress the value 0 into an ASCII character >> wins the title of the world's most capable hacker :p
>> Marcus > asciichar = chr(len(0)) if the OP's wishes come true?
Marcus Wanner wrote: > First one to correctly decompress the value 0 into an ASCII > character wins the title of the world's most capable hacker :p
Bah...uncompressing the value 0 into *an* ASCII character is easy. Uncompressing it into the *original* ASCII character from which it was compressed (with the aforementioned compression method) becomes a bit trickier ;-)
On Fri, Jul 24, 2009 at 1:30 PM, Tim Chase<python.l...@tim.thechases.com> wrote: > Marcus Wanner wrote:
>> First one to correctly decompress the value 0 into an ASCII >> character wins the title of the world's most capable hacker :p
> Bah...uncompressing the value 0 into *an* ASCII character is easy. > Uncompressing it into the *original* ASCII character from which it was > compressed (with the aforementioned compression method) becomes a bit > trickier ;-)
Steven D'Aprano wrote: > On Fri, 24 Jul 2009 15:03:29 -0400, Roy Smith wrote:
>> In article <mailman.3674.1248461573.8015.python-l...@python.org>, >> Terry Reedy <tjre...@udel.edu> wrote:
>>> Better: if isinstance(x, (int, float, complex)): >> I never noticed this before, but it seems odd that the second argument >> to isinstance() should be a tuple. Using the normal arguments made >> about tuples vs. lists, it seems like a list would be the right data >> structure here.
I ignore the 'normal arguments' and it seems that Guido or the designer of isinstance did so here too. Fortunately. Practicality beats 'purity', especially misguided purity.
> What would be the point of using a list? You're never going to sort it, > or append items to it, or otherwise mutate it. You build it, pass it to a > function which doesn't modify it in any fashion, then it gets garbage > collected.
>> I suppose a set would be even more right, but (I'm >> pretty sure) isinstance() predates sets.
> Yes.
> [steve@sylar ~]$ python1.5 > Python 1.5.2 (#1, Apr 1 2009, 22:55:54) [GCC 4.1.2 20070925 (Red Hat > 4.1.2-27)] on linux2 > Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam >>>> isinstance > <built-in function isinstance> >>>> set > Traceback (innermost last): > File "<stdin>", line 1, in ? > NameError: set
>> I'm curious why a tuple was chosen.
> Tuples are smaller and faster to build than lists -- they're the most > lightweight sequence type in Python. You don't need all the extra > functionality of lists, so why go to the time and effort of building a > list?
In fact, constant tuples can be and now are compiled as constants:
Internally, even a frozenset is more complicated than a tuple since it still needs a hash table, which is overkill for something that will be linearly scanned exactly once.