1
\$\begingroup\$

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?

asked May 7, 2018 at 19:03
\$\endgroup\$
3
  • 1
    \$\begingroup\$ What's the difference between instantiating an object and instantiating a factory? It's hard to advise you on whether this is appropriate, when we don't know what the constructors for these quail objects look like, and what the objects do. \$\endgroup\$ Commented May 7, 2018 at 20:01
  • \$\begingroup\$ Installer class will be used to install a solution with install() method and others. Solution class will be used to retrieve a solution (download or unzip) with open() retrieve() and close() Which means we are already initializing stuff in the constructor: like where will be the install folder. if you want more informations, here is the project github.com/mouuff/Quail \$\endgroup\$ Commented May 7, 2018 at 20:18
  • \$\begingroup\$ Also I forgot to mention, this classes could possibily be inherited by the "user developer" to change the default behavior \$\endgroup\$ Commented May 7, 2018 at 21:16

1 Answer 1

2
\$\begingroup\$

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')
answered May 8, 2018 at 9:32
\$\endgroup\$
4
  • \$\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\$ Commented May 8, 2018 at 9:46
  • \$\begingroup\$ @mou "forces to write additionnal stuff while writting configuration". How and where does it do this? \$\endgroup\$ Commented May 8, 2018 at 9:54
  • \$\begingroup\$ Writting partial() every time passing a class \$\endgroup\$ Commented 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\$ Commented May 8, 2018 at 9:56

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.