Live data from Hacker News

Anti-Patterns in Python Programming

lignos.org

191–200 of 242 posts

Re: Anti-Patterns in Python Programming

#191

Failing 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.

Proper Python would use the csv module for this operation, as your CSV export would break if `header` or `dataset[key]` contains a comma.

Yeah. 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.

Re: Anti-Patterns in Python Programming

#192
post #94

Earlier quoted context omitted.

It has something to do with mutability, because if an object is immutable, the behavior of Python matches what the naive developer expects. It's only mutable objects that break those expectations. Don't even get into unexpected behavior in classes: In [1]: class A(object): ...: l = [] ...: In [2]: a, b = A(), A() In [3]: a.l.append("Something") In [4]: a.l Out[4]: ['Something'] In [5]: b.l Out[5]: ['Something'] In [6…

> if an object is immutable, the behavior of Python matches what the naive developer expects If the object was immutable then append wouldn't work. That's hardly matching expectations.

Why? You would just get a new list back.

Re: Anti-Patterns in Python Programming

#193

Failing 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.

You should just use dataset.values() (or itervalues if you're using Python 2) instead of iterating over the keys, and then looking them up.

Re: Anti-Patterns in Python Programming

#194

Mmm... 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 happ…

It depends. If you're checking to see if that value is None, then yes - you should check that. If you're merely checking if the value is truthy, then using "if x:" is completely legitimate.

I've found "if x" to be much less readable, especially when I'm looking at code written in a language I'm not familiar with. When I'm reading such code, I want to be 100% what something is doing, and not have to read documentation when avoidable.

Re: Anti-Patterns in Python Programming

#195
post #172

Earlier quoted context omitted.

> The default value gets created when the function is interpreted ("compiled"). No. The default value gets "created" (the expression is evaluated and stored) when the def statement is executed. Take the following example: In [1]: def foo(): ...: def append_five(l=[]): ...: l.append(5) ...: return l ...: return append_five ...: In [2]: a = foo() In [3]: b = foo() In [4]: a() Out[4]: [5] In [5]: b() Out[5]: [5] In [6]:…

I thought that was what he meant. Is there any sharp distinction between "interpreting" and "evaluating" in python that I am unaware of? I've always used the words more or less interchangeably. But now that I think about it that might be a little naive since I have no idea how it works under the hood

You could say that interpreting is first parsing and second executing/evaluating. The parser tokenizes and does a small amount of optimization such as ignoring unassigned values.

Re: Anti-Patterns in Python Programming

#196
post #56

Earlier quoted context omitted.

I'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?

The key is object mutability. A list type is mutable and a tuple type is immutable. If the candidate correctly deduces what will happen, I'll ask them to write a bug-free version, which looks like one of the below: def append_one(var=None): var = var or [] var.append(1) return var def append_one(var=None): if var is None: var = [] var.append(1) return var Mutability is a very subtle but very important concept to unde…

I'd argue that the key difference from other languages is (re)assignment rather than (im)mutability.

Re: Anti-Patterns in Python Programming

#197

Earlier quoted context omitted.

> if an object is immutable, the behavior of Python matches what the naive developer expects If the object was immutable then append wouldn't work. That's hardly matching expectations.

Why? You would just get a new list back.

I can see why you might think so, but remember the Zen of Python is to have only one obvious way to do something.

    >>> [1,2,3] + [4,5]
    [1, 2, 3, 4, 5]
Thus appending should do something different than addition.

    >>> x = [1,2,3]
    >>> x.append([4,5])
    >>> x
    [1, 2, 3, [4, 5]]

Re: Anti-Patterns in Python Programming

#198
post #56

Earlier quoted context omitted.

The key is object mutability. A list type is mutable and a tuple type is immutable. If the candidate correctly deduces what will happen, I'll ask them to write a bug-free version, which looks like one of the below: def append_one(var=None): var = var or [] var.append(1) return var def append_one(var=None): if var is None: var = [] var.append(1) return var Mutability is a very subtle but very important concept to unde…

Since append doesn't return a value, how about: def append_one(var=None): return (var or []) + [1] Would this take longer and/or use more storage for long lists as vars?

When you use + on two lists, a new list is created, and elements from both are copied into the new one. Whereas the append operation modifies the list, and simply adds a value. Keep in mind that a python "list" is really like a C++ vector, so while sometimes append operation sometimes allocates a new array, and copies all the values, in general is O(1). The add operation is O(n).

And besides all that, there is nothing wrong with doing an append on one line, and returning the variable on the next. It's clear and readable.

Re: Anti-Patterns in Python Programming

#199

Earlier quoted context omitted.

In most languages you can't usually: 1. Iterate over a tuple 2. Convert a list to a tuple 3. Construct a tuple of a length not known at compile-time Python allows these because " why not? " but it does break their "one and only one way to do it" rule and confuses beginners a hell of a lot. There are definitely borderline cases. For instance, should a Vector be a list or a tuple? A Vec3 type is obviously a tuple, but…

> Python allows these because "why not?" No, it allows them because the distinction that those restrictions are founded on is only useful in a statically-typed languages, and Python isn't statically typed. > For instance, should a Vector be a list or a tuple? A real vector/array should be its own data type (probably implemented in a C, or similar low-level, extension) that happens to implement the interface expected…

So why does Python distinguish between a list and a tuple at all?

Re: Anti-Patterns in Python Programming

#200

I 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 mai…

I'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.

Post reply on HN