4 ms·
I wish there was a pattern to encapsulate data and functions from a module. So you could do: import greeter greet1 = encapsulate(greeter) greet2
by TekMol 4y ago
I wish there was a pattern to encapsulate data and functions from a module.
So you could do:
import greeter
greet1 = encapsulate(greeter)
greet2 = encapsulate(greeter)
greet1.name = 'Joe'
greet2.name = 'Sue'
greet1.greet()
greet2.greet()
And greeter.py would look like this:
name = 'nobody'
def greet():
print ('Hello '+name)
Output:
Hello Joe
Hello Sue
This would be so much nicer and leaner than classes.
- akx 4y agoYou could absolutely write `encapsulate`, but you will lose out on all of the established code completion and other IDE features.
- TekMol 4y agoHow would encapsulate look like? Would it have a negative performance impact? I don't care about code completion at all. When I type "a", I don't want anything to happen on my screen except for an "a" to appear. I think that all the flickering stuff that the average programmer experiences on their screen burns more mental energy than it saves.
- LittlePeter 4y agoI once had a working "encapsulate" but lost the code. That code was modified version of this: https://docs.python.org/3/library/importlib.html#approximating-importlib-import-module https://docs.python.org/3/library/importlib.html#approximati... My "encapsulate" was 4 lines of code at most IIRC. As someone mentioned, autocomplete in PyCharm did not work.
- _ZeD_ 4y agoI hope you are talking about code you use internally and don't share. FWIW I care about code completion, and using a library that broke my workflow is not fun.
- tekknolagi 4y agoYou could normally do this with copy.copy or copy.deepcopy but apparently you can't pickle a module object... A quick internet search does not immediately reveal anybody trying to do this before, and I am surprised.
- imurray 4y ago> apparently you can't pickle a module object Not with the standard library pickle, but "dill extends python’s pickle module for serializing and de-serializing python objects to the majority of the built-in python types". — https://pypi.org/project/dill/ https://pypi.org/project/dill/
- aston 4y agoI don't think you want this, but just in case you do :) def encapsulate(mod): import types out = types.SimpleNamespace() def replace_global_scope(f): # via https://stackoverflow.com/a/1144561, but tweaked for py3 return types.FunctionType( f.__code__, out.__dict__, f.__name__, f.__defaults__, f.__closure__, ) for name in dir(mod): val = getattr(mod, name) if callable(val): val = replace_global_scope(val) setattr(out, name, val) return out
- TekMol 4y agoThat gives me an error: https://www.online-python.com/sTLH5nYGvk https://www.online-python.com/sTLH5nYGvk
- abeyer 4y agoYou moved the for loop inside the inner function, rather than outside.
- Starlevel001 4y agoThis is essentially just a class but worse.
- CraigJPerry 4y agoHere's my attempt: ~/Code/local/hn ································ at 09:14:14 python3 hn_example.py Hello Joe Hello Sue ~/Code/local/hn ································ at 09:14:16 cat hn_example.py greeter.py ───────┬─────────────────────────────────────────────────────────── │ File: hn_example.py ───────┼─────────────────────────────────────────────────────────── 1 │ import sys 2 │ from importlib.util import find_spec, module_from_spec 3 │ 4 │ def encapsulate(mod): 5 │ s = find_spec(mod) 6 │ m = module_from_spec(s) 7 │ s.loader.exec_module(m) 8 │ return m 9 │ 10 │ greet1 = encapsulate("greeter") 11 │ greet2 = encapsulate("greeter") 12 │ 13 │ greet1.name = 'Joe' 14 │ greet2.name = 'Sue' 15 │ 16 │ greet1.greet() 17 │ greet2.greet() ───────┴─────────────────────────────────────────────────────────── ───────┬─────────────────────────────────────────────────────────── │ File: greeter.py ───────┼─────────────────────────────────────────────────────────── 1 │ name = 'nobody' 2 │ 3 │ def greet(): 4 │ print ('Hello '+name) ───────┴─────────────────────────────────────────────────────────── I differ from your syntax by passing string parameters on (your) line 3 & 4 (my line 10 & 11). I think this isn't a good idea though, i'd lookup __slots__ if leanness is an issue.
- TekMol 4y agoThat is super awesome! Do you think it negatively affects performance? Why don't you think it's a good idea? I don't see how __slots__ would be relevant to this? Isn't __slots__ a thing to define how classes store their properties?
- CraigJPerry 4y ago>> Do you think it negatively affects performance? Yes and no. Pragmatically, no, because in python usually performance comes from (ignoring using native code) avoiding dynamic dispatches (circa 500x speedups are common) and like in almost all languages, being aware of your allocations (varies wildly but avoiding allocs in loops for example will give remarkable speedups). In theory though, yes absolutely this is slower - what was a query of a dict (or even better, an array in the __slots__ case) - i.e. grabbing an attribute from a class - is now going outside the process and asking the OS about stuff on the filesystem. Masssively slower. >> Why don't you think it's a good idea? Because you’re paying an overhead to use something in an unexpected way when the language provides a faster, idiomatic solution to this problem: >> a pattern to encapsulate data and functions That’s the class and object system described in one sentence >> I don't see how __slots__ would be relevant to this? You’ve heard of turtles all the way down? In python it’s dicts all the way down. Behind the scenes you can think of a loaded module being a dict, just the same as an object has a dict underpinning it. Slots comes in as an optimisation on this dicts all the way down. An optimisation that’s not open to you if you (ab?)use modules like this. But i’d finish up by saying this - i’m a random person on the internet that doesn’t know your use case. I stand by the above as decent general case advice but you might actually have a valid case to deviate AND you’re not going to unlock new discoveries in better ways to do things if you just always follow the dogma so don’t be put off by my negativity toward the idea but DO measure the outcomes you care about when experimenting to see if the modules idea is working for you.
- zo1 4y agoI don't understand, this looks like a class? Put all that in a class called Greeter in greeter.py and call it a day. Anything beyond that, I'd question very heavily "Why?" to justify the complexity/non-standardness.
- roelschroeven 4y agoYou want something that encapsulates state and a method. That's precisely what classes are for! I feel going against how the language is meant to so is just going to make things more difficult for yourself. Is using modules like that nicer? That's subjective, I guess. I don't think it's nicer. You could use dataclasses to make classes somewhat nicer (though that is subjective too). It it leaner than classes? I don't think it's something to worry about. Maybe use __slots__ in your class if and where needed. Is an encapsulated module still leaner than a class? In the end both modules and classes are namespaces. Modules are singletons, classes have as many instances as you need.
- stinos 4y agoIdiomatic for me would be this simple, short, clear thing: def greeter(name): def greet(): print(f"Hello {name}") return greet greet1 = greeter('Joe') greet2 = greeter('Sue') greet1() greet2() Or else a class. I don't quite understand why you'd want a global in a module not mean the canonical 'this singletone thingie configures this module' ?
- born2discover 4y agoIMHO that greeter function of yours is unnecessarily complicated. The same could be achieved in an easier way: """python from functools import partial def greeter(name): print(f"Hello {name}") greet1 = partial(greeter, "Joe") greet2 = partial(greeter, "Sue") greet1() greet2() """
- klyrs 4y agoI'm having trouble determining if this is parody. It appears that you want modules to replace classes, and also for users to roll their own __init__ methods? I guess that's "lean" in the sense of malnutrition, but I don't know about "nice."