Checking for required arguments when instantiating class.

L

Lacrima

Hello!

For example I have two classes:
def __init__(self, *args, **kwargs):
pass
def __init__(self, somearg, *args, **kwargs):
self.somearg = somearg

How can I test that First class takes 1 required argument and Second
class takes no required arguments?
So that I could instantiate them in a for loop.
a = [First, Second]
for cls in a:
instance = cls()

Traceback (most recent call last):
File "<pyshell#22>", line 2, in <module>
instance = cls()
TypeError: __init__() takes at least 2 arguments (1 given)

Of course, I can do like this: try:
instance = cls()
except TypeError:
instance = cls('hello')
hello

But what if I have to instantiate any class with 3 or 4 required
arguments? How can I do it?

With regards,
Max
 
L

Lacrima

class First:
        def __init__(self, *args, **kwargs):
                pass


        def __init__(self, somearg, *args, **kwargs):
                self.somearg = somearg

How can I test that First class takes 1 required argument and Second
class takes no required arguments?


Sorry, I have made a mistake. Of course, Second class takes 1 required
argument, not First.
 
C

Chris Rebert

Hello!

For example I have two classes:

       def __init__(self, *args, **kwargs):
               pass

       def __init__(self, somearg, *args, **kwargs):
               self.somearg = somearg

How can I test that First class takes 1 required argument and Second
class takes no required arguments?

import inspect
args, varargs, varkw, defaults = inspect.getargspec(klass)
num_args_reqd = len(args) - (0 if defaults is None else len(defaults))

However, it sounds like a code smell that you're instanciating unknown
classes that don't share a common constructor signature.

Cheers,
Chris
 
L

Lie Ryan

Lacrima said:
Sorry, I have made a mistake. Of course, Second class takes 1 required
argument, not First.

Of course, it is possible to just try it one-by-one with a try block and
a while loop (as long as your class/function doesn't rely/change on some
global state (e.g. files, global variable, GUI, etc) while
initializing/called), but if you really need that, there is something
seriously wrong with the class/function design. You should check the
help and determine how many and what are all the arguments meant.

In case you're wondering, this is how it would look like:
.... print
........ print a
........ print a, b
........ print a, b, args, kargs
....
>>> import itertools
>>> funclist = [f0, f1, f2, f2a]
>>> for f in funclist:
.... for i in itertools.count():
.... try:
.... f(*range(i))
.... except TypeError:
.... pass
.... else:
.... print 'Function %s takes %s required arguments' %
(f.__name__, i)
.... break
....

Function f0 takes 0 required arguments
0
Function f1 takes 1 required arguments
0 1
Function f2 takes 2 required arguments
0 1 () {}
Function f2a takes 2 required arguments
 
P

Piet van Oostrum

Lacrima said:
L> Hello!
L> For example I have two classes:
L> def __init__(self, *args, **kwargs):
L> pass
L> def __init__(self, somearg, *args, **kwargs):
L> self.somearg = somearg
L> How can I test that First class takes 1 required argument and Second
L> class takes no required arguments?
L> So that I could instantiate them in a for loop.
a = [First, Second]
for cls in a:
L> instance = cls()
L> Traceback (most recent call last):
L> File "<pyshell#22>", line 2, in <module>
L> instance = cls()
L> TypeError: __init__() takes at least 2 arguments (1 given)
L> Of course, I can do like this:
L> try:
L> instance = cls()
L> except TypeError:
L> instance = cls('hello')
L> hello
L> But what if I have to instantiate any class with 3 or 4 required
L> arguments? How can I do it?

cls.__init__.im_func.__code__.co_argcount

This will include self, so it will be 1 in First and 2 in Second.

However this is very dirty trickery and should not be recommended. It
may also change in future versions and other implementations of Python.

I think it would be cleaner to put a class attribute in the classes that
defines how they should be initialized (e.g. just the number of required
arguments or more specific information) or have a special factory method
for this use case.
 
C

Chris Rebert

L> Hello!
L> For example I have two classes:
L>     def __init__(self, *args, **kwargs):
L>             pass
L>     def __init__(self, somearg, *args, **kwargs):
L>             self.somearg = somearg
L> How can I test that First class takes 1 required argument and Second
L> class takes no required arguments?
L> So that I could instantiate them in a for loop.
a = [First, Second]
for cls in a:
L>     instance = cls()
L> Traceback (most recent call last):
L>   File "<pyshell#22>", line 2, in <module>
L>     instance = cls()
L> TypeError: __init__() takes at least 2 arguments (1 given)
L> Of course, I can do like this:
L>     try:
L>             instance = cls()
L>     except TypeError:
L>             instance = cls('hello')
L> hello
L> But what if I have to instantiate any class with 3 or 4 required
L> arguments? How can I do it?

cls.__init__.im_func.__code__.co_argcount

This will include self, so it will be 1 in First and 2 in Second.

AFAICT, that would count non-required arguments too, which isn't
strictly what the OP requested.
However this is very dirty trickery and should not be recommended. It
may also change in future versions and other implementations of Python.

Very much agreed.
I think it would be cleaner to put a class attribute in the classes that
defines how they should be initialized (e.g. just the number of required
arguments or more specific information) or have a special factory method
for this use case.

Seconded. I'd recommend the latter personally, though it's impossible
to give a definitive answer without more context.

Cheers,
Chris
 
L

Lacrima

L> Hello!
L> For example I have two classes:
class First:
L>     def __init__(self, *args, **kwargs):
L>             pass
class Second:
L>     def __init__(self, somearg, *args, **kwargs):
L>             self.somearg = somearg
L> How can I test that First class takes 1 required argument and Second
L> class takes no required arguments?
L> So that I could instantiate them in a for loop.
a = [First, Second]
for cls in a:
L>     instance = cls()
L> Traceback (most recent call last):
L>   File "<pyshell#22>", line 2, in <module>
L>     instance = cls()
L> TypeError: __init__() takes at least 2 arguments (1 given)
L> Of course, I can do like this:
for cls in a:
L>     try:
L>             instance = cls()
L>     except TypeError:
L>             instance = cls('hello')
print instance.somearg
L> hello
L> But what if I have to instantiate any class with 3 or 4 required
L> arguments? How can I do it?
cls.__init__.im_func.__code__.co_argcount

This will include self, so it will be 1 in First and 2 in Second.

AFAICT, that would count non-required arguments too, which isn't
strictly what the OP requested.
However this is very dirty trickery and should not be recommended. It
may also change in future versions and other implementations of Python.

Very much agreed.
I think it would be cleaner to put a class attribute in the classes that
defines how they should be initialized (e.g. just the number of required
arguments or more specific information) or have a special factory method
for this use case.

Seconded. I'd recommend the latter personally, though it's impossible
to give a definitive answer without more context.

Cheers,
Chris
--http://blog.rebertia.com

Thanks for all of you!

I think I'll try to write a special method for this case and will
report you result.

-Max
 
P

Piet van Oostrum

L> But what if I have to instantiate any class with 3 or 4 required
L> arguments? How can I do it?
CR> AFAICT, that would count non-required arguments too, which isn't
CR> strictly what the OP requested.

If you mean, parameters with default values, yes. Of course you can find
out how many of these there are from cls.__init__.func_defaults, but
this will get dirtier and dirtier.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top