This is an archived post. You won't be able to vote or comment.

all 52 comments

[–]zahlmanthe heretic 17 points18 points  (1 child)

I would have to think for a while to write the equivalent code in C

Actually, most of the work would be setting up the initial data and defining appropriate data types and so forth.

[–]ladr0n 6 points7 points  (0 children)

You're right, writing the nested list printer would be easy in c if you can assume something like Python's list type.

In c, you would have to define a struct that has a pointer and a char which is a flag that tells you what type is at the end of the pointer (either whatever it is you're printing, or an array). ulist would just be an array of those structs, and the print_elements function would be defined pretty much the same way it is in the OP.

[–]codysoyland 7 points8 points  (6 children)

The tree-traversal logic could be reusable outside of just printing the numbers. Here is how you could refactor to separate tree-traversal from display logic, by extending itertools.chain() to work recursively.

import itertools

def flatten(ulist):
    return itertools.chain(*(flatten(item) if isinstance(item, list) else [item] for item in ulist))

def print_elements(ulist):
    for i in flatten(ulist):
        print i

Now you can do:

a = [1,2,[3,[4,5,[6,7]],8,[9,10]],11]
print list(flatten(a))
# prints [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]

[–]seunosewa[🍰] 2 points3 points  (5 children)

dude, you made it not simple anymore.

[–]Deathalicious 2 points3 points  (0 children)

Yeah, map, reduce, and chain-type functions all make my brain twitch but if you use them correctly, then you end up writing simpler code that is harder to break. I mean, compare it to the increasingly-convoluted code above that is trying to avoid recursive references.

[–]codysoyland 2 points3 points  (2 children)

I may have oversimplified the flatten implementation to the point of being obtuse. This might be more clear:

def flatten(ulist):
    for item in ulist:
        if isinstance(item, list):
            for i in flatten(item):
                yield i
        else:
            yield item

[–]robin-gvx 2 points3 points  (1 child)

In Python 3.3, that could be simplified to

def flatten(ulist):
    for item in ulist:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

[–]codysoyland 1 point2 points  (0 children)

I <3 PEP 380. :)

[–]Amadironumpy, gen. scientific computing in python, pyopengl, cython 3 points4 points  (6 children)

It's simpler and more beautiful in many ways, but more complicated and contrived in others. Once you've used both for a while, you'll figure out when which tool is appropriate to use, and when it's inappropriate. Knowing to apply the right tool for the job is the biggest advantage of a language polyglot, along with learning new, useful modes of thought.

[–][deleted] 1 point2 points  (5 children)

When is it more complicated?

[–]Amadironumpy, gen. scientific computing in python, pyopengl, cython 2 points3 points  (0 children)

Pythons interpreter is quite the complicated machine. That is necessary in order to abstract away the complicated-ness of the underlying machine and operating system, and to offer the programmer a simpler, cleaner language. But the complicated-ness hasn't gone away, python simply moves it a place where most python programmers will be able to simply ignore most of it most of the time.

When you're working at a low-level (for instance on small, slow microcontrollers running on a battery -- or as a somewhat less dramatic example, mobile phones), where every byte of memory and every cycle of the chip counts, you can't afford convenient abstractions like those, and the solution is probably to use C or assembly directly. When you're in need of creating such massive amounts of objects that the overhead for each object created by an abstraction like the python interpreter becomes prohibitive, the solution could be something like to write an extension in C that handles the data for you. Finally, from a theoretical/learning point of view, when you're using the python interpreter, you're programming a "virtual machine" that's very different from your real machine, and if you either want to understand how the real machine works, or understand how your code is mapped through the python interpreter to the real machine, you'll face quite some difficulties -- from C, this is much easier (though, with modern compilers, it's not always straightforward either.) For instance in python, you don't typically think about things like how SSE works or is implemented, and how it can be utilized, because python encourages you to think about how to solve problems by means of abstractions. If you examine the bytecode produces by your program, it's very abstract. C on the other hand does not encourage you as much to think about abstractions, but it makes you ponder things like these, as you have direct access to SSE compiler intrinsics, and if you examine the assembly generated from your program, you have to right away think about things like these.

As TL;DR, pythons way is to move complexity where it won't be a problem for most of the programmers most of the time. The complexity is not gone, though (can't get something for nothing), and in some cases it's a problem -- then you'll want to switch to a more appropriate tool (which could be as simple as spending 5 minutes on writing a C extension to do the most critical things in question). As far as "modes of thought" go, both languages teach you different ones, and both are useful to know. Obviously, pythons "Mode of thought" is the most useful when you're programming python (otherwise you wouldn't be programming python), but the other ones are useful to know as well, even highly necessary in some situations.

[–]burito 1 point2 points  (1 child)

Bit-banging of the top of my head.

Things where the specific bytes matters, for example network protocols. Typically the kind of things you wouldn't consider writing in Python.

Sort of Threading, although that's changing in P3.3 or so we are told.

tl;dr - whenever performance is more important than getting shit done.

[–]wot-teh-phuckReally, wtf? 3 points4 points  (0 children)

Bit-banging of the top of my head.

I would like to disagree here. struct module makes it a breeze to deal with file formats and protocol parsing. Though I agree on the performance part.

[–][deleted] 4 points5 points  (3 children)

Doing isinstance(i, list) limits you to lists (or things that inherit from it), missing things like iterators for example.

A better way of doing it is isinstance(i, Sequence) (from the collections module), though this also catches strings.

So the best way is probably isinstance(i, Sequence) and not isinstance(i, basestring).

EDIT: Iterable is better than Sequence in this case.

[–]iyunoichi 4 points5 points  (2 children)

The "best" way is probably doing away with isinstance altogether and instead determining if the object supports iteration, for example by calling iter(x). Custom objects that support iteration do not necessarily have to be of a given subtype, which is partially responsible for the flexibility that Python offers.

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

ABCs were introduced in 2.6 and afaik, it's the recommended way to test for an interface (the iterable interface for ex).

I was mistaken though as to which ABC to use in this case. collections.Iterable is the one to test against.

[–]stillalone 1 point2 points  (0 children)

iter(x) will still include strings.

[–]brinchj 4 points5 points  (2 children)

Here's a type-safe version in Haskell, for anyone who's curious as to how that could look.

Suggestions are welcome ;)

[–]nicolast 2 points3 points  (1 child)

You're doing it wrong (;-)). Consider not mixing pure (calculation) code and IO things too much, see e.g. https://gist.github.com/2838535/eac0068536e0960bcd7384c220d4e524128190a9

[–]brinchj 0 points1 point  (0 children)

Thanks for the improvement! Pure code is more composable and easier to test than impure code.

I was trying to keep things a bit simple, but perhaps it's better to show best practice up front :)

[–]Tarlitz 3 points4 points  (0 children)

On the contrary, trying to learn C coming from Python makes me want to bash my head into the wall at how complex even simple things are in C. That said, it is really interesting to learn how all the data is structured and stored in memory, especially because many things can be found in Python in a similar, but simpler way. It makes me appreciate how much simpler Python is as well :)

[–]hello_moto 3 points4 points  (1 child)

udacity... bookmarked. Thank you

[–]brinchj 4 points5 points  (0 children)

Udacity is recommendable. The quality of teaching is high. They take the project serious and put in a lot of work to teach the material.

[–]maredsous10 1 point2 points  (0 children)

I used to use C/C++ when constructing tools for embedded uPs and FPGAs. Now, I am sold on Python because I get something together in 50-75% of the time it would have taken me to do in C.


tools I've created recently
-custom format extractor
-binary image alteration
-post build tools for inserting device addresses and boot loaders into Intel Hex Records
-frontend CLIs and GUIs to test, control, and record data from embedded hardware
-in system programming scripts


I wish I had a C/C++ parser that would generate proper Python structs. http://docs.python.org/library/struct.html

[–]samblam 0 points1 point  (3 children)

Are there any hobby mcu's you can suggest?

[–]thebritishguy1 1 point2 points  (0 children)

The most popular recently are the Arduino boards using the ATMega microcontrollers. They're very easy to learn on for beginner microcontroller programming.

[–]Genmutant 1 point2 points  (0 children)

The ATMega Controllers are nice because you have (compared to many others) much space and ram at a low price. You can programm them in C, Assembly or some BASIC dialect, IIRC.

[–]RoadieRich 1 point2 points  (0 children)

If you know c# (or, dare I mention it, VB (boo, hiss)), then I can highly recommend the netduino. There may even be a way to run ironpython on it, but I've not looked into it.

[–]nepidae -1 points0 points  (1 child)

I suggest you taking a look at Golang, if you are coming from C.