Live data from Hacker News

Anti-Patterns in Python Programming

lignos.org

221–230 of 242 posts

Re: Anti-Patterns in Python Programming

#221
post #207
post #184

Earlier quoted context omitted.

> Tuples were supposed to have a structure (at least that's what all the rest of the world thinks of them) No, a structure is, you know, a structure -- what C calls a struct. Python calls it a namedtuple. If some people call it just a tuple, well, that's a difference in terminology, but it doesn't mean Python is confused about the concepts, it's just using terminology you're not used to. Also, if we're going to be pe…

> [...] that's a difference in terminology, but it doesn't mean Python is confused about the concepts No, it menas exactly this. The term "tuple" and its use predates Python. Sorry, no banana. > [...] your blog post is wrong about lists. You say "position in the list doesn't matter", but that means ordering doesn't matter Oh, so what's the difference in meaning of element True on position 1 and element True on positi…

> The term "tuple" and its use predates Python.

References, please? And not mathematical references; programming references. C was using the keyword "struct" long before Python to refer to what you are calling a tuple.

> what's the difference in meaning of element True on position 1 and element True on position 20?

The fact that the index is 1 instead of 20. Both elements have the same type, and might well refer to the same property of some sequence of things; but the index being 1 instead of 20 means the element True is describing that property relative to the first item in some sequence, instead of the 20th item. That's why position in the list makes a difference: the ordering of the items, as well as the type of the items, carries information.

(Of course, in Python the list items don't even have to be of the same type; but most uses of Python lists in practice that I've seen do assume that all the elements are "the same kind of thing".)

Re: Anti-Patterns in Python Programming

#222
post #72
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…

> The key is object mutability. A list type is mutable and a tuple type is immutable. I don't think the question has much to do with mutability, it isn't surprising to me nor would I imagine most programmers that a list is mutable, that's very common. The surprising part of this question is that the default value of 'l' continues to exist outside the lexical scope of the function, the expected behavior is that the va…

In Python everything is an object, including a function. The default value isn't a global, it belongs to the function.

I wonder if people who weren't exposed to languages which work differently ala C++ would be as surprised?

Re: Anti-Patterns in Python Programming

#223
post #34
post #3

The 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!

Couldn'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()

I've seen this trotted out time and time again, and at least in this simplified form it's a red herring. If you're going to mutate the argument, it doesn't make sense to give it a default value. If you're going to return a modified form of the input you need to make a copy of it. Doing both is simply absurd.

Re: Anti-Patterns in Python Programming

#224

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

Re: Anti-Patterns in Python Programming

#225
post #223
post #34

Earlier quoted context omitted.

Couldn'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()

I've seen this trotted out time and time again, and at least in this simplified form it's a red herring. If you're going to mutate the argument, it doesn't make sense to give it a default value. If you're going to return a modified form of the input you need to make a copy of it. Doing both is simply absurd.

Disagree. Would say it's a decent violation of expectations for the same instance to be passed into every invocation. Of course, the counterargument is 'know your tools,' which I'm partial to, but the fact that this pops up is an indication it is counterintuitive.

Re: Anti-Patterns in Python Programming

#226
post #160

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.

Read my post that has the "correct answers" which show you how to do it. The key is setting the default to None and then doing something like: if val is None: val = [] or the more idiomatic python way: val = val or []

I believe the former is more idiomatic, but I don't have a reference.

You want to explicitly check against `None` so that you're not overwriting all falsey values of `val` - even though you should generally try to enforce argument types, your second example would cause unexpected behavior in some cases, particularly those that have non-falsey 'default' assignments

Re: Anti-Patterns in Python Programming

#227
post #221
post #207

Earlier quoted context omitted.

> [...] that's a difference in terminology, but it doesn't mean Python is confused about the concepts No, it menas exactly this. The term "tuple" and its use predates Python. Sorry, no banana. > [...] your blog post is wrong about lists. You say "position in the list doesn't matter", but that means ordering doesn't matter Oh, so what's the difference in meaning of element True on position 1 and element True on positi…

> The term "tuple" and its use predates Python. References, please? And not mathematical references; programming references. C was using the keyword "struct" long before Python to refer to what you are calling a tuple. > what's the difference in meaning of element True on position 1 and element True on position 20? The fact that the index is 1 instead of 20. Both elements have the same type, and might well refer to t…

> References, please? And not mathematical references; programming references.

ML has had tuples several decades before Python existed.

Re: Anti-Patterns in Python Programming

#228
post #220
post #69

Earlier quoted context omitted.

As 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, peo…

Why not just memoize the result of the slow function?

I think by your use of "just memoize" that I didn't explain myself well enough. I don't mean to focus on the slow aspect, nor even the function aspect. It could be

   def foo(default_arg = [0]*(256*256)):
     ...
and still get the same namespace issues.

Memoization is not always going to be an available solution. For example, it may be that slow_f() returns a stateless object, so can be reused, while slow_f(x) returns something stateful. You can think of my examples as either using default arguments as a single element memo, or using a module variable for the same. Both premised on the idea that the developer knows enough to make the right decision.

Re: Anti-Patterns in Python Programming

#230

Set membership checking is constant-time? Somehow I don't believe that.

Sets 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
Post reply on HN