473,289 Members | 1,810 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,289 software developers and data experts.

Attack a sacred Python Cow

Hi everyone,

I'm a big Python fan who used to be involved semi regularly in
comp.lang.python (lots of lurking, occasional posting) but kind of
trailed off a bit. I just wrote a frustration inspired rant on my
blog, and I thought it was relevant enough as a wider issue to the
Python community to post here for your discussion and consideration.

This is not flamebait. I love Python, and I'm not out to antagonise
the community. I also realise that one of the issues I raise is way
too ingrained to be changed now. I'd just like to share my thinking on
a misstep in Python's guiding principles that has done more harm than
good IMO. So anyway, here's the post.

I've become utterly convinced that at least one criticism leveled at
my favourite overall programming language, Python, is utterly true and
fair. After quite a while away from writing Python code, I started
last night on a whim to knock up some code for a prototype of an idea
I once had. It's going swimmingly; the Python Image Library, which I'd
never used before, seems quick, intuitive, and with the all the
features I need for this project. As for Python itself, well, my heart
still belongs to whitespace delimitation. All the basics of Python
coding are there in my mind like I never stopped using them, or like
I've been programming in this language for 10 years.

Except when it comes to Classes. I added some classes to code that had
previously just been functions, and you know what I did - or rather,
forgot to do? Put in the 'self'. In front of some of the variable
accesses, but more noticably, at the start of *every single method
argument list.* This cannot be any longer blamed as a hangover from
Java - I've written a ton more code, more recently in Python than in
Java or any other OO language. What's more, every time I go back to
Python after a break of more than about a week or so, I start making
this 'mistake' again. The perennial justification for this 'feature'
of the language? That old Python favourite, "Explicit is better than
implicit."

I'm sorry, but EXPLICIT IS NOT NECESSARILY BETTER THAN IMPLICIT.
Assembler is explicit FFS. Intuitive, clever, dependable, expected,
well-designed *implicit* behaviour is one of the chief reasons why I
use a high level language. Implicitly garbage collect old objects for
me? Yes, please!

I was once bitten by a Python wart I felt was bad enough to raise and
spend some effort advocating change for on comp.lang.python (never got
around to doing a PEP; partly laziness, partly young and inexperienced
enough to be intimidated at the thought. Still am, perhaps.)

The following doesn't work as any sane, reasonable person would
expect:

# Blog code, not tested
class A():
def __eq__(self, obj):
return True
a = A()
b = []
assert a == b
assert not (a != b)

The second assertion fails. Why? Because coding __eq__, the most
obvious way to make a class have equality based comparisons, buys you
nothing from the != operator. != isn't (by default) a synonym for the
negation of == (unlike in, say, every other language ever); not only
will Python let you make them mean different things, without
documenting this fact - it actively encourages you to do so.

There were a disturbingly high number of people defending this
(including one quite renowned Pythonista, think it might have been
Effbot). Some had the temerity to fall back on "Explicit is better
than implict: if you want != to work, you should damn well code
__ne__!"

Why, for heaven's sake, should I have to, when in 99.99% of use cases
(and of those 0.01% instances quoted in the argument at the time only
one struck me as remotely compelling) every programmer is going to
want __ne__ to be the logical negation of __eq__? Why, dear Python,
are you making me write evil Java-style language power reducing
boilerplate to do the thing you should be doing yourself anyway?
What's more, every programmer is going to unconciously expect it to
work this way, and be as utterly as mystified as me when it fails to
do so. Don't tell me to RTFM and don't tell me to be explicit. I'll
repeat myself - if I wanted to be explicit, I'd be using C and
managing my own memory thank you very much. Better yet, I'd explicitly
and graphically swear - swear in frustration at this entrenched design
philosophy madness that afflicts my favourite language.

I think the real problem with the explicit is better than implicit,
though, is that while you can see the underlying truth its trying to
get at (which is perhaps better expressed by Ruby's more equivocal,
less dependable, but more useful Principle of Least Surprise), in its
stated form its actually kind of meanginless and is used mainly in
defence of warts - no, we'll call them for what they are, a language
design *bugs*.

You see, the problem is, there's no such thing of explict in
programming. Its not a question of not doing things implicitly; its a
question of doing the most sensible thing implicitly. For example this
python code:

some_obj.some_meth(some_arg1, some_arg2)

is implicitly equivalent to

SomeClass.some_meth(some_obj, some_arg1, some_arg2)

which in turn gives us self as a reference to some_obj, and Python's
OO model merrily pretends its the same as Java's when in fact is a
smarter version that just superficially looks the same.

The problem is that the explicit requirement to have self at the start
of every method is something that should be shipped off to the
implicit category. You should have to be explicit, yes - explicit when
you want the *other* behaviour, of self *not* being an argument,
because thats the more unusual, less likely case.

Likewise,

a != b

is implicitly equivalent to something like calling this function (may
not be correct, its a while since I was heavily involved in this
issue):

def equal(a, b):
if hasattr(a, "__ne__"): return a.__ne__(b)
if hasattr(b, "__ne__"): return b.__ne__(a)
if hasattr(a, "__cmp__"): return not (a.__cmp__(b) == 0)
if hasattr(b, "__cmp__"): return not (b.__cmp__(a) == 0)
return not (a is b)

There's absolutely nothing explicit about this. I wasn't arguing for
making behaviour implicit; I was arguing for changing the stupid
implict behaviour to something more sensible and less surprising.

The sad thing is there are plenty of smart Python programmers who will
justify all kinds of idiocy in the name of their holy crusade against
the implict.

If there was one change I could make to Python, it would be to get
that damn line out of the Zen.
Jul 24 '08
270 7834
Antoon Pardon wrote:
I now have the following question for people who argue that "if x"
is more polymorphic. I could subclass list, so that instances
of this new sequence would always behave as true, even if they are
empty. I could then rewrite my loop as follows:

while 1:
extra = produce()
if not extra:
break
for el in extra:
adjust_with(el)
calculate()

Is this second loop now more polymorphic as the first?
It's more confusing since you've changed the standard behavior of a
standard type, which doesn't really have anything to do with
polymorphism. It's more confusing, if that's a benefit.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 18 N 121 57 W && AIM, Y!M erikmaxfrancis
In a cosmic setting, vast and old, beyond ordinary human
understanding, we are a little lonely. -- Carl Sagan, 1934-1996
Aug 1 '08 #251
On 2008-08-01, Erik Max Francis <ma*@alcyone.comwrote:
Antoon Pardon wrote:
>I now have the following question for people who argue that "if x"
is more polymorphic. I could subclass list, so that instances
of this new sequence would always behave as true, even if they are
empty. I could then rewrite my loop as follows:

while 1:
extra = produce()
if not extra:
break
for el in extra:
adjust_with(el)
calculate()

Is this second loop now more polymorphic as the first?

It's more confusing since you've changed the standard behavior of a
standard type, which doesn't really have anything to do with
polymorphism. It's more confusing, if that's a benefit.
So you accept my point that "if x" can be less polymorphic
and in fact can be more confusing than a more specific test.

--
Antoon Pardon
Aug 1 '08 #252
Antoon Pardon wrote:
On 2008-08-01, Erik Max Francis <ma*@alcyone.comwrote:
>Antoon Pardon wrote:
>>I now have the following question for people who argue that "if x"
is more polymorphic. I could subclass list, so that instances
of this new sequence would always behave as true, even if they are
empty. I could then rewrite my loop as follows:

while 1:
extra = produce()
if not extra:
break
for el in extra:
adjust_with(el)
calculate()

Is this second loop now more polymorphic as the first?
It's more confusing since you've changed the standard behavior of a
standard type, which doesn't really have anything to do with
polymorphism. It's more confusing, if that's a benefit.

So you accept my point that "if x" can be less polymorphic
and in fact can be more confusing than a more specific test.
I think your example is more related to a trap with polymorphism in
general rather than an argument against 'is x' specifically. Any time
you override a method, you have an opportunity to change the behavior in
unexpected ways. Especially in languages like Python that don't do type
checking. For example, you write a __cmp__ method like this:

class ReallyBadPractice(object):
def __cmp__(self, other):
return -cmp(self, other)

Of course any code that does comparisons on this class is going to
behave unexpectedly!

-Matt
Aug 1 '08 #253
Carl Banks wrote:
On Jul 31, 11:44 pm, Carl Banks <pavlovevide...@gmail.comwrote:
[snip excellent explanation of why it's hard to for "if x" to be
extensively polymorphic]
By the way, one thing I forgot to mention is Matt Fitzgibbons' filter
example.

As I said, it's hard to write code that works for both numeric and
container types because they share so few methods. However, sometimes
you don't know ahead of time what methods are used! When you're doing
functional programming you might pass in a method that calls the
appropriate method, like so:

def apply_if_true(func,x):
if x:
func(x)
I find myself doing things like this surprisingly often. All you've done
is move the decision as to what function is applied to x elsewhere. Like
a factory, for example. I could see using something like this where func
prepares object x to be inserted into a database, and you want to make
sure x is meaningful first.

def add_to_db(prep_func, x):
if x:
entry = prep_func(x)
add_to_db(entry)

'if x' strikes me as better for this case because you might want to
accept a non-empty list (or some other objects) but reject non-empty
lists. 'if x is None' would not work. It still may be susceptible to the
empty iterator problem, depending on what prep_func does.
>
The function f could use methods appropriate for ints when x is an
int, and for lists when x is an int.

I did downplay this, because frankly, not many programmers use
functional methods this extensively. But one should also note that
you could pass in the test as well:

def apply_if_condition(func,cond,x):
if cond(x):
func(x)

Thus this usage of "if x" arguably could be considered "replaceable
with a simple explicit test". But in the interests of harmony I
didn't push the point, and it really would have been stretching the
spirit of what I was trying to prove.
Carl Banks
--
http://mail.python.org/mailman/listinfo/python-list
Are you using cond to (a) determine is x is a suitable type to pass to
func, or (b) whether or not to call func, based on some other
characteristics of x?

If (a), I think using a condition is this way is a little goofy. Why not
just allow func to decide is x is an acceptable argument?

(b) is not really what we've been discussing, so I assume not. I would
seriously consider refactoring cond outside the function anyway.

-Matt
Aug 1 '08 #254
Matthew Fitzgibbons wrote:
'if x' strikes me as better for this case because you might want to
accept a non-empty list (or some other objects) but reject non-empty
lists. 'if x is None' would not work. It still may be susceptible to the
empty iterator problem, depending on what prep_func does.
I mean reject empty lists. I need to be slower on hitting send.

-Matt
Aug 1 '08 #255
On 2008-08-01, Matthew Fitzgibbons <el*****@nienna.orgwrote:
Antoon Pardon wrote:
>On 2008-08-01, Erik Max Francis <ma*@alcyone.comwrote:
>>Antoon Pardon wrote:

I now have the following question for people who argue that "if x"
is more polymorphic. I could subclass list, so that instances
of this new sequence would always behave as true, even if they are
empty. I could then rewrite my loop as follows:

while 1:
extra = produce()
if not extra:
break
for el in extra:
adjust_with(el)
calculate()

Is this second loop now more polymorphic as the first?
It's more confusing since you've changed the standard behavior of a
standard type, which doesn't really have anything to do with
polymorphism. It's more confusing, if that's a benefit.

So you accept my point that "if x" can be less polymorphic
and in fact can be more confusing than a more specific test.

I think your example is more related to a trap with polymorphism in
general rather than an argument against 'is x' specifically.
I didn't want to argue against "if x". I just wanted to give a
counter point to the argument that "if x" is more polymorphic.

Whether more or less polymorphic is good or bad, depends on
what kind of polymophism and circumstances.

--
Antoon Pardon
Aug 1 '08 #256
Ethan Furman wrote:
Even if we find out that C.__nonzero__ is called, what was it that
__nonzero__ did again?

reinforce the impression that he is unaware of the double-underscore
functions and what they do and how they work.
Only if your newsreader malfunctioned and refused to let you read the rest of
the paragraph.

Of course I know what __nonzero__ does.

regards, Anders
Aug 1 '08 #257
On Aug 1, 8:49 am, Matthew Fitzgibbons <eles...@nienna.orgwrote:
Carl Banks wrote:
On Jul 31, 11:44 pm, Carl Banks <pavlovevide...@gmail.comwrote:
[snip excellent explanation of why it's hard to for "if x" to be
extensively polymorphic]
By the way, one thing I forgot to mention is Matt Fitzgibbons' filter
example.
As I said, it's hard to write code that works for both numeric and
container types because they share so few methods. However, sometimes
you don't know ahead of time what methods are used! When you're doing
functional programming you might pass in a method that calls the
appropriate method, like so:
def apply_if_true(func,x):
if x:
func(x)

I find myself doing things like this surprisingly often. All you've done
is move the decision as to what function is applied to x elsewhere. Like
a factory, for example. I could see using something like this where func
prepares object x to be inserted into a database, and you want to make
sure x is meaningful first.

def add_to_db(prep_func, x):
if x:
entry = prep_func(x)
add_to_db(entry)

'if x' strikes me as better for this case because you might want to
accept a non-empty list (or some other objects) but reject non-empty
lists. 'if x is None' would not work. It still may be susceptible to the
empty iterator problem, depending on what prep_func does.
What if what you consider to be "meaningful" doesn't happen to
coincide with what Python considers to be "something". For instance,
what if being non-negative is what makes an integer meaningful? You
can't use "if x" for that. What if any list, including an empty one,
is meaningful, but you want to indicate the possibility of an
unmeaningful value by passing None? You can't use "if x" for that.

So, you might address this issue by doing something like this:

def add_to_db(prep_func, is_meaningful, x):
if is_meaningful(x):
entry = prep_func(x)
add_to_db(entry

But if you do that, what has the polymorphism of "if x" gained you?

The thing it gained for you before is not having to pass in a
condition: whether x was a sequence, number, or whatever, the same
condition could be used, and thus you avoided considerable
complexity. But if you have to perform tests for which the implicit
boolean doesn't work, that complexity has to be added to the code
anyway.

That matters in the context of this discussion because it limits the
usefulness of the polymorphism of "if x" for this functional idiom:
"if x" only helps you if you have no need for tests that it can't
handle.

[snip]

Carl Banks
Aug 1 '08 #258
Many of you probably consider me a real jerk. Well, I guess I have
been one here. Believe it or not, I'm actually a pretty nice guy in
real life. Something about the detachment and (partial) anonymity of
being online makes me write things I would never say in person. For
that I apologize.

I had two major fights just on this thread, not to mention a couple of
"minor" ones. Although I believe I was treated unfairly at times,
that was no excuse to resort to insulting replies and "childish name
calling," as one person called it.

From this point on, I will try to stay away from any debate about
anything controversial here on comp.lang.python.

Go ahead, say what you will. Take pot shots if you wish. Maybe I
deserve some. But in the end, you will be saying more about yourself
than about me. I will not reply.
Aug 1 '08 #259
Anders J. Munch wrote:
Ethan Furman wrote:
> Even if we find out that C.__nonzero__ is called, what was it that
__nonzero__ did again?

reinforce the impression that he is unaware of the double-underscore
functions and what they do and how they work.


Only if your newsreader malfunctioned and refused to let you read the
rest of the paragraph.

Of course I know what __nonzero__ does.

regards, Anders
Anders, my apologies. Since reading that post I came across some other
replies you made and realized quite cleary that you do know what you're
doing.

By way of explanation as to why I was confused: the "even if we find
out" suggested to me that you weren't aware of the calling pattern for
"if x" (__nonzero__ if defined, otherwise __len__ if defined). Also, in
your sample code you defined a class C, then assigned the variable c
using "c=get_a_C()", and get_a_C was never defined. Then you posed the
question, "if get_a_C might ever return None"... who knows? You never
said what get_a_C did; furthermore, whether or not get_a_C returns a C
object or None is completely irrelevant to whether or not __nonzero__ is
defined or attribute_is_nonnegative is defined.

FWIW, I completely agree with you as far as naming functions that deal
with less consequential attributes; for an attribute that is central to
the object, I agree with using __nonzero__. For example:
--------------------------------------------------------------------
class Human(object):
def __init__(self, hair, eye, skin):
self.alive = True
self.haircolor = hair
self.eyecolor = eye
self.skincolor = skin
def __nonzero__(self):
return self.alive
def hair_color_is_red(self):
return self.haircolor in ['red', 'strawberry blonde', 'auburn']
def eye_color_is_green(self):
return self.eyecolor in ['green', 'hazel', 'teal']
def skin_color_is_medium(self):
return self.skincolor in ['tanned', 'brown', 'burnished']
def die(self):
self.alive = False
def talk(self):
if self:
print "I don't want to go in the cart!"
def walk(self, where=''):
if self:
if not where:
print "Just taking a stroll..."
else:
print "On my way to " + where

--from human import Human
--charles = Human('blonde', 'teal', 'sunburnt')
--charles
--charles = Human('blonde', 'teal', 'sunburnt')
--if charles:
.... charles.eye_color_is_green()
.... charles.walk("away from the cart...")
.... charles.talk()
....
True
On my way to away from the cart...
I don't want to go in the cart!
--------------------------------------------------------------------

If charles is alive is rather important to the whole object -- if he's
dead, he's not walkin' and talkin'! ;)

~Ethan~
Aug 1 '08 #260
Nevertheless, I think this is probably the best example of the
enhanced polymorphism of "if x" yet. I'm kind of surprised no one
came up with it.)
I think of Python code as 'generic' rather than 'polymorphic'. I am not
sure if that is a real difference or not, since I am a bit fuzzy on the
meaning of 'polymorphic' in this context.

The generality of 'if x:' depends on the context. If x is defined as a
number by doc or previous code (or possibly even by subsequent code),
then 'if x:' is equivalent to 'if x != 0:'. At this point, it is a
stylistic argument which to use.

But here is an example where 'if x:' is more generic.

def solve(a,b):
'a and b such that b/a exists'
if a:
return a/b
else:
raise ValueError('a not invertible, cannot solve'

Now suppose we have a matrix class (2x2 for simplicity and realism).
Its __bool__ (3.0) method implements 'is non-singular'. As written
above, solve(mat,vec) works (with compatible mat and vec sizes), but it
would not with 'if a != 0:'.

Of course, the issue goes away by not looking before the leap:

def solve(a,b):
return a/b
# let callers deal with exceptions
or
try:
return a/b
except...
# raise customized error
In general, the ability to take advantage of "if x" polymorphism
across numeric, container, and other types depends on the something/
nothing dichotomy to be applicable.
Numbers and sequences are both sortable with the same algorithm if it is
written to be generic. However, 0 is nothing special when sorting, so
'if x:' would not be used.

All collections are (or should be) iterable. But a boolean test is not
part of the current iteration protocol. One might want to test whether
the iterable starts with anything before setting things up, but that
either exclude most iterators or requires wrapping them in a lookahead
class with a __bool__ method.

In general, asking code to apply across numeric, container, and other
classes is asking too much. Python code can be generic only within
protocol/interface categories such as number-like, sortable, and
iterable. But making tests too specific can unnecessarily prevent even
that.
Something versus nothing is a useless concept most of the time, but
occasionally finds use in human interaction cases such as printing.
It is sometimes useful within categories of classes, as in my solve example.

Terry Jan Reedy

Aug 1 '08 #261
Carl Banks wrote:
On Aug 1, 8:49 am, Matthew Fitzgibbons <eles...@nienna.orgwrote:
>Carl Banks wrote:
>>On Jul 31, 11:44 pm, Carl Banks <pavlovevide...@gmail.comwrote:
[snip excellent explanation of why it's hard to for "if x" to be
extensively polymorphic]
By the way, one thing I forgot to mention is Matt Fitzgibbons' filter
example.
As I said, it's hard to write code that works for both numeric and
container types because they share so few methods. However, sometimes
you don't know ahead of time what methods are used! When you're doing
functional programming you might pass in a method that calls the
appropriate method, like so:
def apply_if_true(func,x):
if x:
func(x)
I find myself doing things like this surprisingly often. All you've done
is move the decision as to what function is applied to x elsewhere. Like
a factory, for example. I could see using something like this where func
prepares object x to be inserted into a database, and you want to make
sure x is meaningful first.

def add_to_db(prep_func, x):
if x:
entry = prep_func(x)
add_to_db(entry)

'if x' strikes me as better for this case because you might want to
accept a non-empty list (or some other objects) but reject non-empty
lists. 'if x is None' would not work. It still may be susceptible to the
empty iterator problem, depending on what prep_func does.

What if what you consider to be "meaningful" doesn't happen to
coincide with what Python considers to be "something". For instance,
what if being non-negative is what makes an integer meaningful? You
can't use "if x" for that. What if any list, including an empty one,
is meaningful, but you want to indicate the possibility of an
unmeaningful value by passing None? You can't use "if x" for that.

So, you might address this issue by doing something like this:

def add_to_db(prep_func, is_meaningful, x):
if is_meaningful(x):
entry = prep_func(x)
add_to_db(entry

But if you do that, what has the polymorphism of "if x" gained you?

The thing it gained for you before is not having to pass in a
condition: whether x was a sequence, number, or whatever, the same
condition could be used, and thus you avoided considerable
complexity. But if you have to perform tests for which the implicit
boolean doesn't work, that complexity has to be added to the code
anyway.
Of course. If a chunk of code already does what you want it to, then you
can use it. Otherwise you have to do something different. I was just
pointing out that 'if x' often does what I want it to. Sometimes it
doesn't, so I do something different.
>
That matters in the context of this discussion because it limits the
usefulness of the polymorphism of "if x" for this functional idiom:
"if x" only helps you if you have no need for tests that it can't
handle.

[snip]

Carl Banks
--
http://mail.python.org/mailman/listinfo/python-list
By this argument, all code is limiting. Obviously, no code can do what
it can't.

We're not getting anywhere; it's past time to kill this one off.

-Matt
Aug 1 '08 #262
On Aug 1, 3:36 pm, Terry Reedy <tjre...@udel.eduwrote:
Nevertheless, I think this is probably the best example of the
enhanced polymorphism of "if x" yet. I'm kind of surprised no one
came up with it.)

I think of Python code as 'generic' rather than 'polymorphic'. I am not
sure if that is a real difference or not, since I am a bit fuzzy on the
meaning of 'polymorphic' in this context.

The generality of 'if x:' depends on the context. If x is defined as a
number by doc or previous code (or possibly even by subsequent code),
then 'if x:' is equivalent to 'if x != 0:'. At this point, it is a
stylistic argument which to use.

But here is an example where 'if x:' is more generic.

def solve(a,b):
'a and b such that b/a exists'
if a:
return a/b
else:
raise ValueError('a not invertible, cannot solve'

Now suppose we have a matrix class (2x2 for simplicity and realism).
Its __bool__ (3.0) method implements 'is non-singular'. As written
above, solve(mat,vec) works (with compatible mat and vec sizes), but it
would not with 'if a != 0:'.
I see what you're saying, even though this example turns out to be
pretty bad in practice.

(Practically speaking, you hardly ever write code for both matrices
and scalars, because the optimal way for matrices would be convoluted
and opaque for scalars, though scalars can sometimes be fed into
matrix code as a degenerate case. Also, checking the condition of the
matrix by calculating and comparing the determinant to zero is like
comparing float equality without a tolerance, only much, much worse.)

But instead of a matrix, take a vector (which has a better chance of
being used in code designed for scalars) and define a zero-length
vector as false, and that could be a good example.

In general, asking code to apply across numeric, container, and other
classes is asking too much. Python code can be generic only within
protocol/interface categories such as number-like, sortable, and
iterable. But making tests too specific can unnecessarily prevent even
that.
At some point we have to throw our hands up and realize that if we're
working with custom classes with varying degrees of nonconformance,
there is nothing we can do that's safe.

Something versus nothing is a useless concept most of the time, but
occasionally finds use in human interaction cases such as printing.

It is sometimes useful within categories of classes, as in my solve example.
I'm going to say no.

Let's take your matrix example: you defined singularity to be false,
but singularity can't reasonably be mapped to the concept of nothing.
For "nothing" I would think you'd want a 0x0 matrix. Something vs
nothing also implies that all objects should have a boolean value.
So, assuming boolean is connected to a matrices singularity, what
should be the boolean value of non-square matrices?

No, I'm going to have to disagree very strongly with this.
Nonzeroness is useful. Emptiness is useful. Singularity a kind of
useful. Nothing and something are vague, ill-defined, ad hoc concepts
that mean nothing to a computer. Have you ever seen an algorithm that
says "if x is something"?

Something and nothing do seem to come into play occasionally in that,
when interacting with humans (be it the user or programmer), it
sometimes--not nearly always--makes sense to treat nonzero and empty
in the same way. But there's no particular reason to apply the
concepts of something and nothing beyond this pragmatic use.
Carl Banks
Aug 1 '08 #263
On Aug 1, 4:45 pm, Carl Banks <pavlovevide...@gmail.comwrote:
On Aug 1, 3:36 pm, Terry Reedy <tjre...@udel.eduwrote:
Nevertheless, I think this is probably the best example of the
enhanced polymorphism of "if x" yet. I'm kind of surprised no one
came up with it.)
I think of Python code as 'generic' rather than 'polymorphic'. I am not
sure if that is a real difference or not, since I am a bit fuzzy on the
meaning of 'polymorphic' in this context.
The generality of 'if x:' depends on the context. If x is defined as a
number by doc or previous code (or possibly even by subsequent code),
then 'if x:' is equivalent to 'if x != 0:'. At this point, it is a
stylistic argument which to use.
But here is an example where 'if x:' is more generic.
def solve(a,b):
'a and b such that b/a exists'
if a:
return a/b
else:
raise ValueError('a not invertible, cannot solve'
Now suppose we have a matrix class (2x2 for simplicity and realism).
Its __bool__ (3.0) method implements 'is non-singular'. As written
above, solve(mat,vec) works (with compatible mat and vec sizes), but it
would not with 'if a != 0:'.

I see what you're saying, even though this example turns out to be
pretty bad in practice.

(Practically speaking, you hardly ever write code for both matrices
and scalars, because the optimal way for matrices would be convoluted
and opaque for scalars, though scalars can sometimes be fed into
matrix code as a degenerate case. Also, checking the condition of the
matrix by calculating and comparing the determinant to zero is like
comparing float equality without a tolerance, only much, much worse.)

But instead of a matrix, take a vector (which has a better chance of
being used in code designed for scalars) and define a zero-length
vector as false, and that could be a good example.
In general, asking code to apply across numeric, container, and other
classes is asking too much. Python code can be generic only within
protocol/interface categories such as number-like, sortable, and
iterable. But making tests too specific can unnecessarily prevent even
that.

At some point we have to throw our hands up and realize that if we're
working with custom classes with varying degrees of nonconformance,
there is nothing we can do that's safe.
Something versus nothing is a useless concept most of the time, but
occasionally finds use in human interaction cases such as printing.
It is sometimes useful within categories of classes, as in my solve example.

I'm going to say no.

Let's take your matrix example: you defined singularity to be false,
but singularity can't reasonably be mapped to the concept of nothing.
For "nothing" I would think you'd want a 0x0 matrix. Something vs
nothing also implies that all objects should have a boolean value.
So, assuming boolean is connected to a matrices singularity, what
should be the boolean value of non-square matrices?

No, I'm going to have to disagree very strongly with this.
Nonzeroness is useful. Emptiness is useful. Singularity a kind of
useful. Nothing and something are vague, ill-defined, ad hoc concepts
that mean nothing to a computer. Have you ever seen an algorithm that
says "if x is something"?

Something and nothing do seem to come into play occasionally in that,
when interacting with humans (be it the user or programmer), it
sometimes--not nearly always--makes sense to treat nonzero and empty
in the same way. But there's no particular reason to apply the
concepts of something and nothing beyond this pragmatic use.

Carl Banks
Aug 1 '08 #264
On Aug 1, 4:45 pm, Carl Banks <pavlovevide...@gmail.comwrote:
On Aug 1, 3:36 pm, Terry Reedy <tjre...@udel.eduwrote:
In general, asking code to apply across numeric, container, and other
classes is asking too much. Python code can be generic only within
protocol/interface categories such as number-like, sortable, and
iterable. But making tests too specific can unnecessarily prevent even
that.

At some point we have to throw our hands up and realize that if we're
working with custom classes with varying degrees of nonconformance,
there is nothing we can do that's safe.
And I want to make clear I'm not trying to downplay your example
here. The example you gave me definitely fits the criteria of being a
useful "if x" that can't be replaced by a simple explicit test, at
least after my alteration to a vector example.

It's a concern when you write code that breaks realistic custom
classes, but really concerning when it breaks built-in classes.
Carl Banks
Aug 1 '08 #265
On Jul 28, 8:46*pm, "Russ P." <Russ.Paie...@gmail.comwrote:
Letting "self" (or whatever the first argument was) be implied in
".cat" does absolutely *NOTHING* to change the internal workings of
the Python interpreter. It's a very simple idea that you insist on
making complicated. As I said, I could write a pre-processor myself to
implement it in less than a day.
I implemented such a pre-processor 6+ years ago and I am absolutely
sure
I was not to first one to reinvent this particular wheel. But now I
have grown up
and I am happy with self, because sometimes it can
be named cls, mcl, obj or anything else, and I take advantage of this
fact in my code. self is more readable than a dot and there is
no point in making a special case for it.

Michele Simionato
Aug 3 '08 #266
On Aug 3, 9:29*am, Michele Simionato <michele.simion...@gmail.com>
wrote:
On Jul 28, 8:46*pm, "Russ P." <Russ.Paie...@gmail.comwrote:
Letting "self" (or whatever the first argument was) be implied in
".cat" does absolutely *NOTHING* to change the internal workings of
the Python interpreter. It's a very simple idea that you insist on
making complicated. As I said, I could write a pre-processor myself to
implement it in less than a day.

I implemented such a pre-processor 6+ years ago and I am absolutely
sure
I was not to first one to reinvent this particular wheel.
I have found the relevant thread:

http://groups.google.com/group/comp....10d0be45113c23

See also the comment by Alex Martelli.

M.S.
Aug 3 '08 #267
On Jul 31, 6:15*pm, Paul McGuire <pt...@austin.rr.comwrote:
On Jul 28, 12:15*pm, Kay Schluehr <kay.schlu...@gmx.netwrote:


On 28 Jul., 06:42, "Russ P." <Russ.Paie...@gmail.comwrote:
On Jul 27, 8:58 pm, castironpi <castiro...@gmail.comwrote:
On Jul 27, 2:39 pm, Bruno Desthuilliers
<bdesth.quelquech...@free.quelquepart.frwrote:
Derek Martin a écrit :
It's bad programming, but the world is full of bad programmers,and we
don't always have the choice not to use their code. *Isn't one of
Python's goals to minimize opportunities for bad programming?
Nope. That's Java's goal. Python's goals are to maximize opportunities
for good programming, which is quite different.
Oh, gosh, that is so clever. What a bunch of crap.
+1 QOTW
Do you realize what an insult that is to everyone else who has posted
here in the past week?
Nothing glues a community together so well as a common enemy. Or even
better: two enemies i.e. Perl and Java in Pythons case. On the other
hand, some enemies have to be ignored or declared to be not an enemy
( Ruby ), although oneself is clearly an enemy for them. The same
antisymmetry holds for Python and Java. Java is an enemy for Python
but Python is not worth for Java to be an enemy as long as it can be
ignored. C++ and Java are enemies for each other. Same holds for Java
and C#.- Hide quoted text -
- Show quoted text -

Help... being... sucked into... black hole... inside a... Klein...
bottle...- Hide quoted text -

- Show quoted text -
... which is inside... the black hole...
Aug 3 '08 #268
On 2008-08-01, Terry Reedy <tj*****@udel.eduwrote:
>Nevertheless, I think this is probably the best example of the
enhanced polymorphism of "if x" yet. I'm kind of surprised no one
came up with it.)

I think of Python code as 'generic' rather than 'polymorphic'. I am not
sure if that is a real difference or not, since I am a bit fuzzy on the
meaning of 'polymorphic' in this context.

The generality of 'if x:' depends on the context. If x is defined as a
number by doc or previous code (or possibly even by subsequent code),
then 'if x:' is equivalent to 'if x != 0:'. At this point, it is a
stylistic argument which to use.

But here is an example where 'if x:' is more generic.

def solve(a,b):
'a and b such that b/a exists'
if a:
return a/b
else:
raise ValueError('a not invertible, cannot solve'

Now suppose we have a matrix class (2x2 for simplicity and realism).
Its __bool__ (3.0) method implements 'is non-singular'. As written
above, solve(mat,vec) works (with compatible mat and vec sizes), but it
would not with 'if a != 0:'.

Of course, the issue goes away by not looking before the leap:

def solve(a,b):
return a/b
# let callers deal with exceptions
or
try:
return a/b
except...
# raise customized error
Maybe I'm going to be pedantic here, but I fear that your code won't
work with matrices. The problem is that multiplication is not
commutative with matrices. This means that matrices have two divisions a right
and a left division. A far as I know the "/" operator usaly implements
the left division while solving is done by using a right division.

So your code will probably fail when applied to matrices.

--
Antoon Pardon
Aug 4 '08 #269
Antoon Pardon wrote:
Maybe I'm going to be pedantic here, but I fear that your code won't
work with matrices. The problem is that multiplication is not
commutative with matrices. This means that matrices have two divisions a right
and a left division. A far as I know the "/" operator usaly implements
the left division while solving is done by using a right division.

So your code will probably fail when applied to matrices.
You're right in your general point, but usually division between
matrices isn't defined at all because of this ambiguity. However the
general point can still exist between `solveLeft` and `solveRight`
functions which do something along the lines of a*b**-1 and a**-1*b. A
single `solve`, of course, would choose one of these, and syntactically
it might be quite reasonable to define matrix division that does one of
these and have a single `solve` function that accomplishes it.

Therefore, the general point about polymorphism still stands.

--
Erik Max Francis && ma*@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 18 N 121 57 W && AIM, Y!M erikmaxfrancis
It is human nature to think wisely and act foolishly.
-- Anatole France
Aug 4 '08 #270


Erik Max Francis wrote:
Antoon Pardon wrote:
[responding to me]
>Maybe I'm going to be pedantic here, but I fear that your code won't
work with matrices. The problem is that multiplication is not
commutative with matrices. This means that matrices have two divisions
a right
and a left division. A far as I know the "/" operator usaly implements
the left division while solving is done by using a right division.

So your code will probably fail when applied to matrices.
Your remarks are correct as far as they go, but..
You're right in your general point, but usually division between
matrices isn't defined at all because of this ambiguity. However the
general point can still exist between `solveLeft` and `solveRight`
functions which do something along the lines of a*b**-1 and a**-1*b. A
single `solve`, of course, would choose one of these, and syntactically
it might be quite reasonable to define matrix division that does one of
these and have a single `solve` function that accomplishes it.

Therefore, the general point about polymorphism still stands.
as Eric suggests, I was assuming that details would be defined to make
my example work. The context was the challenge to find an example
where, for instance, 'if x:' would work but something more specific like
'if x != 0:' would not. My abstract answer was "number-like class with
division that is not valid for the class's null object". The concrete
answer was an incomplete instantiation of that for matrices. Given the
critique, polynomial division might be a better example since polynomial
multiplication is commutative while both ()(if allowed) and (0,) as
polyomials might well be defined as != to numeric 0. I suspect there
are other algebra classes that work for this or other examples, but it
has been a loooooong time since I took abstract algebra.

Actually I sort of worked too hard ;-).
The decimal module breaks the transitivity of equality!
>>import decimal as d
z=d.Decimal('0.0')
z==0
True
>>z==0.0
False
>>0==0.0
True

So if someone wrote 'if x == 0.0:' instead of 'if not x:' (perhaps to
raise an exception with explanation for unusable input), the former
would not work.

tjr

Aug 5 '08 #271

This thread has been closed and replies have been disabled. Please start a new discussion.

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.