I m writting some API which would use configuration-like arguments.
Here is an example configuration which will be used by my API
quail.run(
quail.SolutionPacked(path='Allum1'),
quail.Installer(
name='Allum1',
icon='icon.jpeg',
binary='allum1',
console=True
),
quail.builder.Builder(
quail.builder.CmdIcon('icon.ico'),
quail.builder.CmdZip(solution_path, 'solution.zip')
)
)
The only problem with that: we will always instantiate, even when we won't use the instance, (for example if we want to uninstall we won't use the install class)
One way around I have found is to use metaclass and override __call__
to get an "EggInstance" so I can instantiate the actual class later on.
Example implementation:
import six
class InstanceEgg:
def __init__(self, cls, *args, **kwargs):
self._cls = cls
self._args = args
self._kwargs = kwargs
def __call__(self):
return self._cls(_get_instance=True, *self._args, **self._kwargs)
class Egg(type):
def __call__(cls, *args, **kwargs):
get_instance = kwargs.pop("_get_instance", False)
if get_instance:
return super().__call__(*args, **kwargs)
return InstanceEgg(cls, *args, **kwargs)
@six.add_metaclass(Egg)
class Test:
def __init__(self, a, b):
print("init %d %d" % (a, b))
Example use:
egg = Test(1, 2)
print("got egg")
f = egg()
Output:
got egg
init 1 2
Do you think it is acceptable to use metaclass for this purpose?
And what do you recommend?
1 Answer 1
From the looks of it you have objects that you only want to partially create, when you create them the first time.
To do this you can use functools.partial
:
quail.run(
partial(quail.SolutionPacked, path='Allum1'),
partial(
quail.Installer,
name='Allum1',
icon='icon.jpeg',
binary='allum1',
console=True
),
partial(
quail.builder.Builder,
partial(quail.builder.CmdIcon, 'icon.ico'),
partial(quail.builder.CmdZip, solution_path, 'solution.zip')
)
)
Yeah, this isn't really that attractive. However it is what you want.
And so you can use a metaclass to perform this action automagically.
I'd remove the _get_instance
stuff from your Egg
, remove InstanceEgg
and use a partial
call.
from functools import partial
class PartialInit(type):
def __call__(cls, *args, **kwargs):
return partial(super().__call__, *args, **kwargs)
@six.add_metaclass(PartialInit)
class Test:
def __init__(self, a, b):
print('init {} {}'.format(a, b))
t = Test('a')
print('Made t: {}'.format(t))
t('b')
-
\$\begingroup\$ I didn't know about functools.partial but this solution forces to write additionnal stuff while writting configuration and stilll keeping metaclass's magic stuff, I don't see the advantage \$\endgroup\$mou– mou2018年05月08日 09:46:24 +00:00Commented May 8, 2018 at 9:46
-
\$\begingroup\$ @mou "forces to write additionnal stuff while writting configuration". How and where does it do this? \$\endgroup\$2018年05月08日 09:54:20 +00:00Commented May 8, 2018 at 9:54
-
\$\begingroup\$ Writting partial() every time passing a class \$\endgroup\$mou– mou2018年05月08日 09:55:36 +00:00Commented May 8, 2018 at 9:55
-
1\$\begingroup\$ @mou You don't have to do that... As I said that's what you'd do without a metaclass, and so "you can use a metaclass to perform this action automagically" \$\endgroup\$2018年05月08日 09:56:35 +00:00Commented May 8, 2018 at 9:56
quail
objects look like, and what the objects do. \$\endgroup\$