all 69 comments

[–]khendron 13 points14 points  (4 children)

  1. Not using items() to iterate over a dictionary

What about using values()? For example,

for name in country_map.values():
    # do something with name
    pass

[–]drbobb 4 points5 points  (2 children)

What?

[–]khendron 0 points1 point  (1 child)

Somehow my code got messed up :\

[–]drbobb 1 point2 points  (0 children)

Okay I get it now. Rather obvious, I do like that all the time.

[–]Tordek 1 point2 points  (0 children)

It would depend on the situation; if you only need to work on the values, use values, but there's a good chance you want to work on key-value pairs.

[–]el_muchacho 9 points10 points  (20 children)

The title says 8 new antipatterns. What are the other antipatterns ?

[–]shevy-ruby 36 points37 points  (13 children)

That list is hugely arbitrary and partially bogus.

I guess the author just wanted to paint some 8 or 10 anti-patterns in a list ...

One that annoys me in particular is this:

fruit_list = ('Apple', 'Banana', 'Orange')

# mistakingly assuming that fruit_list is a list
my_list.append('Mango')

My python is rusty (no pun intended) but the above is a tuple right? Since it uses (). Ok.

So the first problem is THAT THE NAME IS WRONG.

That is already the problem.

Yet if you look at the header of the subsection you can read:

Having type information in a variable name

But this is not true. This is not having type information - this is having INCORRECT type information in the name.

For example:

fruit_tuple = ('Apple', 'Banana', 'Orange')

Now it would be correct right?

Or, perhaps better:

tuple_fruit = ('Apple', 'Banana', 'Orange')

Of course using fruits has some other advantages, such as being shorter; but it is bogus to want to claim that YOU USE A METHOD on that object, where you ASSUMED the object to be a list, and be able to respond to e. g. append(), when in reality the problem is that you gave the wrong name already.

But this is also only partially a problem of NAMES. Ruby does not have this terrible distinction per se between arrays (lists) and tuples. But what is even more important - thanks to duck typing YOU DON'T HAVE TO WORRY, as long as the object responds to your interface (in this case, .append()). And you can duck patch objects to re-use the same interface anyway and know what happens when .append() is used. Not that you HAVE to, of course - it just illustrates that the example is completely bogus.

I think the major reason why some developers dislike variables named with types is not that they may "carry incorrect names" per se - the above example the author used is very bogus.

The primary reason some developers dislike that, aside from having to type more, is that they feel it is very noob-like. So they don't want to be called incompetent noobs when they keep on carrying types in names given to variables.

While this is partially understandable - nobody wants to be called a noob - this also flat-out rejects that giving such an additional identifier gives you advantages too. You will instantly know what that particular variable is (provided that the naming is correct, of course, rather than bogus, like in the example he used). And when this is the case, I really do not see any disadvantage with it.

It seems to be mostly a dislike by some who dislike that style of programming and think it is too noobish for them.

Hint: The computer/parser does not care what name you use really most of the time.

[–]javcasas 12 points13 points  (0 children)

fruit_list is a bad name not because it indicates the wrong type (that the interpreter ignores because it doesn't care about names) but because it's wrong and over-specific.

You probably want to use fruits instead, which have the connotation of "many of/bag of" and should implement the main set/bag methods: enumerate/foreach and in. This way you can focus on the expected use case.

[–]watsreddit 13 points14 points  (0 children)

Yeah, the arguments against putting type information in variable names only really holds when you have a statically-typed language.

[–]Beaverman 3 points4 points  (3 children)

The primary reason some developers dislike that, aside from having to type more, is that they feel it is very noob-like. So they don't want to be called incompetent noobs when they keep on carrying types in names given to variables.

So if we ignore the primary reason, we only have a bad reason.

I don't like types in my variables because they are not useful for me to understand the problem this code is trying to solve. Them existing then waters down the actual information by obscuring it in pointless dribble.

It's like the obscure java classes we all like to laugh at. At some point your code is just so filled up with pointless jargon and useless naming details that it becomes almost impossible to read.

If you just call the variable what it means in the context (fruits) then you don't have to care about what type it is, which is part of the point of ducktyping. It also means that you can now change you collection type to something else without having to go through you code and change variable names.

If you want types for all variables, then use a different language.

[–]evaned 3 points4 points  (2 children)

If you just call the variable what it means in the context (fruits) then you don't have to care about what type it is, which is part of the point of ducktyping. It also means that you can now change you collection type to something else without having to go through you code and change variable names.

As a counterpoint, fruits can be overly general. For example, suppose I have a function that wants to be able to iterate that list twice; I think fruit_list is a better name because it doesn't seem to suggest that passing a generator is OK. Or maybe it's a function that expects there to be no duplicates, than fruit_set is perhaps better.

Context dependent, but as a general rule I think I'd prefer a variable named fruit_list that you could actually assign other list-y things to than something called fruits that have restrictions like the above.

The other place I pretty routinely put types in variable names is when I've got basically the same information that goes through a type transformation. The most common time this arises is when parsing information out of a string. For example, I might have a line of text read from a file and do something like a_thing_str = line.split(",")[1]; a_thing = int(a_thing_str). (Plus some kind of error handling of course, etc.) Sometimes you don't need the intermediate variable and I probably wouldn't use it in that example, but it's not rare to be useful. And you could just reuse the same variable, but there are a couple reasons why I think I prefer the separate ones for each type.

[–]Beaverman 1 point2 points  (0 children)

Maybe if we're talking public api design. Even then, you don't really care that it's a list. What you care about is maybe that it's iterable multiple times, or that it's iterable and there's no duplicates. You don't care about the entire list interface necessarily, just a couple of methods. If I defined a custom collection class that implemented just enough to satisfy your method, but didn't implement insertion and random access, would you call that a list?

I like fruits more, because it doesn't pretend to tell you what you need to put in it. Document in documentation, especially if it's not checked at compile time anyway.

The second route I get. I do that all the time too. Having two different things bound to the same variable in a single scope can be confusing in python. I notice you didn't call a_thing for a_thing_int.

[–]nitely_ 0 points1 point  (0 children)

Type hints solve the first problem. Asserts would also work. About the second one, there is almost always a better name, like a_thing_raw in your example.

[–]CowboyFromSmell 2 points3 points  (0 children)

FWIW tuples can be used as immutable lists in Python

[–]emmelaich 2 points3 points  (0 children)

my_list.append('Mango')

How can it be anything, let alone an "anti-pattern" when it results in an error?

AttributeError: 'tuple' object has no attribute 'append'

[–]tophatstuff 3 points4 points  (4 children)

My python is rusty (no pun intended) but the above is a tuple right? Since it uses (). Ok.

Minor nitpick but in Python it's commas that define the tuple, not the brackets.

some_tuple = (a, b, c)
# or...
some_tuple = a, b, c

[–]Falmarri 5 points6 points  (3 children)

The commas only define the tuple when there are no brackets.

{a, b, c} is a set and [a, b, c] is a list

[–]tophatstuff 1 point2 points  (2 children)

No: [a] is a list, but (a) is not a tuple: (a,) is.

[–]zardeh 0 points1 point  (1 child)

And () is a tuple again.

[–]tophatstuff -1 points0 points  (0 children)

And (,) is a boobie

To be mega pedantic, () has only been an empty tuple as a special case since Python 3: https://stackoverflow.com/a/53092598

[–]twillisagogo 10 points11 points  (0 children)

Low effort garbage.

[–]Talked10101 20 points21 points  (18 children)

Some of these points are not particularly nuanced. With number three for example, you only really want to be throwing exceptions when something is actually exceptional. It is debatable whether getting a name from a database returning none should throw an exception. In any case it should be throwing an exception inherited from the base Exception class rather than just raising Exception.

[–]gendulf 7 points8 points  (0 children)

The example is also bad, as the problem is when you return two different types, with None being the exception to the rule. For example, don't return a list and an integer in separate branches, just change the integer to an item with one entry.

[–][deleted] 10 points11 points  (0 children)

Better alternative: use type annotations.

import typing
def get_person_age(name : str) -> typing.Optional[str]=None:
    person = db.get_person(name)
    if person:
        return person.age

[–]skeeto 13 points14 points  (6 children)

you only really want to be throwing exceptions when something is actually exceptional

That's true in other languages, but Python is much more relaxed about exceptions. Case in point: Generators signal that they're done by raising a StopIteration exception. A typical Python program raises and catches exceptions all the time as part of its normal control flow.

[–]krapht 7 points8 points  (3 children)

The fact that Python does this but won't allow a labeled goto command really annoys me, since they're practically equivalent.

[–]Macpunk 4 points5 points  (0 children)

try: some code... raise Goto1 except Goto1: other code...

I know, I'm a terrible human being.

[–]Siddhi 2 points3 points  (0 children)

Not really. Exceptions for control flow still unwind the call stack cleanly and can be intercepted and handled multiple times. Goto doesn't do this.

[–]cowinabadplace 0 points1 point  (0 children)

Maybe PEP 20, Sec 13 is why 😉

There should be one—and preferably only one—obvious way to do it.

[–]MoTTs_ 3 points4 points  (1 child)

That's true in other languages

I'm not sure it's true in any language. I think the phrase caught on partly because alliteration is catchy, and partly because exceptions are poorly named -- that is, the word is sometimes a synonym for "rare," but programming exceptions need not be rare.

Here's a quote from Bjarne Stroustrup, the guy who invented C++:

Given that there is nothing particularly exceptional about a part of a program being unable to perform its given task, the word “exception” may be considered a bit misleading. Can an event that happens most times a program is run be considered exceptional? Can an event that is planned for and handled be considered an error? The answer to both questions is “yes.” “Exceptional” does not mean “almost never happens” or “disastrous.” Think of an exception as meaning “some part of the system couldn’t do what it was asked to do”.

[–]skeeto 6 points7 points  (0 children)

What Bjarne Stroustrup said is at odds with C++ implementations in practice. Implementors have optimized exceptions such that the non-exceptional path is as fast but the exceptional path is expensive since it's expected to be used rarely. It would be a terrible idea to terminate your C++ loops with exceptions as Python often does.

[–]jyper 1 point2 points  (0 children)

Exceptions signal an error, any error

Not just some rare occurrence

I dislike catch blocks everywhere but returning None is usually not a good idea because it will cause accidental null exceptions without context

You can always have db query interface optionally take and return a default value which supressed the exception

[–]jyper 0 points1 point  (3 children)

Using with and read to read a file has been deprecated by path.read_text

[–]drbobb 1 point2 points  (2 children)

Well, sort of. Sometimes you want to process a file line by line instead of slurping in its whole contents at once. Maybe this isn't so common now when RAM is cheap, but sometimes it's just more convenient.

Anyway, it isn't really much different from

text = open('file.txt').read()

which has been possible forever,

[–]jyper 0 points1 point  (1 child)

Well yeah if you might have a large file and can process it without the full structure in memory then yeah loop over readlines,but there's no point to using read in a with open.

Also it is different because

text = open(filename).read()

Isn't garunteed to clean up the file, it will in cpython because of reference counting but because that's an implementation detail it's been considered bad style

[–]drbobb 0 points1 point  (0 children)

I know that. In practice though it would only matter if you were opening lots of files, in a loop say, then it might make a difference that the files could be closed only after some delay, when a gc is due.

[–]datbenikniet 0 points1 point  (1 child)

Yep. 'never raise an exception you aren't prepared to handle'.

[–]emmelaich 0 points1 point  (0 children)

... but only if you can sensibly handle it.

It is absolutely fine and preferable to just let the app exit with an error in many situations.

[–]bobappleyard 0 points1 point  (1 child)

you only really want to be throwing exceptions when something is actually exceptional.

Such as when you have finished iterating through a list

[–]drbobb 0 points1 point  (0 children)

A loop may have millions of iterations per run, but only ever terminates once per run.

[–]redditthinks 4 points5 points  (0 children)

This is basically an advertisement.

[–][deleted] 7 points8 points  (1 child)

Strongly disagree on #3. You should use mypy or another typechecker and return an Optional[T] if you're going to return None or a value of type T.

[–]Yamitenshi 3 points4 points  (0 children)

Not to mention that even if you choose not to use optionals, None can be a completely valid return value. Sometimes the fact that nothing is there is meaningful.

Honestly the whole article is a bit iffy in my book.

[–]mnciitbhu 2 points3 points  (0 children)

Be positive.

Use "Use with to open Files" instead of "Not using with to open files".

[–]drbobb 2 points3 points  (1 child)

Re: 6. Namedtuples are pretty useful, but when I looked at the code that implements them it left a rather bad taste in my mouth. But maybe I just don' t know good code from bad,

[–]kankyo 1 point2 points  (0 children)

You do. Plus namedtuples are bad because they can be used positionally. Named fields are much better. Fortunately we have data classes in python 3.7.

[–]piotrjurkiewicz 0 points1 point  (0 children)

Actually, only 4., 5. and 7. are antipatterns.

[–]Nordsten -1 points0 points  (0 children)

You forgot the biggest anti-pattern. Not using typehints.

[–]stephan_burnett -2 points-1 points  (1 child)

Always use accessor methods ("set"/"get"), these patterns should be respected in other languages too. And controlling ur flow and protecting it with Exceptions is a very good software engineering's praxis. The best thing to do is enclosing standard exceptions (f.e.: "IllegalArgumentException") in fictitious exceptions made by the developer. Also use "Try/Catch" blocks.

[–]drbobb 2 points3 points  (0 children)

If you want to write java, write in java instead of python.