8 ms·
Also known in python as "if your class has only two methods, one of which is init, it's a function" in the "stop writing classes" https://www.youtube.com/watch?
by makapuf 6y ago
Also known in python as "if your class has only two methods, one of which is init, it's a function" in the "stop writing classes" https://www.youtube.com/watch?v=o9pEzgHorH0 https://www.youtube.com/watch?v=o9pEzgHorH0
EDIT: typo, changed link
- TomMarius 6y agoOr it's a named stateful or immutable data container with validation? You don't always want to use primitive types. But I am not a Python dev.
- moonchild 6y agoPython doesn't have private members, so that doesn't help you as much.
- heavenlyblue 6y agoPython has private members through double underscore methods
- ben-schaaf 6y agoThey're only private in a sense that you're not supposed to access them, there's no enforcement by the language in any way beyond renaming them `_<class>__<attr>`. They're still accessible and they still show up in `dir`, `__dict__`, etc.
- travisjungroth 6y agoThat’s not their purpose and they’re not really private. Leading double underscore methods cause name mangling (the class name gets added to the method name) and they’re used for solving specific problems with inheritance. Don’t use them just to make something private. You’re screwing up your inheritance if you don’t realize what you’re doing. Leading single underscore is private by convention. Still should generally be avoided, but it’s the proper use.
- cessor 6y agoMembers in Python are never private. Variables that are supposed to be used internally only are marked with and underscore, but that just conveys intent and isn't enforced by the interpreter/runtime. But you can emulate private data like this: >>> def a(u, v): ... def b(): ... return u + v ... return b ... >>> b = a(1,2) >>> b() 3 Like this, there is no way to access u & v from b.
- moonchild 6y agoLooks like classes can't be closures, though: >>> def f(): ... x = 5 ... class Blub: ... def incx(self): ... x += 1 ... def getx(self): ... return x ... return Blub() ... >>> j = f() >>> j.incx() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in incx UnboundLocalError: local variable 'x' referenced before assignment
- cessor 6y agoWhen using it like this, the interpreter opens a new namespace where the variables are bound. The x is declared in the outer scope, so it can't find it. However, you can ask python to keep searching vor the name `x` in the closest outer scope, that is what the `nonlocal` statement is for: def f(): x = 5 class Blub: def incx(self): nonlocal x x += 1 def getx(self): return x return Blub() j = f() j.incx() print(j.getx()) This will print 6, as expected.
- sideshowb 6y agoOr anything that needs validation before you try to run the main part. e.g. the init() method may give you a shopping list of data you're going to need to pass to run() but which couldn't have been deduced until init() validated its parameters. Sure, you could handle this the functional way by passing a getData() function to run() so it can do so itself but I'm not sure that's any more readable.
- makapuf 6y agoThen build a function from validated parameters (or partially apply some parameters).
- deleted 6y ago[deleted]
- j88439h84 6y agoYes, this is the point of `dataclasses` and `attrs`.
- Sniffnoy 6y agoYour link goes by means of Google; here's a direct link: https://www.youtube.com/watch?v=o9pEzgHorH0 https://www.youtube.com/watch?v=o9pEzgHorH0
- twic 6y agoI have a few little classes that are clients for network services. They have a constructor which sets up an HTTP client or socket or something, and maybe prepares some metadata, and then a method to make a call to the service. I could write these clients as lambdas or nested functions which close over the stuff which init creates. But why? An object makes it much clearer that there is state.
- raverbashing 6y agoIf there is state then it makes sense for it to be a class But the parent comment applies
- brnt 6y agoUsing init/a constructor implies state, does it not?
- raverbashing 6y agodef __init__(self): pass Not necessarily. Or the constructor is just setting some values that are later used by the calculation but are not modified and used later
- MaxBarraclough 6y agoI think that depends on what we understand by 'state'. Do we count immutable data? Consider faking a 'bind' with something like: var quadrupler = new ConstantMultiplier(4); var output = quadrupler.applyTo(42); The 'quadrupler' instance is stateless in that it's immutable, but it presumably has a (constant) member to store the value 4.
- yxhuvud 6y agoYes. If nothing else, the calculation of that immutable data may be costly enough to motivate not calculating it multiple times. Hence you want to treat it as state. But that doesn't mean you want to expose it to the user of the class which may have a nice simple interface of cheap operations after the init is done.
- tarkin2 6y agoAnd if you have many methods, but only one 'public' method, use a closure.
- gindely 6y agoI think before you do that, you should be careful to ask why the methods are private. Frequently, code like that exists because the private methods are reusable code that is not related to the title of this class. So the solution is first to make the private methods into public methods on separate classes. And then the resulting classes probably meet some other rule that says "reduce trivial classes to pure functions or closures". Generally speaking, before a person writes a private function, they should remember that it means "if you misuse it you'll break my otherwise unprotected invariants." Whenever that's not true, you shouldn't write "private". In particular, it doesn't mean "I was too lazy to structure my code correctly so I tried to hide it from the public implementation".
- tarkin2 6y agoIf the code could be extracted into generic top-level pure functions, I generally don’t unless its used in more than once in the codebase. Otherwise I find a number of contextless generic functions/classes without any idea of their use exhausting to look at eventually. I have some functions that live in closures that are fairly generic. I’m always contemplating extracting them into a higher level. But they’re currently only used in one place, and they currently reside close to that one place, and this seems sensible.
- tarkin2 6y agoClasses with one public function can be converted into a closure. Classes with two or more public functions can be converted to a closure that return those functions through an object. Then classes and closures become closer. Classes, of course, offer inheritance. Yet most prefer composition over inheritance. Classes, too, offer polymorphism through typing. But dynamic languages use duck typing. In most dynamic languages where duck typing and composition are used, I find little need for traditional object orientated programming. I frankly prefer closures, composition and duck-typing over the classes, inheritance and polymorphism through typing. The only thing missing is type checking and Golang's interfaces offer a way through that without the overhead of traditional object orientated design.
- iib 6y agoDo python linters catch that? It is not a code error and sometimes not a design error either, but a warning would be useful, and linters may have the tools necessary for that.
- fastball 6y agoAt the same time, if your function has an expensive initialization step that only needs to be called once (and doesn't change between calls), then the pattern of using `__init__()` + `__call__()` can be very nice.
- im3w1l 6y agoI would consider this pattern: def x(): expensive precompute def y(): cheap stuff return y It's a matter of taste I guess.
- fastball 6y agoFor comparisons sake: class Thing: def __init__(self, arg): # expensive precompute self.result = ... def __call__(self, arg): # cheap stuff that uses self.result for something ... And then usage is: f = Foo("some val") bar = f("another val") So yeah, both definitely do the same job. And your pattern is probably faster. But I generally like using Python's dunder methods as I think they allow for more consistent/recognizable code patterns.
- m463 6y agowhat if it has just values (like an enum)?