Live data from Hacker News

Unpythonic Python

skien.cc

131–140 of 156 posts

Re: Unpythonic Python

#131
post #40

When you write too much Haskell, your Python code starts to look like this: print('\n'.join( 'FizzBuzz' if x%5==0 and x%3==0 else 'Fizz' if x%3==0 else 'Buzz' if x%5==0 else str(x) for x in range(1, 101))) I would really like to have a "let" expression in Python to avoid having to write a new function with a def statement when you could get away with a simple lambda or generator expression.

And when you write C carefully, the C code looks like this (thanks seanjensengrey and rhth54656 for ideas):

    int i; static char* a[] = { "%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n" };
    for ( i = 1; i 
The loosely similar Python:

   for i in range( 1, 101 ):
        print( [ i,'Fizz','Buzz','FizzBuzz' ][ (i%5==0)*2 + (i%3==0) ] )

Re: Unpythonic Python

#132
post #116

Doesn't even touch on my personal pet peeve, people who don't use list and dict literals. I assume they're former Java programmers who got ahold of enough python knowledge to be dangerous. e.g: x = dict() x['a'] = 'string' x['b'] = list() x['b'].append('foo') x['b'].append('bar')

You can say

    x = dict(a = 'string', b = list(('foo', 'bar')))

Re: Unpythonic Python

#133

Nobody ever seems to go for the general solution: words = ( (3, 'Fizz'), (5, 'Buzz') ) def fizzbuzz(num): for value, word in words: if i % value == 0: yield word for i in range(1, 101): print ''.join(fizzbuzz(i)) or i

It works even though it should be: words = ((3, 'Fizz'), (5, 'Buzz')) def fizzbuzz(num): for value, word in words: if num % value == 0: yield word for i in range(1, 101): print ''.join(fizzbuzz(i)) or i

Ah. Thanks for pointing that out! Interesting quirk with shared global scope!

Re: Unpythonic Python

#134

Nobody ever seems to go for the general solution: words = ( (3, 'Fizz'), (5, 'Buzz') ) def fizzbuzz(num): for value, word in words: if i % value == 0: yield word for i in range(1, 101): print ''.join(fizzbuzz(i)) or i

It works even though it should be: words = ((3, 'Fizz'), (5, 'Buzz')) def fizzbuzz(num): for value, word in words: if num % value == 0: yield word for i in range(1, 101): print ''.join(fizzbuzz(i)) or i

I think a list comprehension reads better than a generator (and works the same) in this case:

  def fizzbuzz(num):
      return (word for value, word in words if num % value == 0)
And even dropping the fizzbuzz function altogether reads quite nicely IMHO, though it starts to look a bit golfed:

  words = ((3, 'Fizz'), (5, 'Buzz'))

  for i in range(1, 101):
      print ''.join(s for n, s in words if i % n == 0) or i

Re: Unpythonic Python

#135

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    static char c[9];
    
    int p(int i)
    {
        putchar(c[i++]);
        putchar(c[i++]);
        putchar(c[i+(7-i)]);
        putchar(c[i+(8-i)]);
        return 0;
    }
    
    int f(int i, int x) {
        i -= x;
        if(!i) { return p(x); }
        if(i

Re: Unpythonic Python

#136
post #131
post #40

When you write too much Haskell, your Python code starts to look like this: print('\n'.join( 'FizzBuzz' if x%5==0 and x%3==0 else 'Fizz' if x%3==0 else 'Buzz' if x%5==0 else str(x) for x in range(1, 101))) I would really like to have a "let" expression in Python to avoid having to write a new function with a def statement when you could get away with a simple lambda or generator expression.

And when you write C carefully, the C code looks like this (thanks seanjensengrey and rhth54656 for ideas): int i; static char* a[] = { "%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n" }; for ( i = 1; i The loosely similar Python: for i in range( 1, 101 ): print( [ i,'Fizz','Buzz','FizzBuzz' ][ (i%5==0)*2 + (i%3==0) ] )

And my Visual C 6 compiles the above C to this x86 asm with a single conditional jump, just for the loop:

    mov	edi, DWORD PTR __imp__printf
    mov	esi, 1
   L1:
    mov	eax, esi
    cdq
    mov	ecx, 5
    idiv	ecx
    mov	eax, esi
    mov	ebx, 3
    push	esi
    mov	ecx, edx
    neg	ecx
    sbb	ecx, ecx
    cdq
    idiv	ebx
    inc	ecx
    neg	edx
    sbb	edx, edx
    inc	edx
    lea	edx, DWORD PTR [edx+ecx*2]
    mov	eax, DWORD PTR arr[edx*4]
    push	eax
    call	edi
    add	esp, 8
    inc	esi
    cmp	esi, 101
    jl	SHORT L1
The magic is in the neg sbb combination: The neg changes reg to two's complement but also sets or clears the CF if the argument was != 0 then sbb reg,reg effectively moves CF to the reg avoiding conditional jump for != 0.

Re: Unpythonic Python

#137

One of the solutions in the comments I found quite pythonic and concise. Somehow people have it in their heads that "Pythonic" means long-winded. And yes, you have to read the code and think for a second to understand it, but that's no crime. [(not x % 3) * 'Fizz' + (not x % 5) * 'Buzz' or x for x in range(1, 101)]

I've programmed Python before but I had to fire up a REPL to know that:

    True * "String"
    >"String"
And...

    not 0
    >True
I guess knowing the "truthiness" of all regular Python types is useful.

Re: Unpythonic Python

#138

Earlier quoted context omitted.

I think this code is easier to read than the more verbose 12-line version given in the article. It takes longer to read per line, but less time total.

It's not just more complexity per line, it's also a higher level of complexity, using language-specific features that people who aren't fluent in python wouldn't be familiar with (multiplying a string by a boolean)

I'm fairly new to Python, though not to software development, and one of the nice things I find about Python is that when I see something I haven't seen before (in this case multiplying a string by a boolean) I can usually guess correctly what it will do and spend a few seconds with the REPL to confirm.

Of course, this is true to a certain extent of all programming languages, but I do find Python particularly easy in this respect.

Re: Unpythonic Python

#139
post #135

#include #include #include #include #include #include static char c[9]; int p(int i) { putchar(c[i++]); putchar(c[i++]); putchar(c[i+(7-i)]); putchar(c[i+(8-i)]); return 0; } int f(int i, int x) { i -= x; if(!i) { return p(x); } if(i

This'd be more fun if it didn't segfault

    printf(n%15 ? n%3 ? n%5 ? "%d\n" : "Buzz\n" : "Fizz\n" : "FizzBuzz\n", n);

Re: Unpythonic Python

#140

Earlier quoted context omitted.

By that logic, no one should write anything in idiomatic French because anyone who isn't fluent in French wouldn't be able to read it.

I was curious about that. Is multiplying a string by a boolean idiomatic Python? I don't write nearly enough Python to know, but it strikes me that this might be more like writing French using lots of obscure words.

This starts to get into "what is idiomatic python?" which changes as the language evolves. For example, before Python had an official ternary form, this was a common idiom:

    account.status = ["paid", "unpaid"][amount_due > 0]
This does the same thing as the FizzBuzz example, coercing a bool to int. Here the int is used as an index into the list of the two strings.

Personally, I found this handy and liked it a lot. Others didn't and now there is this, which isn't bad:

   account.status = "unpaid" if amount_due > 0 else "paid"

Maybe it's less like using obscure French and more like speaking in a slightly different dialect, or in a different region with different cultural references.
Post reply on HN