6 ms·
Essential Python Tips, Tricks, and Hacks
- stcredzero 18y agoLess syntax in languages, please. The problem with the Perlish "There's more than one way to do it!" is that one ends up expending extra cycles comprehending semantic equivalence. But equivalence should be a fulcrum -- these are things leveraged to be able to reduce the effective complexity of what we're looking at. Anything that obscures the visibility of equivalence is bad on the balance.
- earthboundkid 18y agoIf your language allows both (+ 1 2) and (+ 2 1) then there's more than one way to do it. There's always more than one way to do it. That's just the nature of languages. Even in pure arithmetic, both 1.99999… and 2 represent the same number. It's inescapable. The point isn't "let's only have one way to do it." The point is to have One Obvious Way To Do It. And that's the Python motto. What this article is doing is showing the mappings between the way you might be used to from other languages to the preferred One Obvious Way of Python. Decrying syntax is nuts. Humans work better if they can pick out visually what's going on. Even Lisp people indent their ifs and defuns. (And if Lisp doesn't have syntax, what the hell do ' or # or (in Arc) [] do?) Now, if we have to have at least a little syntax it's also nice if it's easy to pick out from the visual impression of the syntax what it is that the programmer is getting at. In my opinion, Python does a great job of this.
- stcredzero 18y ago(+ 1 2) vs (+ 2 1) is a far cry from "map(lambda x: x * x, numbers)" versus "[x * x for x in numbers]". I call straw man. I am not asking for zero syntax, or that everything be done in Scheme. For one thing, I'm not a Lisper, which you think I am. For another thing, Lisp has syntax, so you are incorrect. For yet another thing, I am decrying Too Much Syntax that encourages too many ways of doing things, not taking a dogmatic stand for absolute syntactic minimums. True one cannot make a syntax so pure that there is only one way of doing something. That's a straw man. But on the other hand, one can make a syntax so complicated that it seems to strongly encourage doing the same thing in many ways. Perl and Ruby are guilty of this. Python not nearly as much, but still more than I would like. Syntactic constructs are a tool. Tools have overhead. Why have redundant syntactic tools? It's enough to get one's head around the problems to be solved.
- nostrademons 18y ago5.1: I prefer to use the rule "don't mutate objects" instead. I would've defined the example as: def function(item, stuff=[]): print stuff + [item] Mutating the passed-in 'stuff' creates a leaky abstraction that then has to be documented. In rare cases this is what you want, but most of the time you're better off creating new objects and working with them. There's less to remember in the API, and fewer ways to screw up. In particular, this code - which looks safe - blows up horribly with the article's function: MODULE_LEVEL_CONSTANT = ['foo', 'bar'] function('baz', MODULE_LEVEL_CONSTANT) function('quux', MODULE_LEVEL_CONSTANT) The second call will print ['foo', 'bar', 'baz', 'quux'] instead of the expected ['foo', 'bar', 'quux'].
- njharman 18y agoHuh? I expect ['foo', 'bar', 'baz', 'quux']. I expect accumulation, esp with a list. If I wanted ['foo', 'bar', 'quux'] I would send in a fresh copy of a list instead of a list I'd been whoring around to other functions. function('baz', list(MODULE_LEVEL_CONSTANT)) function('quux', list(MODULE_LEVEL_NOT_CONSTANT)) I would also change MODULE_LEVEL_CONSTANT to not say CONSTANT cause a list is no such thing and doesn't become constant just cause a label says so. Maybe it even leads people to expect silly things.
- earthboundkid 18y agoI dunno, the Python convention is "if you don't mutate, return a value; if you do mutate, return None." Since you wrote function('baz', MODULE_LEVEL_CONSTANT) without an assignment in front of it, I would assume that function must mutate something. Since strings are immutable, that just leaves either the display (print) or the second argument. I guess the real point is don't mutate your arguments and print them. Do one or the other, but not both.
- andreyf 18y agoNot for me... >>> def function(item, stuff=[]): print stuff + [item] >>> MODULE_LEVEL_CONSTANT = ['foo', 'bar'] >>> function('baz', MODULE_LEVEL_CONSTANT) ['foo', 'bar', 'baz'] >>> MODULE_LEVEL_CONSTANT ['foo', 'bar'] >>> function('quux', MODULE_LEVEL_CONSTANT) ['foo', 'bar', 'quux'] I think you're confusing + and .append (the latter affects the object).
- BrandonM 18y agoThe article really has something against map and filter. Specifically: squares_under_10 = filter(lambda x: x < 10, map(lambda x: x*x, numbers)) squares_under_10 = [number*number for number in numbers if number*number < 10] Those two lines are functionally different; the second one (the one the article prefers) performs a costly multiplication twice. I'm not saying list comprehensions are bad... I use them a ton. I just acknowledge that there are times where map and filter make a lot of sense. Even for trivial reasons like wrapping lines, using map allows you to break at commas instead of embedding backslashes in your code and trying to decide how it should all align. If you haven't, check out the itertools standard library module. The power held in these functional, generator-based functions is amazing. Since I started using it, I don't know how many times I've used: imap(func, izip(somelist, repeat(somevalue))) and other such paradigms. Hmm... more problems with the article: numbers = [1,10,100,1000,10000] if [number for number in numbers if number < 10]: print 'Success' # Output: 'Success!' This is terrible. It may look nice all on one line, but it goes through the whole list when it doesn't need to. Better is: for number in numbers: if number < 10: print "Success" break A problem with using the and ... or paradigm: result = test and 'Test is True' or 'Test is False' The page mentions that "Test is True" must evaluate to True or else it will always be skipped over even if test evaluates to True. It fails to mention, however that "Test is False" must also evaluate to True. Otherwise, if test evaluates to False, the entire expression returns False. Thus s = foo and "word: "+foo or "" will assign False to s instead of the desired "". In this case, it is quite clear that s = "word: "+foo if foo else "" is preferable. I didn't find the article all that helpful, personally. It puts forward inefficient solutions as the recommended methods, and it doesn't offer much that I didn't already know. The two things that I learned, which I'm not sure that I'll ever use, are function decorators and the "string in substring" syntax.
- scott_s 18y agoPreferring the lambda expression because the list comprehensive performs a multiplication operation twice is, I think, misguided. The multiplication probably isn't that expensive compared to the rest of the code.
- yourmomis1337 18y ago
- yourmomis1337 18y agoYou know if you create an account on the site you can edit the article, like a wiki.
- BrandonM 18y agoThat is a great idea for getting new users... make subtle mistakes in articles and then require people to register in order to correct them. I had actually looked for an "edit" link or a way to e-mail the original author, but I didn't want to go through the hassle of registering before being sure that I'd be able to fix the problems.
- yourmomis1337 18y agolol... that's true ;) Although I'm going to try to add all the corrections you guys had when I get a chance (probably tomorrow).
- brianr 18y agoAn improvement on the switch-using-dictionaries example: instead of doing this... keycode = 2 functions = {1: key_1_pressed, 2: key_2_pressed, 3: key_3_pressed} if functions.has_key(keycode): functions[keycode]() else: unknown_key_pressed() you can save three lines by replacing the if-else block with: functions.get(keycode, unknown_key_pressed)()
- yourmomis1337 18y agoWow, yeah, that one is fairly obvious huh...