11 ms·
A few things to remember while coding in Python
- Estragon 14y agoAnother handy one I saw recently: varname, = [x for x in l if predicate_with_single_truth_value(x)] The comma after varname is an implicit assert that the list comprehension only contains one element.
- tikhonj 14y agoTrailing commas are really easy to miss. When reading this line of code, I did not notice it immediately; I originally assumed that varname was being assigned a list. This sort of code would be very confusing when I'm just quickly reading through a procedure trying to find the potential bug.
- vog 14y agoIndeed. I prefer the following variant which is more explicit and thus (IMHO) more in the spirit of Python: (varname,) = [x for x in l if predicate_with_single_truth_value(x)]
- almost 14y agoAgreed. It could be written much more clearly in my opinion like this: [varname] = [x for x in l if predicate_with_single_truth_value(x)]
- Estragon 14y agoGood idea!
- euccastro 14y agoI had to check that on the REPL. I'm surprised that even works and I can't think of a good reason why should list syntax be allowed as a lvalue, in addition to tuple syntax.
- Estragon 14y agoIt's called destructuring assignment. It's been around for a while. http://dunsmor.com/lisp/onlisp/onlisp_22.html http://dunsmor.com/lisp/onlisp/onlisp_22.html
- lucian1900 14y agoYou could also use the ,= operator, of course: varname ,= [...]
- andreasvc 14y agoIt appears to me that there is actually no such operator in Python; cf. http://docs.python.org/reference/simple_stmts.html#augmented-assignment-statements http://docs.python.org/reference/simple_stmts.html#augmented... Superficially it looks like an operator, but I suspect that's merely because of whitespace freedom; i.e., a, = [0] is equivalent to a,=[0] and a ,= [0].
- jgeralnik 14y agoI assume it was a joke; a pop culture reference to this stack overflow question: http://stackoverflow.com/questions/1642028/what-is-the-name-of-this-operator http://stackoverflow.com/questions/1642028/what-is-the-name-...
- lucian1900 14y agoSure it is! And in Python 3 there's the ,_*= operator, similar to lisp's car: varname ,_*= [1, 2, 3] # varname == 1
- andreasvc 14y agoHN isn't the place for facetiousness.
- lucian1900 14y agoApparently it isn't the place for humour either.
- makecheck 14y agoTrailing commas are a bit subtle and readers of the code may think it was a mistake. (We spend more time reading code later than writing it in the first place.) Even Python removed one of its most prominent cases of this, the print command. In Python 2.x you could have a trailing comma after a print to omit the new-line but Python 3's print() function requires print('something', end=' ') to be more explicit about it.
- reinhardt 14y agoA better way: varname = (x for x in l if predicate_with_single_truth_value(x)).next()
- Estragon 14y agoThat avoids the construction of the list, but doesn't check that the sequence only contains one value, which was the point of the example.
- bcl 14y agoMy only complaint with this list is conditional assignments. They make it harder for me to understand the code. I am used to the if being at the start and when it isn't it takes more time for me to parse what the real meaning of the line is.
- mcguire 14y agoOne of the reasons Python stopped being my favorite programming language was the appearance of what seemed to me to be Perl envy. I hadn't seen conditional assignments before, but they don't exactly make me regret that choice.
- halayli 14y agoIt's worth explaining why mutable defaults are bad. The problem with mutable defaults is that they are evaluated once only when the function is defined. Each time the function is called you'll be using the same mutable variable that was created during function definition.
- ollysb 14y agoThat seems like decidedly unexpected behaviour and makes default params far less useful.
- jsprinkles 14y agoActually, if you understand the way Python is evaluated (dig in, the core is pretty transparent), it's the only behavior that makes sense in this case. It's also documented as such[1], so it's quite expected. Default parameters are still just as useful for constants, such as: def f(x=0, y="foo", z=3.14159): This, however, is a perfectly Pythonic idiom: def f(L=None): if L is None: L = [] [1]: http://docs.python.org/reference/compound_stmts.html#function-definitions http://docs.python.org/reference/compound_stmts.html#functio...
- swdunlop 14y agoOr just embrace the tao of the tuple: def f(L=()): ...
- ollysb 14y agoWhile it seems logical when you understand what's going on, from a practical point of view I can't see how this would ever be useful. The tradeoff appears to be that the functions are first class objects. I'm not sure what the benefit here is though. Does having them as first class objects allow some useful idioms? (I'm a ruby dev but I'm genuinely curious to know what this allows you to do)
- jsprinkles 14y ago
- ajitk 14y agoTill now I had never seen Ellipsis. It seems very similar to slice notation [:]. Found an StackOverflow comment [1] that has more details about usage of Ellipsis in slicing higher dimensional array numpy. [1] http://stackoverflow.com/questions/118370/how-do-you-use-the-ellipsis-slicing-syntax-in-python http://stackoverflow.com/questions/118370/how-do-you-use-the...
- grifaton 14y agoHere's a rare example of obfuscated python, deceptively called "Python's Ellipsis Explained": http://blog.brush.co.nz/2009/05/ellipsis/ http://blog.brush.co.nz/2009/05/ellipsis/
- DasIch 14y agoThe only way in which Ellipsis can be useful is to save one character by using it instead of pass in Python3, like: def foo(): ... It is really highly unusual and I wouldn't recommend the practice shown in the blog post at all as this is not a common pattern.
- deleted 14y ago[deleted]
- lihaoyi 14y agohalve_evens_only = lambda nums: map(lambda i: i/2, filter(lambda i: not i%2, nums)) I still find it rather silly that python doesn't supper a nice list map/filter; it could be so much nicer nums.filter(lambda i: i%2 == 0).map(lambda i: i/2) If they did, even including the annoyingly long-to-type "lambda". List comprehensions are cool and all, but do not really scale visually (i.e. get rather messy) when you have more than one map and filter step. These arbitrary break-away from OO method style into module+data style (len(L) is another!) are one of the things I hate most about Python. There are some reasons for doing so, but a pure-OO (like Scala) or pure-method+data (like F#) would have saved me many a runtime error.
- darkstalker 14y agothe D language supports that kind of syntax nums.filter!(i => i%2 == 0).map!(i => i/2);
- fferen 14y agoTo elaborate, this is because of D's Uniform Function Call Syntax (http://www.drdobbs.com/blogs/cpp/232700394 http://www.drdobbs.com/blogs/cpp/232700394) - any function that takes an object as first argument can be called as though it were a method of that object; "map" and "filter" are actually functions in the std.algorithm module. It's a pretty neat trick and although in principle it could make things harder to reason about I haven't had any problems with it so far.
- lihaoyi 14y agoso does Scala stuff.filter(_ % 2 == 0).map(_ / 2) and C#, demonstrating that this isn't some obscure feature that only language geeks care about: Stuff.Where(x => x % 2 == 0).Select(x => x / 2) My point isn't that this sort of syntax is new and novel, it's just that in Python it's annoyingly inconsistent. There are reasons where you would want to use type-class style modules to structure your code in a certain way, but I do not think python's map() filter() reduce() and len() qualify as these cases
- swdunlop 14y agoPython offers filtering expressions in its generator syntax. I find Python's "lambda" hurts readability for most uses, which pains me as a Lisp geek. Your example, as a generator: halve_evens_only = (i / 2) for i in nums if (i%2 == 0) The parens aren't necessary, but they help readability for people who aren't used to the generator order of operations. (Again, Lisp geek, more parens means more readable in my fractured mind.)
- jc4p 14y agoGenerally, most use this: freqs = {} for c in "abracadabra": try: freqs[c] += 1 except: freqs[c] = 1 Who does this?!
- stcredzero 14y agoImmature programmers. It's analogous to musicians who play everything as fast and ornately as possible.
- swdunlop 14y agoBeat me to my equivalent answer: "java programmers."
- stcredzero 14y agoNot a mature answer.
- makecheck 14y agoNot only is it overly-verbose and ultimately unnecessary (as shown by the alternative that follows in the article) but this mechanism is actually broken. For instance if "freqs" were data given to you from somewhere else and "freqs[c]" happened to have a type that cannot legally have 1 added to it, you'd want to see this error. The "except:" however will absorb any and all exception types and respond to all of them by setting the value to 1.
- Estragon 14y agoAlso, raising exceptions used to be quite slow, which could hurt for a sparse counting set. Don't know whether that's still the case.
- underwater 14y agoAlso Exceptions should be used in "exceptional" circumstances and not as part of normal flow.
- stcredzero 14y agoGenerally most use this: freqs = {} for c in "abracadabra": try: freqs[c] += 1 except: freqs[c] = 1 If this is really the common idiom, I'd say this is a sign that professional programming has yet to fully mature as a field. Some may say a better solution would be: freqs = {} for c in "abracadabra": freqs[c] = freqs.get(c, 0) + 1 Okay, so I understood immediately what was going on with the 2nd bit of code. Rather go for the collection type defaultdict from collections import defaultdict freqs = defaultdict(int) for c in "abracadabra": freqs[c] += 1 As a non-pythonista, the 3rd bit of code, I had to Google "defaultdict" to figure out. It's only a couple of seconds to Google, and a professional should know this tidbit, but it seems like premature optimization to me. This brings to mind this post: http://news.ycombinator.com/item?id=3995185 http://news.ycombinator.com/item?id=3995185 As a programmer, one's most valuable resource is brainpower. Supposedly, a programmer's most important goal is writing clear code. Look around at what goes on in our industry. There's a lot of our most valuable resource spent on showing off our cleverness, not directed towards the clearest code. To me this is like spending money to show one can spend money or playing an instrument to show off dexterity instead of producing gorgeous sounds. (I think this starts in school and other environments where one is motivated to show off one's coding chops.) Most of the complexity in our field accrues like litter: a bit here and a bit there. I think it says something about the culture of the folks who live there.
- nostrademons 14y agoWell, yes, but I'd also like to think that code should raise the level of the programmers reading it. You shouldn't avoid language features just because some people don't know about them. That's as ridiculous as the folks who say "Don't use the ?: operator because folks who haven't taken Intro CS 101 may not be familiar with it." So you spent a couple seconds Googling defaultdict. Great, you now know what a defaultdict is and can use it in your own code. It's useful in a lot of places besides this toy example. You should avoid gratuitous complexity, where you force the reader to learn something that will never, ever be useful to them again. A great example might be writing your own encryption algorithm, which will be complicated, wrong, under-performant, and totally useless on any other project. If you just use bcrypt (or whatever the recommended best practice is now), then your code works well, and all readers of your code now know about bcrypt and can use it themselves.
- harlowja 14y agoProbably best not to use iteritems, in python 3, it won't be there, "the dict.iterkeys(), dict.iteritems() and dict.itervalues() methods are no longer supported."
- jsprinkles 14y agoBecause the regularly-named ones got better.
- andreasvc 14y agoThe iter* methods appear in Python 3, just without the iter prefix. Therefore it is good to use them in Python 2 because that makes it possible to translate the code automatically with the 2to3 script. Otherwise possibly superfluous list conversion might get added, e.g., list(d.keys()).
- DasIch 14y ago.keys(), .views() and .items() in Python3 return a memoryview which happen to be iterators but do far more.
- raymondh 14y agoOverall, this is a nice post. There are two quibbles though. 1). For the most part, "c = collections.Counter()" is almost always better than "c = defaultdict(int)" * Counter only supplies missing values rather than automatically inserting them upon lookup. * The Counter version is much clearer about what it is trying to do. The defaultdict version is cryptic to the uninitiated (understanding it entails knowing that it has a __missing__ method to insert values computed by a factory function and that int() with no arguments returns zero). * The Counter version provides helpful methods such as "most_common(n)". 2). An ellipsis in Python is normally used in a much different way than shown in the article (it's used for an extended slice notation in NumPy).
- Camillo 14y agoUnfortunately, Counter is not available before 2.7, so for many people it's a bit too early to require it.
- MBlume 14y agoThis. On reading this post, I ran to my computer to replace defaultdict(int) with Counter in some of my code, only to find that I couldn't use it yet.
- njharman 14y agoTwo years old is a long enough.
- sitkack 14y agoOSX 10.6.8 still has Python 2.6 as the system Python.
- raymondh 14y agoThere is a Python2.5 backport of collections.Counter() at http://code.activestate.com/recipes/576611/ http://code.activestate.com/recipes/576611/
- phpfanboi 14y agoPHP: Facebook. Python: ? Ruby: ?
- mappu 14y agoPython: Reddit. Ruby: Twitter.
- phpfanboi 14y agothanks, I've always wondered about that. to the downvoters: thank you for letting the world see the hatred.
- pyrotechnick 14y agoYou will find the most critical components of Facebook are not written in PHP: https://github.com/facebook https://github.com/facebook
- kracekumar 14y agoPython: Youtube
- gcb 14y agoisn't items more efficient than iteritems when you are going to go over all the items for sure on a short list?
- Estragon 14y agoNo, never. The items method generates an intermediate list.
- euccastro 14y agoThere is a builtin function called `reversed`. You'd better remember that than the "useful" [::-1] idiom. The recommendation on `iteritems` had better be generalized to include `iterkeys`, `itervalues`, and other opportunities for using iterators rather than building lists. A note that the 'iter...' versions are removed in Python 3 (because iterator behaviour becomes the default) would be appropriate here. In relation to collections, itertools is a great module to get familiarized with. I import * from this module. I consider functions there as if they were builtins. "Conditional assignment" is a weak and misleading name. "Conditional expressions" is more descriptive. There is no assignment in print "yes" if some_condition else "no" as the article acknowledges later. Using Ellipsis for getting all items is a violation of the Only One Way To Do It principle. The standard notation is [:]. I commend the good intentions of the writer, but I'm surprised that this article got 144 upvotes in HN.
- heretohelp 14y ago>There is a builtin function called `reversed`. You'd better remember that than the "useful" [::-1] idiom. Reversed produces an iterator and not a copy with all the dangers of mutable semantics. The [::-1] syntax is not equivalent as it returns a copy of the list as if it were reversed, changing up somewhat what it's doing. You'd better remember that you should know what you're talking about before you start dictating to other people how they should write their code.
- jongraehl 14y agolist(reversed(xs)) to make a copy rather than a view, though
- shenberg 14y agoThere is one slight mistake there - saying that [::-1] is a special case. An empty value in a slice implies the beginning or the end, and when the stride is negative, the beginning is the last index, while the end is 0 - making [::-2] for example start from the last element and go down in jumps of two.
- Terretta 14y agoSatyajit Ranjeev: Since this is posted by the you as the author, I'll comment here: some JavaScript is running on page load that blanks the entire page in Safari and iCab on iPad, making the page turn white except for the bullet symbols, and making the article unreadable. I was able to read it only by disabling JavaScript or parsing it with Readability. (Both disable my ability to comment about this bug there.)
- FiloSottile 14y agoYou should think of adding the use of `with` context managers.