Live data from Hacker News

Python Best Practice Patterns

stevenloria.com

61–70 of 97 posts

Re: Python Best Practice Patterns

#61
post #36

Earlier quoted context omitted.

Chainable mutating methods are not idiomatic Python; returning None is idiomatic for mutating methods. ISTR that the reasons here regard readability and clearly distinguishing mutations from queries, but in any case if you don't follow the idiom in libraries you create, they won't behave like Python's builtins and standard library, which, whatever you think about chaining on its own, will cause some context switching…

The builtins are broken, that is why there are new builtins liked `sorted()`, list.sort() returning None is not an improvement. The standard libraries are a _mess_, there is absolutely know cohesion in their api design, or even naming conventions (go peruse the standard lib). Much of what is Pythonic is someones opinion on how a nanny-language should behave. At this point I think calling something idiomatic or Python…

sorted returns a new object, not mutates the original object, so it follows the convention GP is talking about. sort() also follows that rule, since it mutates, it returns None.

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.

Re: Python Best Practice Patterns

#62
post #46

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…

`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 s…

Personally, I do agree with having ary[0] as the "one true way". Doing ary.first will lead to ary.second, which will lead to ary.slippery_slope :). I mean some conveniences are good, while having too many is evil.

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

Re: Python Best Practice Patterns

#63

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 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 follow…

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

Re: Python Best Practice Patterns

#64

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…

The whole "use __iter__ whenever you can" thing is dubious too. It may be fine sometimes but the example is poorly chosen. If the department gains a name and a manager, using __iter__ makes much less sense. And now you also need to implement __len__ if you want to count your employees, etc.

And possibly allow indexing: `department[3]`. Yeah, I don't like that. It's not clear whether it's a generator or a full sequence. Generators when you don't expect them are evil (you cannot iterate over them twice). I would much rather see something like department.get_employees() and department.iget_employees() that return a tuple and a generator respectively.

Re: Python Best Practice Patterns

#65
post #46

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…

`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 s…

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

Re: Python Best Practice Patterns

#66
post #22
post #18

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…

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.

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

Re: Python Best Practice Patterns

#67

Earlier quoted context omitted.

The whole "use __iter__ whenever you can" thing is dubious too. It may be fine sometimes but the example is poorly chosen. If the department gains a name and a manager, using __iter__ makes much less sense. And now you also need to implement __len__ if you want to count your employees, etc.

And possibly allow indexing: `department[3]`. Yeah, I don't like that. It's not clear whether it's a generator or a full sequence. Generators when you don't expect them are evil (you cannot iterate over them twice). I would much rather see something like department.get_employees() and department.iget_employees() that return a tuple and a generator respectively.

That's the sort of things people used(?) to complain about C++: gratuitous operator overloading. It sometimes makes sense, but you really need to think long and hard about the semantics.

Re: Python Best Practice Patterns

#68

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 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 follow…

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

"Small" obviously depends on your language, but well-named methods which follow the "do one thing and do it well" principle always win over wall-of-text code. It's all a matter of decomposing the issue, finding the level of abstraction your method will work at, and naming things.

Re: Python Best Practice Patterns

#69

Earlier quoted context omitted.

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

"Small" obviously depends on your language, but well-named methods which follow the "do one thing and do it well" principle always win over wall-of-text code. It's all a matter of decomposing the issue, finding the level of abstraction your method will work at, and naming things.

We agree completely.
Post reply on HN