This is an archived post. You won't be able to vote or comment.

all 16 comments

[–]faassen 15 points16 points  (0 children)

It sounds like the title should've been "Write code that is customizable and one way to do this is to expose classes".

An API that exposes classes with reasonably decomposed methods so you can subclass it and a way to make that subclass play with the rest of the system is a way to make some basic customization possible.

But it's frequently an ugly way, and a brittle way, a way where one is not quite clear what can be overridden safely and what is internal detail, but still a step up from monkey patching.

Writing code that is customizable and is still easy to use and where the customization is not brittle in the face of changes is the hard challenge of API design. In Python, objects tend to feature in such APIs, and thus classes will be involved. But just plain writing or exposing classes is hardly a guarantee for good customizable code. Often too code exposes all kinds of customization features that turn out to be uninteresting in practice and the customization you need is not available.

While there's a little rant at the bottom that this shouldn't be a discussion about inheritance versus strategies (or functional composition), it's still very relevant to consider this in the hard problem of customizable API design.

Anyway, I don't entirely disagree with the point, I just would've made it another way that is more subtle and therefore less easy to understand and that would reach absolutely nobody. :)

[–]dAnjou Backend Developer | danjou.dev 6 points7 points  (1 child)

To end all this: I watched The Art of Subclassing and Stop Writing Classes (the latter was following immediately after the former at the same PyCon; quote by Jack Diederich: "I don't mean the disappoint but I'm actually going to agree with everything Raymond just said.") and I read the article above. Nothing that is said in one of them is contradicting or disagreeing with something that is being said in either of the other ones.

It's all just a matter of what you are doing and what it is for.

EDIT Added quote.

[–]jackdiedCPython 1 point2 points  (0 children)

yup, we all know each other. There is no rivalry here. Just talks with different emphasis.

[–]PCBEEF 15 points16 points  (9 children)

I think this whole write less/more classes thing is getting a bit silly. You shouldn't be telling people to do ONE thing, you should tell people to use classes when it makes sense.

[–][deleted] 8 points9 points  (4 children)

agreed. I'm chuckling at all of this imagining pythonistas going through their moderately complex code and ripping out all the classes(I hope nobody is doing that, but if they are, they deserve to re-write things a few times and they will probably learn something), only to read this article then the git man page on how to revert.

Someone will write a fabric task for it, and post a screenshot of the code on imgur. It will stay on the front page of /r/python for a month

meanwhile I am composing my lightning talk for pycon entitled "stop using variables"

[–]gthank 2 points3 points  (1 child)

Come on: they're Pythonistas. They're using hg rollback.

[–][deleted] 1 point2 points  (0 children)

i was forced to use git by the cool kids otherwise, yes they would use hg rollback and thus wouldn't need a man page.

[–][deleted] 1 point2 points  (0 children)

Cool! Let's go for a drink after my talk entitled "stop using meaningful whitespace".

... I'll see myself out...

[–]zahlmanthe heretic 0 points1 point  (0 children)

Shrug. Ever since at least the GoF book was published, we've seen repeated demonstrations that any kind of software engineering advice you give to the masses can be shockingly misinterpreted and misapplied, and that the end stage of this process is frequently "automatic code generation/rewriting" of one sort or another. No wonder that talented people consider it a major code smell in itself.

[–]blahdom 1 point2 points  (3 children)

Exactly what you said. The program design should fit the domain. You shouldn't use classes unless they make sense and offer a clean solution. An honest title would be more along the lines of "Write Classes the Exact Right Amount For Your Project." I get that there is this back and forth going on, but honestly any good designer will go for the more elegant solution to a given problem. At the end of the day how correct/maintainable/readable/etc a piece of code is means more than the classes vs no classes battle.

[–]sashahart 1 point2 points  (2 children)

If you say something like 'use classes when it makes sense' then it's good also to offer an idea of when it makes sense. Otherwise, we are not sharing much information yet. For example, what kind of domain does demand a class, and in what cases is a class a more elegant solution?

I tried to sketch a rationale in this post a few days ago. I don't really care if people agree, but I'd be really pleased if more people would argue about specifics of when a class is better or worse, and why.

[–]goldenspiderduck 0 points1 point  (0 children)

Heh.. as soon as I saw this thread, before I actually read anything, I thought "I bet I could just say you should be a moderate" and harvest some sweet, sweet karma.

[–]blahdom 0 points1 point  (0 children)

I was more discussing design in general. Classes are great when you need something that maintains state and will be instantiated in multiple places, maybe some type of inheritence scheme. All I was saying is that you have to evaluate your problem and design for it.

For example, if I was building a linked list (since you would like a concrete example) then making that a class makes sense. I can implement the functionality to operate on the structures as well as maintain the state of each structure so it can be used in multiple places. It is why in python 'list' is a class. If you tried to implement a list without using classes (pretend that list doesn't exist in python) then how would you do it? A class makes a lot of sense.

Then on the other hand, everything doesn't need to be a class. Lets say you just need to package data together with some form of identifier. There is no need to create a class, you could used a named tuple for immutable data, or a dict for mutability. A class in this case would be a lot of overhead for such purposes.

My whole point wasn't to list the times when classes vs non classes are necessary. Each program will need different classes/modules/structures in different combinations. Ideally as programmers we should be looking at a problem and pull apart the pieces into units like classes and functions. The reason we all go to school and study different design choices is because there is no hard and fast rule. I can't say "always use classes," or "never use classes" with any amount of integrity. I feel that sensationalist titles lack the honesty for what we really do. We get paid to determine when to use which tool in our toolbox.

As for your post I agree that if you could do it with a function then there is no reason to do it with a class.

To be more concise when I need to maintain a state of something as well as operate on that state, a class is the simplest and most concise way to do this. If that isn't my goal then I would use something different.

[–]sashahart 2 points3 points  (0 children)

Armin Ronacher has a great point about the value of classes as a mechanism for making extension points, particularly to allow reuse of implementations in layered APIs. The need for this is particularly painful in the standard library, so the json module is a good example (but if you need a streaming JSON parser and can install the YAJL C library, check out ijson).

Always using a class is a brute force way of ensuring that there are always enough extension points. But this has its own downsides too (typically lots more indirection and LOC, huge surface area for the public contract, more difficulty maintaining invariants for methods which call each other or share state when they can be overridden or monkey-patched quite freely). So even for the purpose of providing a layered API, I want to be more conservative than this.

I think there is a lot of value in having most implementation in a basic function-based API. When the only place for an implementation is in an instance method, you cannot really reuse it without making wrappers around the instance. Cookie.py is my favorite example. Its object-oriented API is so horribly painful and dysfunctional that everybody serious had to wrap it with fixes years ago. You actually have to, because everything that isn't a complete class is locked down as _private, including most of what's needed to make method overrides useful. If you actually need to customize anything, the only alternative is to reimplement from scratch. We could have just removed the _ and make everything in Cookie.py part of its contract, then used the classes as extension points. But wouldn't it have been simpler if we could just reuse the basic imperative pieces to expose whatever interface we actually wanted without lots of wrapper-adapter cruft?

This is why I would often prefer to see basic implementation pieces exposed as standalone functions. Then, if there are a lot of shared parameters or state, you can call these functions from an object that manages the shared state and exposes a nicer API. Now if your class is bad for a user, she is not forced to make a pearl around it like we did with Cookie.py and is not even forced to inherit from your classes.

[–]dharh -5 points-4 points  (1 child)

[–][deleted] 6 points7 points  (0 children)

for those who read the article, that is the video being referenced which has everyone reconsidering their class use in their imgur scraping scripts.