How to instantiate a different class in a constructor?

G

GiBo

Hi all,

I have a class URI and a bunch of derived sub-classes for example
HttpURI, FtpURI, HttpsURI, etc. (this is an example, I know there is
module urllib & friends, however my actual problem however maps very
well to this example).

Now I want to pass a string to constructor of URI() and get an instance
of one of the subclasses back. For example uri=URI('http://abcd/...')
will make 'uri' an instance of HttpURI class, not instance of URI.

To achieve this I have a list of all subclasses of URI and try to
instantiate one by one in URI.__new__(). In the case I pass e.g. FTP URI
to HttpURI constructor it raises ValueError exception and I want to test
HttpsURI, FtpURI, etc.

For now I have this code:

=====
class URI(object):
def __new__(self, arg):
for subclass in subclasses:
try:
instance = object.__new__(subclass, arg)
return instance
except ValueError, e:
print "Ignoring: %s" % e
raise ValueError("URI format not recognized" % arg)

class HttpURI(URI):
def __init__(self, arg):
if not arg.startswith("http://"):
raise ValueError("%s: not a HTTP URI" % arg)
self._uri = arg

class FtpURI(URI):
def __init__(self, arg):
if not arg.startswith("ftp://"):
raise ValueError("%s: not a FTP URI")
self._uri = arg

subclasses = [HttpURI, FtpURI]

if __name__ == "__main__":
print "Testing HTTP URI"
uri = URI("http://server/path")
print uri

print "Testing FTP URI"
uri = URI("ftp://server/path")
print uri
=====

The problem is that ValueError exception raised in HttpURI.__init__() is
not handled in URI.__new__():

-----
~$ ./tst.py
Testing HTTP URI
<__main__.HttpURI object at 0x808572c> # this is good
Testing FTP URI
Traceback (most recent call last):
File "./tst.py", line 35, in <module>
uri = URI("ftp://server/path")
File "./tst.py", line 18, in __init__
raise ValueError("%s: not a HTTP URI" % arg)
ValueError: ftp://server/path: not a HTTP URI # this is bad
-----

When I change the __init__ methods of subclasses to __new__ I instead get:

-----
../tst.py
Testing HTTP URI
Traceback (most recent call last):
File "./tst.py", line 29, in <module>
uri = URI("http://server/path")
File "./tst.py", line 7, in __new__
instance = object.__new__(subclass, arg)
TypeError: default __new__ takes no parameters
-----

Does anyone have any hints on how to solve this problem? (other than
using urllib or other standard modules - as I said this is just to
demonstrate the nature of my problem).

Thanks!


GiBo
 
D

Diez B. Roggisch

I have a class URI and a bunch of derived sub-classes for example
HttpURI, FtpURI, HttpsURI, etc. (this is an example, I know there is
module urllib & friends, however my actual problem however maps very
well to this example).

Now I want to pass a string to constructor of URI() and get an instance
of one of the subclasses back. For example uri=URI('http://abcd/...')
will make 'uri' an instance of HttpURI class, not instance of URI.

To achieve this I have a list of all subclasses of URI and try to
instantiate one by one in URI.__new__(). In the case I pass e.g. FTP URI
to HttpURI constructor it raises ValueError exception and I want to test
HttpsURI, FtpURI, etc.

<snip/>


Use a factory function:

class UriBase(object):
REGISTRY = {}

class HttpUri(UriBase):
pass

UriBase.REGISTRY['http'] = HttpUri

def URI(arg):
return UriBase.REGISTRY[get_protocol(arg)](arg)

This is untested and could be enhanced by e.g. using metaclasses to perform
the registration automagicall, but I think you get the idea.

Diez
 
P

Paul McGuire

Hi all,

I have a class URI and a bunch of derived sub-classes for example
HttpURI, FtpURI, HttpsURI, etc. (this is an example, I know there is
module urllib & friends, however my actual problem however maps very
well to this example).

Now I want to pass a string to constructor of URI() and get an instance
of one of the subclasses back. For example uri=URI('http://abcd/...')
will make 'uri' an instance of HttpURI class, not instance of URI.

To achieve this I have a list of all subclasses of URI and try to
instantiate one by one in URI.__new__(). In the case I pass e.g. FTP URI
to HttpURI constructor it raises ValueError exception and I want to test
HttpsURI, FtpURI, etc.

For now I have this code:

=====
class URI(object):
def __new__(self, arg):
for subclass in subclasses:
try:
instance = object.__new__(subclass, arg)
return instance
except ValueError, e:
print "Ignoring: %s" % e
raise ValueError("URI format not recognized" % arg)

<snip>

Call __new__ and subclass.__init__ explicitly:

class URI(object):
def __new__(self, arg):
for subclass in subclasses:
try:
instance = object.__new__(subclass)
instance.__init__(arg)
return instance
except ValueError, e:
print "Ignoring: %s" % e
raise ValueError("URI format not recognized" % arg)

(Might I suggest 4-space indents vs. 8?)

-- Paul
 
G

GiBo

Paul said:
<snip>

Call __new__ and subclass.__init__ explicitly:

Thanks! That's it :)

BTW When is the subclass.__init__() method invoked if I don't explicitly
call it from __new__()? Apparently not from baseclass.__new__() nor from
object.__new__().

GiBo
 
G

Gabriel Genellina

BTW When is the subclass.__init__() method invoked if I don't explicitly
call it from __new__()? Apparently not from baseclass.__new__() nor from
object.__new__().

At instance creation, when type(name, bases, ns) is invoked, after
the __new__ call, but only if the returned object is an instance of
type. This happens in function type_call (in typeobject.c)


--
Gabriel Genellina
Softlab SRL






__________________________________________________
Preguntá. Respondé. Descubrí.
Todo lo que querías saber, y lo que ni imaginabas,
está en Yahoo! Respuestas (Beta).
¡Probalo ya!
http://www.yahoo.com.ar/respuestas
 

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,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top