
Python Best Practice Patterns - abeinstein
http://stevenloria.com/python-best-practice-patterns-by-vladimir-keleshev-notes/
======
bru
Several of those patterns are incomplete or frowned upon:

* if a method does not use the object's state (no `self` usage) make it a `class-` or `staticmethod`.

* Some magic methods are presented. There's more to them[0].

* one should not write `class MyClass:` but `class MyClass(object):` (new style class[1])

* the last one (`return None`) make me very dubious

* Cascading methods: that's a big _no_. The idiom is that if a method may change the state of the object then it should return None (eg `set.add`)

0: well-written and comprehensive guide:
[http://www.rafekettler.com/magicmethods.html](http://www.rafekettler.com/magicmethods.html)

1: [http://www.python.org/doc/newstyle/](http://www.python.org/doc/newstyle/)

~~~
euphemize
> * the last one (`return None`) make me very dubious

I'm curious, as someone who uses this pattern quite a bit, how would you
improve on this?

~~~
jessaustin
Just speculating here, but a func that just ends without returning,
effectively returns None, so that idiom may be considered redundant. IMHO,
it's often worth the additional clarity to be redundant in this way.

~~~
StavrosK

        In [1]: def foo(): pass
        
        In [2]: foo() is None
        Out[2]: True

~~~
jessaustin
I was speculating about what the original complainer meant, not about what
python does... b^)

------
SoftwareMaven
Having gone through a couple thousand lines of Javascript that adhered to the
"keep methods small", I call bollocks on that. Make methods as big as they
need to be.

    
    
        function doFooOnList(l) {
            for (var i=0; i<l.length; i++) {
                doFoo(l[i]);
            }
        }
        function doFoo(i) {
            i.foo();
        }
    

gets old, very quickly.

After designing and building code for 20+ years, I can comfortably say that
there are no arbitrary rules of software design, and some of the worst code
I've seen has been a result of following "best practices" instead of thinking
for oneself.

Write code like it is meant to be read, because that's what happens most
often.

~~~
gnaritas
> Make methods as big as they need to be.

Correct, and if you do that, methods will invariably end up small. What you
cite above is small no matter how you write it, so that's not the point of the
advice to keep methods small. Methods pretty much never _need_ to be long;
they're long because they're poorly written code. Well written code tends
towards small methods.

~~~
Silhouette
_Correct, and if you do that, methods will invariably end up small._

If you'd said "usually", I'd have agreed with you, but "invariably" is far too
strong. Sometimes the logic you need to implement is fundamentally
complicated, and so the code you write to implement it must inevitably be at
least that complicated. If that means writing a 100 line function, but the
function really is doing one job, at one level of abstraction, in a cohesive
way, then so be it.

~~~
gnaritas
We agree, I'm fine with usually.

------
IgorPartola
Agree with all of these but two. The first is the example of doing:

    
    
        class Foo(object):
            highlight = reverse
    

No, that is not clearer. Now I have no idea what this method does. Making it
explicit requires more keystrokes, but allows you to properly document the
method. Also, when I run help(Foo.highlight) I won't get the generic
documentation for `reverse`.

Second, using `each` for a generic iteration variable. This is an opinion, not
a best practice. I would argue that either the loop is a one liner, at which
point use whatever you want (x works well), or it's more than one line and
then I want a proper name for the thing you are iterating over.

~~~
pak
`highlight = reverse` also flies in the face of TOOWTDI (from PEP 20).

Which is a shame, because this means that convenience methods that Ruby has,
e.g. ary.first → ary[0], ary.compact → ary.reject{|x| x.nil? }, ary.map →
ary.collect are pruned out of the stdlib and frowned on in contributed
libraries. This chilling effect that descends from PEP20 is one of the worse
aspects of Python.

They increase readability and should be encouraged. Even if ary.last is one
more character, it uses less of my brain to read than ary[-1]. ary.map might
be more readable if other code uses ary.reduce, while ary.collect is more
readable if other code uses ary.inject, ary.detect, etc.

The OP gave a perfect example with this---in an event handler for a drag
operation within an editor, I'd rather communicate that text is being
.highlight()-ed, even if the underlying view methods are reversing the pixels.
If I used .reverse(), it might confuse a coder into thinking the text itself
is being reversed when I drag.

Perhaps if more Pythonistas consider this a "best practice," it will swing
favor for amending the Zen. But I wouldn't bet on it.

Also, you're incorrect about help(). help(Foo.highlight) will provide the
docstring for Foo.reverse if Foo.highlight = Foo.reverse.

~~~
habitue
> Even if ary.last is one more character, it uses less of my brain to read
> than ary[-1]

This has nothing to do with whether it's a good idea. If you read a lot of
python code, the latter is easier to grok. And when you have separate ways of
doing things, it takes a longer and longer time to absorb those idioms and
internalize them to the point where you don't need to think. Adding a bunch of
"convenience" methods that do minor permutations on common operations might
read better when a line of code is given as an isolated example, because it
can read more like an english sentence. But when you're reading over code, the
resemblance to english only helps when you're looking at application code
which isn't a part of the language or standard library. Having the language
and standard library present a single way to do things makes it easier to get
to a base level of familiarity.

Endless variety in doing simple things doesn't buy you much.

That being said, the python stdlib is full of stuff built up over many years,
so it doesn't follow that idea everywhere. Also, higher level design is never
going to fit into the TOOWTDI concept because things at that level are more
subjective.

------
sloria
Author here. Must give credit where it's due:

These patterns come from a talk by Vladimir Keleshev, author of docopt and
excellent Pythonista. These are NOT my original work.

~~~
Walkman
Here is the talk:
[http://www.youtube.com/watch?v=GZNUfkVIHAY](http://www.youtube.com/watch?v=GZNUfkVIHAY)

------
atrk
Several of these are generally applicable to programming:

* Keep functions small and composable

* Keep functions at a consistent level of abstraction

* Use constructors to ensure objects always exist in a complete, usable state

* Use meaningful method names in place of comments

It is nice to see that other people struggle with functions with lots of
parameters + lots of partial state variables. I don't suppose anyone here has
a better solution?

~~~
collyw
Comments should explain why you are doing something while method names are a
guide to what they are doing. I see them as for different purposes and one
should not replace the other.

~~~
mercurial
Absolutely, unless you're writing end-user documentation.

------
unoti
I never knew you could use __enter__ and __exit__ to code your own things that
work with the 'with' statement. Well worth the read!

~~~
ajtulloch
For simple context managers, an easier method is to use
contextlib.contextmanager
([http://docs.python.org/library/contextlib.html](http://docs.python.org/library/contextlib.html)).

    
    
      from contextlib import contextmanager
      
      @contextmanager
      def tag(name):
          print "<%s>" % name
          yield
          print "</%s>" % name
      
      with tag("h1"):
          print "foo"
      """
      <h1>
      foo
      </h1>
      """

------
abecedarius
For the 'method object' one, I use nested functions. Python is not Java or
Smalltalk.

The @classmethod example I'd write with an ordinary function also, outside the
class.

------
frodopwns
Not enough explanation as to what problems are being solved or why your
solutions are "best practices".

------
simon_weber
Many of these tips address the idea that good naming improves readability. I
couldn't agree more!

If you're looking for more on this topic, Brandon Rhodes gave an excellent
talk on this at PyCon US last year [0].

[0] [http://pyvideo.org/video/1676/the-naming-of-ducks-where-
dyna...](http://pyvideo.org/video/1676/the-naming-of-ducks-where-dynamic-
types-meet-sma).

------
CmonDev
And the most important pattern:

[http://stackoverflow.com/questions/1275646/python-3-and-
stat...](http://stackoverflow.com/questions/1275646/python-3-and-static-
typing)

~~~
sitkack
except no tools use it, _yet_.

I would absolutely love a version of shedskin that moved to Python3 syntax and
used optional typing.

~~~
tveita
PyCharm will read simple type annotations like

    
    
      def get_error_message(error_code: int) -> string:
          ...
    

and use them for autocompletion hints and type warnings.

------
codelucas
This is the guy who authored TextBlob!
[https://github.com/sloria/TextBlob](https://github.com/sloria/TextBlob)

------
lukasm
I'm skeptical about the last one return None

~~~
Walkman
I saw this code yesterday:

    
    
        def is_file_for(is_nagyker, type):
            if type == KIS_ES_NAGYKER:
                return True
            elif type == KISKER and not is_nagyker:
                return True
            elif type == NAGYKER and is_nagyker:
                return True
    

At the first glance, I thought it always return True. Would have been more
clear an explicit return False at the end!

~~~
edavis
May I suggest `any` here?

    
    
        def is_file_for(is_nagyker, type):
            return any([
                type == KIS_ES_NAGYKER,
                type == KISKER and not is_nagyker,
                type == NAGYKER and is_nagyker])
    

I know unsolicited code improvements from strangers isn't the coolest thing in
the world, but `any` (and `all`) can really improve clarity for stuff like
this. I know I use them quite a bit.

~~~
aidos
I saw that used in the article too - not something I'd thought to do myself.
Thanks for pointing it out, I find it really readable and I know there are
places I could use this.

------
Axsuul
Is there something like this for Ruby?

------
Jiliwang
Very learnsome, Thanks!

------
binarysolo
Commenting to save for later reading. :)

~~~
maxerickson
If you go to your user page:

[https://news.ycombinator.com/user?id=binarysolo](https://news.ycombinator.com/user?id=binarysolo)

One of the links is 'Saved stories'. That's every story you vote up.

------
Walkman
You should name a classmethod first variable "cls", not "class_".

