27 ms·
Anti-Patterns in Python Programming
- orf 12y agoInteresting read, a couple of things I noticed though: 1. In "Checking for contents in linear time" both examples are the same. Perhaps remove the list entirely in the second example 2. Itertools.islice helps if you need to slice a list with a bajillion elements
- tribaal 12y agoThe more frequent and dangerous pitfalls are, in my humble opinion: - Bare except: statements (that catches everything, even Ctrl-C) - Mutables as default function/method arguments - Wildcard imports!
- scott_w 12y agoPossibly the most interesting anti-pattern I saw was: a_list_of_words = "my list of words".split(" ") I never enquired why, since there were bigger issues in the code e.g. "unit testing" by running the code, taking the result and putting it as the check value. By running repr(value), copying out the string then comparing self.assertEqual(repr(value), '[<Object1: unicode_value>, ...]')
- oddthink 12y agoI do that all the time in the interpreter, especially when slicing pandas DataFrame objects, e.g.: df_subset = df['date buyer nwidgets'.split()] That is far easier to type than the explicit list, with all its punctuation. Now, it's definitely weird that they did a `split(" ")` rather than just using the default, but the idea is the same. I do try to strip stuff like that out before I put it into a script, replacing it with the explicit list, but I'm never sure if that actually improves anything. It's not as if the explicit list is any easier to read.
- klibertp 12y agoIt's not weird, it's wrong: In [1]: "a string r".split(" ") Out[1]: ['a', '', '', '', 'string', '', '', '', '', 'r'] In [2]: "a string r".split() Out[2]: ['a', 'string', 'r']
- glomph 12y agoIn their case though that wouldn't have been a problem as each word was split on a single space.
- hcarvalhoalves 12y agoSplit without argument is equivalent to: >>> filter(None, " quick hack for split".split(" ")) ['quick', 'hack', 'for', 'split']
- klibertp 12y agoYeah, or re.split(r'\s+', ...), or something - the issue here is that you need to know about this. From the comment a few levels up I understood that the code which used the str.split with " " argument didn't signify that someone who written it knew about its semantics. If he did and it was really what was intended then ofc it's completely ok, but if not, it can easily lead to bugs. For example, if the user is required to input several ints separated with whitespace, this: map(int, input_str.split()) will rise only in expected cases, while this: map(int, input_str.split(" ")) can lead to rejecting correct input just because someone pressed space twice. It's very frustrating for the user, too, because whitespace are hard to spot visually. So, I don't know if this qualifies as antipattern, but I think if I saw .split(" ") instead of .split() in the code I'd at the very least expect the comment explaining why it's used.
- blossoms 12y agoI don't mean to be pedantic, but a list (I am assuming df is a list) requires an int. (That sentence I wrote about using hashable types need not apply, sorry!) If you ran that code, you would get this error: TypeError: list indices must be integers, not list
- coldtea 12y agoHow is this a real "anti-pattern"? Inefficent, or bizarre way to do it maybe. Anti-pattern is supposed to mean something more, though. In this case there are no adverse effects and no ambiguity -- so, I guess the programmer was just lazy to construct the list.
- bkeroack 12y agoWhy even use a list here? Tuples are for immutable/constant data. a_tuple_of_words = ("my", "tuple", "of", "words") or a_tuple_of_words = "my", "tuple", "of", "words"
- dozzie 12y ago...because it's a list? Tuples were supposed to have a structure (at least that's what all the rest of the world thinks of them), so iterating through combination of apples, cars and languages makes no sense whatsoever. But yes, Python misses entirely the point of tuples, treating them as read-only lists. http://dozzie.jogger.pl/2014/04/11/python-tuples-the-useless-thing-in-language/ http://dozzie.jogger.pl/2014/04/11/python-tuples-the-useless...
- bkeroack 12y agoI'm not sure what you're getting at here, or what you are expecting tuples to be like. They can have as much "structure" as you need--they're just a collection. Lighter-weight, immutable collections have a use case. The code in OP appears to be one where it makes sense. I follow the rule where variables are mutable IFF they need to be mutable.
- dozzie 12y agoTuples by pythonists are used as they were mere lists, just immutable. This is clearly displayed by Python's own interface. For the rest of the world, tuples are not immutable lists. They are tuples, i.e. collections of "objects" that could share nothing about their type. Tuples often are not even iterable! (Erlang, Haskell) The fact that tuples in Python can have as much structure as one wants is derived from dynamic typing, not from the tuples' nature. The same you could say about Python's lists. This is a really subtle issue. It takes to know more languages to see it clearly.
- bkeroack 12y agoIf it's so subtle, does it matter? This sounds like you just have a problem with the word "tuple" applied to an object that behaves differently from tuples in a statically-typed language. Would you feel better if they named it "ImmutableList" instead?
- james2vegas 12y agoperhaps that line was written by someone used to Perl, where they would have had @a_list_of_words = qw/my list of words/; there
- bluecalm 12y agoThis is actually useful: you may want to experiment with different list_of_words in the future and typing the words between [" ", " ", " "] is time consuming. It's also less readable.
- cabalamat 12y ago> Mutables as default function/method arguments It would really make sense to change the semantics of Python to fix this issue.
- klibertp 12y agoRemove a `static` keyword from C (used inside a function) while you're at it...
- bkeroack 12y agoChange them how, to no longer have functions be first class objects? The behavior of mutable default arguments is clear if you know how Python treats function objects. Any "fix" would handicap the language.
- jcampbell1 12y agoCan you clarify? def foo(default_arg = []): Why can't that just be shorthand for: def foo(default_arg = ParamNone): if default_arg == ParamNone: default_arg = [] How would that break first class functions?
- bkeroack 12y agoHow would the above work for duck-typed lists, for example? How would the language "know" which types are mutable?
- dalke 12y agoAs a minor point, use "default_arg is ParamNone", since "==" probably won't do the right thing. What breaks is something like: def foo(default_arg = slow_f()): pass Under the shorthand gets turned into: ParamNone = object() def foo(default_arg = ParamNone): if default_arg is ParamNone: default_arg = slow_f() pass This is fine, since everyone would know that the shorthand means to not put slow code there. Instead, people will start writing it as: _foo_arg = slow_f() def foo(default_arg = _foo_arg): pass Of course, then what happens with: _foo_arg = slow_f() def foo(default_arg = _foo_arg): _foo_arg = 5 ? Under expansion it becomes: _foo_arg = slow_f() def foo(default_arg = ParamNone): if default_arg is ParamNone: default_arg = _foo_arg _foo_arg.add(5) This violates Python's scoping rules, because _foo_arg is now being used in local scope instead of global scope. Eg: >>> def f(x=None): ... if x is None: ... x = spam ... spam = 3 ... >>> spam = 9 >>> >>> f() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in f UnboundLocalError: local variable 'spam' referenced before assignment Which means you now need a new scoping rule, just to handle default parameters without making things more confusing. It also turns what was a simple O(1) offset into a precomputed list into a globals() lookup for many cases.
- peter-row 12y agoAlso, not knowing what scope a variable has when creating closures.
- SEJeff 12y agoCouldn't agree more! One of my all time new python interview questions gets a surprisingly large number of developers. Given a function like: def append_one(l=[]): l.append(1) return l What does this return each time? >>> append_one() >>> append_one() >>> append_one()
- ryangittins 12y agoThe l (lowercase L) and the 1 (one) look really similar. Could that be the cause of some confusion? Of course, the function name helps, but most developers have learned not to trust function names to be an accurate description of what the function does, especially in tricky interview questions. Still, I'd change this to something like: def append_five(l=[]): l.append(5) return l It tests the same thing (knowledge of how default parameters work), but without the confounding problem of similar-looking characters. Of course, syntax highlighting would help the applicant out. All of that being said, I still don't doubt that many developers don't know what they should about default parameters.
- trevor-e 12y agoI'm not a Python expert, but iirc from various blog posts the "l" variable does not get reset between function calls which will cause undesired behavior. So calling the function 3 times without argument would produce a list of size 1,2, and 3 with the third call rather than 3 lists of size 1. Can any Python guru's confirm?
- ryangittins 12y agoYes, that's correct. The default value is only interpreted once, when the `def` statement is called. After that point, it's completely mutable. You have to see Python functions as objects and default parameter values as object variables.
- Dylan16807 12y agoThe problem is not the existence of the object variables, but that they are in such an unfortunate place. The rest of the parameter list is declaring fresh local variables. It's inconsistent that the left side of the equals sign is per-invocation, and the right side is per-def.
- geertj 12y agoAgreed on all counts. However I do find myself using mutables as default arguments sometimes because the generated documentation is clearer. For example, this is a real method in one of my projects: def listen(self, address, ssl=False, ssl_args={}): pass I like the way this turns up in the docs because it's immediately clear that ssl_args needs to be a dict. Otherwise I have to describe it in words.
- tribaal 12y agoWell, that's throwing people implementing subclasses under the bus, IMO. Why not just add @param annotations in your docstrings instead?
- geertj 12y ago> Well, that's throwing people implementing subclasses under the bus, IMO. If they need to touch this argument in an overridden method and they don't know what they are doing, then yes. > Why not just add @param annotations in your docstrings instead? I'm using Sphinx and it renders them separately. I want the empty dict to show up in the function signature.
- Peaker 12y agoA single cryptic bug due to this practice will more than negate the minor doc readability benefits you get from that. And there will likely be many more than one cryptic bug. There are other ways to emphasize it ought to be a dict/mappable. Change its name to be suffixed as "_dict", for example?
- jamiis 12y agoYou should not ever use mutable default args. The documentation benefit is drastically outweighed by the potential for nasty bugs. http://pythonconquerstheuniverse.wordpress.com/2012/02/15/mutable-default-arguments/ http://pythonconquerstheuniverse.wordpress.com/2012/02/15/mu...
- kunstmord 12y ago
- doctoboggan 12y agoI agree that bare excepts are bad, however they do not catch Ctrl-C. If you _do_ want to catch Ctrl-C you have to except a KeyboardInterrupt explicitly.
- njharman 12y agoThis is untrue (just tested) for at least Python 2.7.x. under Linux, try ... except catches Ctrl-C.
- LyndsySimon 12y agoWildcard imports are OK, if and only if the module is specifically designed with this usage in mind. Example: nose.tools
- msvalkon 12y agoCheck out Raymond Hettingers Transforming Code into Beautiful, Idiomatic Python talk on youtube [1]. Great talk on avoiding some of the common pitfalls new python developers step in. Exposes some nice language features. [1]: https://www.youtube.com/watch?v=OSGv2VnC0go https://www.youtube.com/watch?v=OSGv2VnC0go
- gshubert17 12y agoAnd the slides are here: https://speakerdeck.com/pyconslides/transforming-code-into-beautiful-idiomatic-python-by-raymond-hettinger-1 https://speakerdeck.com/pyconslides/transforming-code-into-b...
- gr3yh47 12y ago# Avoid this lyrics_list = ['her', 'name', 'is', 'rio'] words = make_wordlist() # Pretend this returns many words that we want to test for word in words: if word in lyrics_list: # Linear time print word, "is in the lyrics" # Do this lyrics_list = ['her', 'name', 'is', 'rio'] lyrics_set = set(lyrics_list) # Linear time set construction words = make_wordlist() # Pretend this returns many words that we want to test for word in words: if word in lyrics_list: # Constant time print word, "is in the lyrics" the second example should read ... if word in lyrics_set: ...
- darkxanthos 12y agoJust to point out, if you really will have a tiny list and that's knowable, it's possible this example would be best with a straight linear time check. It could be fewer operations than hashing a string and looking it up. Practically pedantry though.
- constantine_ 12y agoThanks for pointing out this mistake. It's now fixed.
- cuu508 12y ago> write a list comprehension (...) code just looks a lot cleaner and what you're doing is clearer. I know how to use list comprehensions, but often avoid using them and use the standard for loops. List comprehensions look nice and clean for small examples, but they can easily get long and become mentally hard to parse. I would rather go for three 30 character lines instead of one 90 character line.
- meowface 12y agoI'm a bit torn on it. In a case where you need to do a lot of nested appends, I've found that even a long list comprehension can be easier to read. You just have to be sure to properly indent it and break it up into multiple lines. My rule is that every extra `for` starts a new line, and sometimes moving the predicate to its own line when it's too long, too.
- cuu508 12y agoFor a concrete example, I was just recently converting a list-of-dicts into a dict-of-dicts. Here's an isolated snippet: http://pastebin.com/8q46bK0v http://pastebin.com/8q46bK0v To my eye, the list comprehension version is reasonable. But I like the imperative style better: it uses the most basic language features and at a glance you can tell what it does. My favourite is the dictionary comprehension version, it's the shortest but still conveys clearly what it's doing.
- meowface 12y agoYeah, I prefer the dict comprehension. I use dict comprehensions quite frequently in my own code as well.
- bkeroack 12y agoDepends on how you want to program: imperative vs functional. Personally I think list comprehensions are the most beautiful part of Python, though sometimes I use map() when I'm trying to be explicitly functional (I realize it's allegedly slower, etc). Generally I think list comprehensions are cleaner and allow you to write purer functions with fewer mutable variables. I disagree that deeply nested for loops are necessarily more readable.
- metaphorm 12y agoan anti-pattern is a design pattern gone bad. these aren't anti-patterns. they're just noobie mistakes and non-idiomatic expressions.
- ascotan 12y agois it an anti-pattern to call something an anti-pattern because the author isn't sure what an anti-pattern is?
- greenspider 12y agoread this right after using a range in a loop in order to get the index. Can't believe I went this long without knowing about enumerate.
- maxerickson 12y agoIt first mentions enumerate as a footnote after looping with a range: https://docs.python.org/2/tutorial/controlflow.html#for-statements https://docs.python.org/2/tutorial/controlflow.html#for-stat... But the tutorial in the Python docs has pretty high information density and good coverage of things like this. (I think there is some risk that this comment will be interpreted as If you don't know enumerate you need to look at the tutorial. That isn't what I intend, I just want to point out that the tutorial is a reasonably dense resource that hits on a lot of stuff like enumerate.)
- parham 12y agoInstead of this: # Do this lyrics_set = set(lyrics_list) # Linear time set construction words = make_wordlist() for word in words: if word in lyrics_set: # Constant time print word, "is in the lyrics" You could do this: lyrics_set = set(lyrics_list) words = set(make_wordlist()) matched_words = list(lyrics_set & words) for word in matched_words: print word, "is in the lyrics"
- LyndsySimon 12y agoOf, off the top of my head: for word in (set(lyrics_list) & set(words)): print('{} is in the lyrics'.format(word))
- parham 12y agoEven shorter, nice. How about this one liner, the last bit is looking a bit messy any ideas? print " is in the lyrics \n".join([set(lyrics_list) & set(words)]), "is in the lyrics"
- MattConfluence 12y agoHow about >>> lyrics_list = ["her", "name", "is", "rio"] >>> words = ["is", "rio"] >>> print '\n'.join("{} is in the lyrics".format(word) for word in set(lyrics_list) & set(words)) rio is in the lyrics is is in the lyrics
- LyndsySimon 12y agoHeh, I just replied with pretty much this exact code to someone else trying to make it a one-liner. Yes, it's possible. I would never, ever publish code like this, though. It's opaque.
- LyndsySimon 12y agoJust because it can be written as a one-liner doesn't mean it should be written as a one-liner :) Don't ever do this. print('\n'.join(['{} is in the lyrics'.format(word)) for word in (set(lyrics_list) & set(words))])
- LyndsySimon 12y agoI can't think of a single case where using sentinel values is necessary or appropriate in Python. Generally speaking, one should just return from within the loop.
- agentultra 12y agoFunky OS of FFI APIs... but Python has a nice way of abstracting away the pattern using the second argument to iter().
- jessaustin 12y agoI get what you're saying, but even when sentinels are used inside a function, returning a -1 to the caller seems like a pretty bad API. It's OK to raise ValueError! I had thought the idiomatic sentinel value was an instance of object() you could is against, anyway.
- LyndsySimon 12y agoI agree - especially when you're returning a list index, since `[-1]` is a valid index in Python.
- omegote 12y agoPoint 3 of the iteration part is not good advice. With [1:] you're making a copy of the list just to iterate over it...
- moretti 12y agoI always struggle to understand why a list comprehension alist = [foo(word) for word in words] is considered more Pythonic than map alist = map(foo, words)
- XorNot 12y agoYou use list comprehensions in other places you wouldn't use map. The difference in text size as you posted is minimal, but the list comprehension - once you're used to reading them - tells you exactly what's going on. Where as map could be anything. It could be redefined for all you'd know.
- blossoms 12y agoalist = [foo(word) for word in words if word.startswith('a')] alist = map(foo, filter(lambda word: word.startswith('a'), words)) Which reads better?
- lolpep8 12y agoMap and filter, of course; less syntactical noise, simple function semantics, and plenty of precedent and equivalents in all other languages.
- bluecalm 12y agoIt's not like list comprehensions lack equivalent in other languages. Let's take Haskell for example. Python lists comprehension could use a where clause from Haskell though so one could really pack everything into a one-liner :) (as it is now, list comprehensions requiring various references to result of a function call evaluate the function each time it's used)
- TheLoneWolfling 12y agoThose are not equivalent. You need to wrap the map in a list() call.
- omaranto 12y ago
- yoo-interested 12y agoSpeaking of find\_item, is the `for..else` loop (which can be used to write find\_item in another way) considered Pythonic? I personally like `for..else` loops but I don't know where the consensus is at.
- PeterGriffin 12y agohttp://en.wikipedia.org/wiki/No_true_Scotsman http://en.wikipedia.org/wiki/No_true_Scotsman Don't ask what's "more Pythonic" or "less Pythonic", Python is not a cult, it's a very practical scripting language. Ask for benefits and weaknesses of a given approach in given circumstances.
- dlitz 12y agoSpeaking a language in the same way as other speakers of that language makes you easier to understand.
- PeterGriffin 12y agoWhen it comes to basic formatting, symbol naming and high-level code organization, sure. But computer languages, unlike human languages, are precise. Their intent is clear. And you'll never encounter a case where the Python 3 interpreter hasn't heard of that particular Python 3 keyword you're using. It's also not an excuse to avoid certain features of a language, when using them leads to a better and simpler solution, just because they're less popular. Programming is not an exercise in popularity. On the other hand, human language is fuzzy and full of phrases that consist of statements having nothing to do with their meaning. Such as me saying "your argument doesn't hold water". Human languages also have additional layers entirely separate from the primary meaning of a conversation, such as sending social cues like "how smart am I", "do I like you", "do I fit in this group", and "am I a leader or a follower". Each layer of concern drives a certain way of expression and imitation, none of which occurs (or should occur) when writing computer code. A better example to compare to programming code would be mathematical notation. As long as you express your intent shortly, using the available mathematical notation, people will be fine, and your intent will be clear. I've never seen someone ask in a math forum if their formula is more Mathematic one way, or another way.
- msl09 12y agoI find that the testing for empty is a bit misguided if you want to be rigorous with types. for instance: >>>def isempty(l): >>> return not bool(l) >>>isempty([]) True >>>isempty(None) True If embedded within your program logic this kind of pattern can waste precious time with debugging. You can catch your errors much more quickly if you are explicit with your comparisons.
- u124556 12y ago> Consider using xrange in this case. Is xrange still a thing? doesn't range use a generator instead of creating a list nowadays?
- omaranto 12y agoWell, I'd say no, not in "Python". It seems to me that when people say "Python" they still mostly mean Python 2, where range returns a list and xrange a generator. In Python 3 there is no xrange and range returns a generator, but I think people still most often call that language "Python 3", not "Python".
- minopret 12y agoYes, I'd revise as "Consider using Python 3 in this case." This is a chief reason why I now avoid Python 2. Python 3 is more than five years old. Where we have discretion to choose Python 3, it's time to exercise that discretion. Not because 3 is greater than 2. Because Python 2 has prominent pains that are healed in Python 3.
- deleted 12y ago[deleted]
- shadowmint 12y agoMmm... you should always use 'if x is not None:' imo. It's very common for libraries to make values evaluate to False, and very easy to get bugs if you just lazily test with 'if x'. Sqlalchemy springs to mind immediately as one of the common ones where using any() and if x: is a reeeeeallly bad idea; there are plenty of others. I'm pretty skpetical about modifying your coding behavior based on what libraries you happen to be currently using. 'If x' isn't your friend.
- tomp 12y agoEspecially taking into account the more bizarre bugs (features?) of python: (bool(datetime.time(0)), bool(datetime.time(1))) == (False, True) I always consider `if x:` a bug, unless x can only be a boolean. Furthermore, it seriously hinders readability and clarity of the code.
- CmdrKrool 12y agoAgreed. Especially if you program in more than one language, trying to remember the subtleties of each one's collection of rules for "truthiness" is a fraught exercise. And isn't it Python people who like to say, "explicit is better than implicit"?
- Zancarius 12y agoI was specifically looking through these comments for your example. I got bit by this once, and it's certainly... strange (it's also surprising). It's considered "behavior consistent with the rest of Python" [1] (which I can agree with) even if it makes little sense in terms of immediate readability to someone who hasn't previously encountered it. Fortunately, the workaround is easy, and it is documented. There's at least a couple of spats on the mailing list regarding this feature that are of interest to the curious or at least those who are interested in the history of such behavior. [1] http://bugs.python.org/issue13936 http://bugs.python.org/issue13936
- rmrfrmrf 12y ago> I always consider `if x:` a bug, unless x can only be a boolean. Oh, like this? ;) var eventA = new Date(), eventB = new Date(); if (!parseInt((eventA - eventB) / 1000)) { console.log("these events occurred simultaneously"); } else { // troll harder with confusing use of 'asynchronous' console.log("these events occurred asynchronously"); }
- sophacles 12y agoThe only thing I disagree with is "use nested comprehensions" thing. In my mind: x = [letter for word in words for letter in word] is inside-out or backwards or backwards. I want the nested for being the less specific case: x = [letter for letter in word for word in words] makes more sense in my mind. (It's also my first answer to the "what're some warts in the your language of choice).
- jules 12y agoEven clearer: x = [for word in words: for letter in word: letter] This also has the advantage of being readable left to right without encountering any unbound identifiers like all other constructs in Python.
- hueving 12y agoAs someone who spends a significant amount of time in python that is much harder to follow. There is an expectation for order in list comprehensions and what you wrote completely violates it.
- njharman 12y agoI'm in the camp that if your list comp needs more than one for clause, it's complicated enough to be broken out into actual for loop.
- wodenokoto 12y agoI use python for datamining, and most of my work is done exploring data in iPython. > First, don't set any values in the outer scope that > aren't IN_ALL_CAPS. Things like parsing arguments are > best delegated to a function named main, so that any > internal variables in that function do not live in the > outer scope. How do I inspect variables in my main function after I get unexpected results? I always have my main logic live in the outer scope because I often inspect variables "after the fact" in iPython. How should I be doing this?
- hyperion2010 12y agoIf you are using the interpreter directly then that particular bit of advice is hard to follow since you basically live in global all the time. For that reason I would say that this advice applies mainly to .py files.
- LyndsySimon 12y agoAgreed. There's a big difference between "scripting" and "writing software" in terms of best practices. If you're writing some ETL scripts in an IPython notebook, it would be overkill to encapsulate everything to keep your global scope clean.
- msellout 12y agoI'd try to test smaller chunks of code for validity. If any block of code is longer than 12 lines, I get nervous that I don't understand what it's doing. Refactor your code into functions as you confirm the code behaves as expected in the interpreter. It's very difficult to write automated tests when all logic is in outer scope rather than chunked into functions.
- yeukhon 12y ago> PEP 8 is the universal style guide for Python code. > If you aren't following it, you should have good reasons beyond "I just don't like the way that looks." Core dev and Guido have said many times PEP 8 are not holy. See https://mail.python.org/pipermail/python-dev/2010-November/105681.html https://mail.python.org/pipermail/python-dev/2010-November/1... In essence, a "stupid reason" like "I don't like it" is a valid reason not to adopt PEP 8. In fact, I don't like the PEP 8 recommendation on docstring. I like Google's docstring (aka napoleon in Sphinx contrib-module). http://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html http://sphinxcontrib-napoleon.readthedocs.org/en/latest/exam...
- me_myself_and_I 12y agoI don't think there us such thing as a pattern per language, unless the language is really unique. IMO what does exists is Language Bad Practices, which are actually tied to the language itself. An (anti)pattern is something abstract and can be applied to any other similar language.
- mimighost 12y agoI would say that the leaky out scope problem combined with those really similar typos is perhaps most diffcult to spot out.
- deleted 12y ago[deleted]
- noobermin 12y agoQuestion, how do you over multiple long lists (in python 2) especially if zip itself takes a long time to zip them, for example.
- takeda 12y agoYou use Python 3. J/K, while this is technically a limitation of Python 2, there actually is izip in itertools package which is a generator and works in similar way to zip in python 3.
- noobermin 12y agoAwesome! I knew there was some lazy zip-ish thing for python 2. I personally hate using range(len(foo)) as anyone else.
- jimmaswell 12y agoYou just use a for i in range(len(... and ignore this web article's opinion on it since the situation calls for it
- ggchappell 12y agoThis is a nice little article, but I wonder about some of the design decisions. In particular: > The simplifications employed (for example, ignoring generators and the power of itertools when talking about iteration) reflect its intended audience. Are generators really that hard? (Not a rhetorical question!) The article mentions problems resulting from the creation of a temporary list based on a large initial list. So, why not just replace a list comprehension "[ ... ]" with a generator expression "( ... )"? Result: minimal storage requirements, and no computation of values later than those that are actually used. And then there is itertools. This package might seem a bit unintuitive to those who have only programmed in "C". But I think the solution to that is to give examples of how itertools can be used to create simple, readable, efficient code.
- elandybarr 12y agoFailing to use join is a big one. I have seen countless instances of people writing the logic to output commas in between items (like for CSV export) that they want to concatenate into a string. header_line = ','.join( header for header in headers ) csv_line = ','.join( str(dataset[key]) for key in dataset.keys() ) Example for a case of a dictionary mapping a string to a bunch of numbers.
- cpenner461 12y agoAny reason not to do the first one more compactly? ','.join(headers)
- elandybarr 12y agoOops. Good catch!
- marcinw 12y agoProper Python would use the csv module for this operation, as your CSV export would break if `header` or `dataset[key]` contains a comma.
- elandybarr 12y agoYeah. This was a special case for a one line CSV that was requested by my client. It was a dictionary with a bunch of single measurements.
- deleted 12y ago[deleted]
- rcfox 12y agoYou should just use dataset.values() (or itervalues if you're using Python 2) instead of iterating over the keys, and then looking them up.
- andreasvc 12y agoYou're relying on dataset.keys() being in the same order as headers. Really, you should use the csv module, even for one-line CSV files.
- me_and 12y ago> Things like parsing arguments are best delegated to a function named main, so that any internal variables in that function do not live in the outer scope. Why a function named `main`? We're not writing C here, there's no need for a function named `main`. Let's call it something that's actually useful, like `parse_cmd_arguments`
- natmaster 12y agoislice should be used, not xrange
- jimmaswell 12y agoSet membership checking is constant-time? Somehow I don't believe that.
- TorKlingberg 12y agoIt's a hash table, so constant time lookup in the average case. It could be argued that it's actually O(log(n)), but it would not matter for any practical n.
- kstrauser 12y agoSets are basically hash tables that store only keys and not values. You can easily write your own Set implementation by subclassing `dict` and stripping out references to `.values()`, etc. As such, testing for membership is an O(1) hash table lookup. If you're skeptical: $ python -m timeit -s 'nums=range(1000000)' '100000 in nums' 1000 loops, best of 3: 1.4 msec per loop $ python -m timeit -s 'nums=range(1000000)' '500000 in nums' 100 loops, best of 3: 7.15 msec per loop $ python -m timeit -s 'nums=range(1000000)' '900000 in nums' 100 loops, best of 3: 13 msec per loop $ python -m timeit -s 'nums=set(range(1000000))' '100000 in nums' 10000000 loops, best of 3: 0.0572 usec per loop $ python -m timeit -s 'nums=set(range(1000000))' '500000 in nums' 10000000 loops, best of 3: 0.057 usec per loop $ python -m timeit -s 'nums=set(range(1000000))' '900000 in nums' 10000000 loops, best of 3: 0.0584 usec per loop