Open luismsmendonca opened 1 year ago
funcy already has LazyObject doesn't it work for you? If not can you please explain the specifics?
Hi @Suor , yes I use it a bunch for initialization too. The pattern I'm describing is something along the lines of:
foo_api = Proxy()
foo_api_1 = FooApi()
foo_api.initialize(foo_api_1)
foo_api.bar() # uses foo_api_1
foo_api_2 = FooApi()
foo_api.initialize(foo_api_2)
foo_api.bar() # uses foo_api_2
I find this pattern useful for testing and abstracting connections to db's and stuff.
This cannot be accomplished with the LazyObject
since _setup cannot be called for an adhoc object (same as with other libraries mentioned before). This is not much of a "Lazy" pattern but a "deferred" initialization one I believe.
Thanks. Luis
And how you want it to look? The same way it's in your code then that could be achieved with something like:
def make_proxy():
obj = None
@LazyObject
def proxy():
if obj is None:
raise ValueError("You need to init the proxy before using it")
return obj
def init(api):
nonlocal obj
obj = api
return proxy, init
And use it like:
foo_api, init_foo_api = make_proxy()
init_foo_api(FooApi())
foo_api.bar()
It could also be integrated into LazyObject
itself somehow, i.e. by allowing ._setup()
to accept arguments or smth. Or simply subclassing it, rewriting how initialization is done.
The full thing could be written as:
class DeferInitObject:
def init(self, obj):
object.__setattr__(self, '__class__', obj.__class__)
object.__setattr__(self, '__dict__', obj.__dict__)
def __getattr__(self, name):
raise ValueError("You need to init the proxy before using it")
def __setattr__(self, name, value):
raise ValueError("You need to init the proxy before using it")
Won't work when obj
has a class with slots though, same as LazyObject
.
Hi @Suor,
Thank you very much for your replies.
In the examples you gave the proxy object can only be initialized once and not multiple times like in the example I gave.
Example:
class Foo():
def bar(self):
print(id(self))
p = DeferInitObject()
p.init(Foo())
p.bar()
p.init(Foo())
p.bar()
on the second init it will raise AttributeError: 'Foo' object has no attribute 'init'
where as with the make_proxy:
p, p_init = make_proxy()
p_init(Foo())
p.bar()
p_init(Foo())
p.bar()
will print same id, i.e.:
$ python foo.py
140142395670336
140142395670336
effectively not using the second init.
Just as an example:
from peewee import Proxy
p = Proxy()
p.initialize(Foo())
p.bar()
p.initialize(Foo())
p.bar()
gives:
$ python foo.py
140282243186400
140282243186352
Again, when I'm using peewee I use the Proxy that comes with it, but when no sql db is needed and I don't have peewee installed I find it silly to include a sql db library just to apply this pattern.
Since funcy is always the first in the list :) and this is akin to its sort of utilities, I was wondering if there is an opening to include it.
Thanks,
Luis
Ok, I see, I missed that you rewrite the object twice. The name initialize
also didn't help. I guess one may get rewiring code out and make it work like:
def rewire(dest, source):
object.__setattr__(dest, '__class__', source.__class__)
object.__setattr__(dest, '__dict__', source.__dict__)
foo_api = object()
foo_api_1 = FooApi()
rewire(foo_api, foo_api_1)
foo_api.bar() # uses foo_api_1
foo_api_2 = FooApi()
rewire(foo_api, foo_api_2)
foo_api.bar() # uses foo_api_2
Or you can simply copy this peewee.Proxy
code to your project and use it. Or do something in-between.
It might be useful to have it in funcy in some form, not sure which one exactly though.
Also, making it explicit instead of implicit is also an option if you control the using part of your code:
foo_api = Proxy()
foo_api.client = FooApi()
foo_api.client.bar() # uses foo_api_1
foo_api_2 = FooApi()
foo_api.client = foo_api_2
foo_api.client.bar() # uses foo_api_2
This might be easier on the code reader in the future.
Hi @Suor,
Never realized how easy it to do this with this rewiring snippet you gave.
Thanks :)
Add a Proxy object in style of peewee.Proxy in order to have a later initialization of the object. There are some libraries (wrapt or lazy-object-proxy) but they don't cover this specific scenario where initialization occurs after proxy is initialized. If there is interest I can go on and make a pr. Thanks for awesome library.