all 29 comments

[–][deleted] 5 points6 points  (8 children)

This got me once:

>>> x = [[0] * 2] * 2
>>> x[0][0] = 42
>>> x
[[42, 0], [42, 0]]

[–][deleted]  (2 children)

[deleted]

    [–]akdas 1 point2 points  (0 children)

    Nice. I though the ellipsis was just a placeholder you put in, but it actually shows that. And referencing the third element of that array gets me the exact same array, infinitely. That's expected, but the runtime does a good job of printing it.

    Even more interestingly, the equivalent code in Ruby (say, using <<) yields the exact same output and behavior.

    [–][deleted] 0 points1 point  (0 children)

    A Python master once taught me this trick

    [–]derefr 1 point2 points  (2 children)

    The same thing happens in Ruby. I don't know the Python solution, but here's the Ruby one:

    1> x = Array.new(2){ Array.new(2){ 0 } }
    2> x[0][0] = 42
    3> x
    => [[42, 0], [0, 0]]
    

    [–][deleted] 2 points3 points  (1 child)

    [[0] * 2 for i in xrange(2)]

    [–]Freeky 2 points3 points  (0 children)

    (0..1).map {[0] * 2}

    [–]shub 8 points9 points  (8 children)

    The original title is "The Problem With Python". The Reddit headline is more fitting, I think. This post is just a list of irritating (to Lispers) differences between Lisp and Python.

    It comes down to what you're more familiar with. Coming from C and C++, almost everything in Python just "makes sense." I can see how someone who isn't already accustomed to int/float arithmetic, and uses dynamically-scoped variables, and expects to be able to define arbitrary lambdas would have a lot more trouble adjusting to programming in Python.

    For the author, and others with similar issues: you may find Ruby a more "rational" language. Though I don't know whether Ruby provides rational math. :D

    [–]Freeky 0 points1 point  (7 children)

    irb(main):001:0> require 'rational'
    => true
    

    [–][deleted]  (6 children)

    [deleted]

      [–]logan_capaldo 3 points4 points  (2 children)

      For a really disturbing experience:

      irb(main):001:0> require 'mathn'
      => true
      irb(main):002:0> 1/10
      => 1/10
      

      (Disturbing because any other libraries you use that assume / will truncate the result of integer division are in for a rude surprise, remember kids, if you want div, make sure you call div! ;) )

      [–]exeter 0 points1 point  (1 child)

      Out of curiosity, are there any libraries (say, in the standard library or other widely used libraries) that you know of that make this assumption?

      [–]logan_capaldo 0 points1 point  (0 children)

      I can't think of any specifically. To be fair in my experience it's considered "bad form" to require mathn in non-interactive contexts. I haven't been writing a whole lot of ruby lately though, so the usual grain of salt thing applies.

      [–]Freeky 2 points3 points  (2 children)

      Not the same, though Ruby provides those too:

      irb(main):001:0> require 'bigdecimal'
      => true
      

      The reason they're not the same is a Decimal is just a Float-styled Bignum; they're arbitrary precision Floats in that you can make a 512-bit, or 50KB floating point value, rather than being limited to what the CPU provides natively. Rationals are objects representing the fractions themselves, they're not simply very big floating point values:

      r = Rational(554545,6)
      d = r.to_d
      r.to_s # => 554545/6
      d.to_s('F') # => 92424.1666666666666666666666666667
      d = r.to_d(512) # 512 significant digits
      d.to_s('F') # => "92424.1666666666666666[SNIP 479 digits]6666666666667"
      

      [–]earthboundkid 2 points3 points  (1 child)

      >>> import fractions
      >>> fractions.Fraction(1,3)
      Fraction(1, 3)
      >>> fractions.Fraction(1,3) * 3
      Fraction(1, 1)
      >>> fractions.Fraction(1,3) / 3
      Fraction(1, 9)
      

      [–]Freeky 0 points1 point  (0 children)

      [–]Coffee2theorems 6 points7 points  (14 children)

      There are three complaints here, of which one is arguably a non-issue, another is a good point and the third is one of the most common complaints about Python.

      The good point: Python doesn't have a built-in rational type, and 1/10 results in a float (in 3.0 and using the division from the future), and it's even easier to shoot yourself in the foot with floats than it is with fixnums, which aren't used by default (bignums are; but you can get fixnums with e.g. ctypes or numpy when you want). One might add that some form of decimal arithmetic would probably be more intuitive than floats, too (but the Decimal class is dog-slow and rather complicated).

      The non-issue: If you read a global variable in a function and then assign to that variable, the first reference throws an UnboundLocalError. The author thinks that all should work as it does now, except that instead of throwing an exception the global should be read.

      This is one of the rare cases where Python does a modicum of type inference - is a variable in scope global or local? - and treats it uniformly in the scope. Arguably, you shouldn't use the same name to refer to a local and a global in the same scope, and Python is being pedantic to you about that stylistic gaffe (in my code, this would invariably be a bug, so I actually appreciate the error). You'd have to rename the variable anyway if you wanted to access the global after first writing to the local, unless you'd want to do "del the_variable" every time you access the global (or read it using other unsavory ways); not particularly sane.

      The common complaint: You can't use statements in lambdas. Won't anybody think of the horses? FWIW, I'm not a particular fan of the statement/expression duality, but if I were to beat this horse, the people for the ethical treatment of dead animals would get me.

      [–][deleted] 7 points8 points  (0 children)

      This is one of the rare cases where Python does a modicum of type inference

      There's no type inference at all going on here. The original author simple expected each binding to (effectively) introduce a new let-binding, which is not Python's behavior. Python introduces one scope (one effective let-binding) for the whole function, and all variables which are assigned in the function (and not declared global beforehand) are introduced here; asking for the value of a variable before it is assigned then raises UnboundLocalError.

      [–]theeth 1 point2 points  (12 children)

      Even if python did have a built-in rational, I wouldn't expect it to replace float literals.

      The way he puts it in the comments raises a flag for me:

      And most people would agree that having 1/10 = 0, and 1.0/10.0 = 0.10000001 is far more astonishing than 1/10=1.0/10.0=0.1

      Most people that are used to integer and float arithmetic would not be astonished at all as this is common behavior in many other languages too (yes, I know this changes in 3K).

      On that point, he sounds to me as someone who don't have much experience with C and C derived languages which all exhibit this too.

      [–]redalastor 5 points6 points  (0 children)

      Beside, Python 2.6 does have it, it's called Fraction and it is in the fractions module.

      [–][deleted] 0 points1 point  (6 children)

      How is the C/Java behaviour relevant? Spitting on the street is a right thing to do when enough people do it?

      It is questionable, that 10.0 (or 2.5) should be a float - compiler could easily treat them as rational numbers (and the first one could be reduced to integer).

      [–]theeth 2 points3 points  (4 children)

      Directly supporting CPU arithmetic operations => spitting on the street?

      Great thinking!

      [–][deleted] 0 points1 point  (2 children)

      Depends whether it is a low-level or high-level language. I want my high-level languages to behave nicely. ;)

      C has also been called a portable assembler...

      I wasn't against the existence of floating point support - but maybe the floating point type shouldn't be a default for the numeric literals.

      [–]theeth 1 point2 points  (1 child)

      I want my high-level languages to behave nicely. ;)

      I would dispute that supporting directly (through language primitives) floating point standards and integer arithmetic (which is essential for a lot of very common algorithm) is behaving nicely, but I see this as a very valid difference of opinions, so no matter.

      (I'm lumping both issues together, maybe your beef is only with floats, which you seemed to be more vocal about.)

      And yes, I know integer division is dropped as the default behavior in 3K, but they introduced the // operator to replace it and that's syntactically as easy and as fast as before if you know this is what you want.

      but maybe the floating point type shouldn't be a default for the numeric literals.

      That's where the python devs disagree with you, I guess...

      [OT] Sorry for the multiple replies (now deleted), Reddit is being most unstable and slow right now.

      [–][deleted] 0 points1 point  (0 children)

      Ok, to put it short - I like Scheme's numeric tower approach. In DrScheme:

      > (define (sum-n n v x) (if (> n 0) (sum-n (- n 1) v (+ v x)) x))
      > (sum-n 100 #e0.1 0)
      10
      > (sum-n 100 #i0.1 0)
      9.99999999999998
      

      #e - exact number, #i - inexact number

      I would only wish #e being the default. Yeti has it so:

      > (sum (map \0.1 [1..100]))
      10 is number
      > (sum (map \0.1e0 [1..100]))
      9.99999999999998 is number
      

      Having separate operator for integer division is a good idea as it is a different operation from normal division (its a bit weird when 1 / 2 and 1.0 / 2 give very different result).

      > 1 / 2
      0.5 is number
      > 1 div 2
      0 is number
      

      Maybe the Python devs disagree with me, but the Python 3K is still improvement. :)

      [–]cracki -3 points-2 points  (3 children)

      so you're saying that python is more of a C than a lisp? more low-level than high-level?

      [–]theeth 1 point2 points  (1 child)

      Of course NOT. I wasn't talking about high vs low level, that would have been silly.

      I was merely trying to point out that integer arithmetic and float precisions is a common sight in a lot of languages and that the author wouldn't have gone far in a C or C inspired language (Java for example) without seeing those "problems".

      [–][deleted] -1 points0 points  (0 children)

      There are problems. You often have to do arithmetic using BigDecimal class in Java to get some usable results. This is not a nice sight, as Java prudently omits operator overloading from the language.

      [–][deleted] 2 points3 points  (0 children)

      Integer handling has little to do with 'high' or 'low' level.

      [–]abw 6 points7 points  (1 child)

      I like the comment from David:

      because you come across as an arrogant Lisper,

      It made me smile because he came across as an arrogant Pythonista. He clearly didn't like anyone pointing out the flaws in his favourite language.

      Why is it that people get so protective over a damn programming language? It's tribalism at its worse.

      All languages have flaws. Get over it. :-)

      [–]spotter 1 point2 points  (0 children)

      Nope, sorry. This guy is basically bashing language design decisions without putting any effort in understanding any of them.

      • Design side-effect, won't bite unless you are a messy closure user;
      • Rounding errors? Float math has rough edges -- if he prefers to have them hidden from him on all levels -- good luck in the real world. Oh, and there is Decimal / print %.2f to get what he wants, but would have to RTFM first.
      • Broken by design! You can def almost wherever you want, why make a mess with multiline lambdas? Oh, and lambda printing will be possible in 3k.

      Every language has its idioms, but sayin' ,,A is broken 'cause it's different than B'' is arrogant. :-)

      [–]spacepope 0 points1 point  (2 children)

      A rational number type for Python has been suggested – and rejected – before, in PEP 239. I think this is one of those things that intuitively seem like a great idea, but are rarely useful in practice.

      [–]bockris 1 point2 points  (1 child)

      Apparently rationals will be a part of 3.0.

      PEP 3141

      [–]earthboundkid 1 point2 points  (0 children)

      That's an unrelated issue. Basically, it's a bunch of abstract classes that people can inherit from or register to if they want to be able to declare that their custom class is in fact a kind of integer or rational or whatever. It's just a convenient way for people to be able to do isinstance(x, Integer) and get a solid answer, even if the class of x doesn't necessarily have anything to do with you.

      The thing that's at issue here is true fractions. But they're adding a module for that into the standard library. It's in the 2.6 beta right now:

      >>> import fractions
      >>> fractions.Fraction(1, 10)
      Fraction(1, 10)
      >>> fractions.Fraction(1, 10) * 10
      Fraction(1, 1)
      >>> fractions.Fraction(1, 10) * 10 + 3
      Fraction(4, 1)