Python Best Practice Patterns
stevenloria.com
stevenloria.com
* 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
Severely, since what you're doing is mutating the actual state-data of the query object.
For example, when looking through someone elses code, if it does not have deep levels of nesting I prefer longer methods you can read like a script rather than having to jump around the place to see what is each method.
Likewise with the 80 character guideline in PEP. It can take a lot longer to find the closing brace if it is hidden in columns that look like a newspaper article.
Definitely. The PEP8 was actually revised 6 months ago[0]. Nothing crazy, but a bit more pragmatic.
>For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the nominal line length from 80 to 100 characters (effectively increasing the maximum length to 99 characters)
0: text: http://legacy.python.org/dev/peps/pep-0008/#maximum-line-len...
Compare:
def makeMove(self, target):
enemy_piece = target.piece
if enemy_piece:
self.takeEnemyPiece(enemy_piece)
target.piece = self
self.position = target
And def changeCoords(self, foo):
if foo.standing_on_it:
self.nowItsMine(foo.standing_on_it)
foo.standing_on_it = self
self.coords = foo
In the former case, you mostly don't need to consult the source of functions called. In the latter, you're never sure.I'm curious, as someone who uses this pattern quite a bit, how would you improve on this?
In [1]: def foo(): pass
In [2]: foo() is None
Out[2]: True> if a method does not use the object's state (no `self` usage) make it a `class-` or `staticmethod`.
Uh, that's a little too "cookbook" for my tastes. The decision of whether to make something a class / static method or not should be decided based on what the function is doing, rather than some pointless dogma. I'm assuming he means this method:
> def alarm(self): > with open(BUZZER_MP3_FILE) as f: > play_sound(f.read())
It's a design decision, and one that is particularly trivial at that. Let's say the effects an alarm ultimately change, resulting in an internal state read / write on the object. Now you have to go back and update the class despite the fact that it at a higher philosophical level one could argue that an alarm should only be triggered on an individual boiler.
Point being: it's nit-pickery for nit-pickery's sake. I don't personally use `return None`, but it's certainly more explicit and is fine to use if your judgment has found it to make sense.
At this point I think calling something idiomatic or Pythonic is a thought terminating statement. I need to see refutation.
In terms of refutation, some things are just conventions, and breaking them has to be done with the understanding that the benefits outweigh the costs. If python were a clean slate, totally new language, you might have a discussion about the merits of mutating chaining, but as it is, the convention is not to do that, and the benefits don't buy enough to violate everyone's expectations.
for each in enumerate(items):
if each.match(self):
# This fails because enumerate yields an (index, value) tuple.
# Match isn't a function defined on tuples.Is this really true?
What I really disliked is storing intermediate results of a multi-stage computation in instance variables. It is so easy to return multiple values from Python functions. Also, thinking what you need and need not pass and return between interconnected functions helps structure them much better, into smaller, clearer pieces (as #1 correctly advises).
simply `return` would be enough
So methods that mutate state, and don't return None are un-Pythonic?
What about this example:
a = [1, 2, 3]
b = a.pop()
pop() mutates the state of a and returns a value that is not None. Is the Python code language itself un-Pythonic? 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.
Same applies to Python, BTW; get used to list comprehensions and generators and you cannot look back.
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.
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.
Very very true. This phrase suggests there is no better way to do something, and that it should always be used because it is "best", and the dogmatic application of rules replaces actual thought and understanding. I am not against a "recommendation" or "suggestion", but to call it "best practice" is cargo-cult-like.
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.
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.
> Also, you're incorrect about help(). help(Foo.highlight) will provide the docstring for Foo.reverse if Foo.highlight = Foo.reverse.
That's what I was trying to say. When doing help(Foo.highlight) I want it to say something like "Event handler for highlighting text", not "Reverse pixel color for the given rectangle."
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.
These patterns come from a talk by Vladimir Keleshev, author of docopt and excellent Pythonista. These are NOT my original work.
* 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?
from contextlib import contextmanager
@contextmanager
def tag(name):
print "<%s>" % name
yield
print "</%s>" % name
with tag("h1"):
print "foo"
"""
<h1>
foo
</h1>
""" @contextlib.contextmanager
def manager(*args):
object = initialize(args)
try:
yield object
finally:
# cleanup
object.close()The @classmethod example I'd write with an ordinary function also, outside the class.
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://stackoverflow.com/questions/1275646/python-3-and-stat...
I would absolutely love a version of shedskin that moved to Python3 syntax and used optional typing.
def get_error_message(error_code: int) -> string:
...
and use them for autocompletion hints and type warnings. 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! def is_file_for(is_nagyker, type):
return type == KIS_ES_NAGYKER or \
(type == KISKER and not is_nagyker) or \
(type == NAGYKER and is_nagyker) def is_file_for(is_nagyker, type):
return ((type == KIS_ES_NAGYKER) or
(type == KISKER and not is_nagyker) or
(type == NAGYKER and is_nagyker))
This way, it doesn't break if there is extra whitespace at the end of the line. def is_file_for(is_nagyker, type):
return ((type == KIS_ES_NAGYKER)
or (type == KISKER and not is_nagyker)
or (type == NAGYKER and is_nagyker)) return (
(type == KIS_ES_NAGYKER ) or
(type == KISKER and not is_nagyker) or
(type == NAGYKER and is_nagyker)) def is_file_for(is_nagyker, type):
return ( (type == KIS_ES_NAGYKER)
or (type == KISKER and not is_nagyker)
or (type == NAGYKER and is_nagyker)) def is_file_for(is_nagyker, type):
return (
(type == KIS_ES_NAGYKER)
or (type == KISKER and not is_nagyker)
or (type == NAGYKER and is_nagyker)
)The other reply to you seems to be the best of both worlds.
From what I can tell this is kind of a point of contention among many developers; there are lots of debates when Googling that particular line.
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. any(k in obj for k in other)https://news.ycombinator.com/user?id=binarysolo
One of the links is 'Saved stories'. That's every story you vote up.