

Python Makes Me Say God Damn - coderdude
http://sam.tregar.com/blog/2010/05/19/python-makes-me-say-god-damn/

======
ggchappell
It's interesting to see how different people's complaint lists can be. I have
my own list for Python (e.g., mutable/immutable/reference semantics/blah blah
blah stuff messes me up all the time), but my list has almost nothing in
common with the one in this article.

In particular, I've thought about the first issue -- range specifications --
off & on for years, and I've come to the conclusion that this writer is wrong;
Python does ranges the right way. The crux of the matter, for me, is that
sometimes includes-the-end feels right, and sometimes does-not-include-the-end
feels right. _However_ , sometimes includes-the-end is _definitely_ wrong, but
I don't know of any case where does-not-include-the-end is definitely wrong.
So if you're going to pick one convention, then go with the one Python did;
use it all the time, and you'll never have to wonder which one you're using.
It _is_ true that this convention takes some getting used to, but I think it
results in fewer bugs in the long run.

Other advantages: b - a is the number of items in range(a,b); also range(a,b)
and range(b,c) partition range(a,c) nicely.

BTW, I believe E. Dijkstra has an essay that also takes this position on range
specifications; he probably explained it a lot better than I did.

As for Unicode, no one seems to know how to do it well. But this writer is
talking about the Python 2 way of doing things. Python 3 has improved things,
IMHO.

~~~
barrkel
Range as [a, b) is definitely one of the best ways of thinking about them in
the context of most algorithms. b-a is reliably the length; and when you
choose a midpoint m, [a, m) and [m, b) don't overlap. Thinking in these terms
makes implementing e.g. quicksort and binary search much easier.

~~~
nailer
My math is rusty. Could you explain your use of square and regular brackets in
[a, m) and [m, b) ?

~~~
alan-crowe
The common useage in mathematics is in analysis and topology, to specify
subsets of the real numbers. Remembering that _open_ and _closed_ are jargon
terms in topology:

(a,b) is an open set and contains exactly those x such that a is less than x
and x is less than b.

[a,b] is a closed set and contains exactly those x such that a is less than or
equal to x and x is less than or equal to b

For example, one might be thinking about the interval [0,pi] but the function
1/sin x has singularities at both ends of the interval. It is only defined on
(0,pi). Notice that 1/x is defined on (0,pi]. It is sometimes convenient to
mix up open=exclusive=() and closed=inclusive=[].

These conventions can be carried over into computer science in the obvious way
[0,3) = {0,1,2} while (0,3] = {1,2,3} but the motivation is different. In
computer science we are more concerned how smoothly our subranges chain
together. For example

[0,r)+[r,s)+[s,t) = [0,t)

while

[0,r]+[r+1,s]+[s+1,t] = [0,t]

------
stevenwei
Aside from the unicode thing, none of the items on that list bother me
particularly much. There is one that I find really irritating though:

    
    
      def foo(a = [])
          a.append(123)
    

or the class based version:

    
    
      class Foo(object):
          a = []
    
          def foo(self):
              a.append(123)
    

Neither example behaves like I would expect it to.

For a language that espouses 'there should only be one, and preferably only
one, obvious way to do it', that is not obvious to me at all!

Oh, and I wish it had multi-line lambdas / blocks.

~~~
tetha
Granted, it is not completely intuitive. However, it makes sense if you know
pythons module loding mechanisms:

The function definition is a statement which is evaluated at module load time,
and it registers the foo-function in the module namespace. During creating the
foo-function, the default-parameters are evaluated (resulting in a single list
instance), and the results of this evaluation are used as default parameters
in the following. That is also why something like def foo(x = random_value())
is kind of tricky and should be documented.

------
JoelMcCracken
There are probably 10-15 things that really bother me about python, but none
of them are in this list. This list is really pretty darn awful.

My biggest issue with python is its stupid tendency toward imperative
programming in the standard library. For an instance of this irritation, try
running python, then executing dir(<any object you pick>). Then, try
dir(<obj>).sort . Notice anything? Yeah, the f'er sorted it in place, and
didn't return anything. Nice job, library.

I once tweeted that python was a c programmers version of lisp, and ruby is a
lisp programmers version of c, and the more time I spend working with both
languages, the more I am convinced of the truthfulness of this statement.

~~~
aston
The reason .sort() doesn't return anything is _because_ it sorts in place.
That way you won't get confused between the returned list and the sorted one.

Maybe you're looking for sorted(dir(<obj>)), which gives back a new list?

------
viraptor
While I could agree with some of the points, here are some comments:

 _range()_ \- "Perl has trained me", well lolcode taught be that I end script
with KTHNXBYE, but that's not the reason to expect it in other languages :(
It's just an arbitrary choice and there's no reason to discuss about it
really.

 _Python is happy to do nothing_ \- Meet PyLint. Perl has "use warning",
python has PyLint, which will tell you that:

    
    
        C:  1: Black listed name "foo"
        W:  3: Statement seems to have no effect
    

_Unicode support is a mess_ \- I really hate how it's done, but it definitely
is not a mess. It's clean and strict. It's just hard to achieve some stuff
with it. I really miss a "just give me an ascii version and ignore any errors,
because I really don't care in this case" option...

The rest... I can see why you might not like them, but I don't see them as
design errors in any specific way.

~~~
conesus
> I really miss a "just give me an ascii version and ignore any errors,
> because I really don't care in this case" option...

You can use options to either ignore or replace invalid characters.

    
    
        >>> unicode('\x80abc', errors='strict')
        Traceback (most recent call last):
          File "<stdin>", line 1, in ?
        UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0:
                            ordinal not in range(128)
        >>> unicode('\x80abc', errors='replace')
        u'\ufffdabc'
        >>> unicode('\x80abc', errors='ignore')
        u'abc'

------
mbenjaminsmith
Python is my favorite language, but finding a way to iterate over a list while
changing the list kills me. I remember there is a good reason for Python's
behavior but it always surprises me how non-intuitve a solution is.

~~~
Luyt
Python is not unique in this regard. Consider a functional approach, where you
leave the original list intact and construct a changed one.

~~~
koenigdavidmj
List comprehensions or map are the way that a Pythonista would do this:

[transform(x) for x in my_list]

map(transform, my_list)

------
gte910h
Unicode was fixed....in the newest version of the language. All of us just
need to switch to that unfortunately.

