Class design issues: multiple constructors

G

Greg Brunet

I'm trying to write a package to handle working with DBase files.
There's enough different stuff in handling them (unfortunately) that I'm
not going to try to the interface DB-API 2.0 compliant - it'll be a
lower level wrapper around reading & writing the DBF files. In trying
to design a good framework, however, I'm unsure of how to progress. I
intend to have a class to represent the actual DBF files, and I would
expect to have an __init__ method to be called when creating a new
instance. When I first started, I included a filename as a parameter
that would be used to open an existing DBF file. Next I decided to add
the ability to create a new DBF file. This method needs additional
parameters (such as the field definitions), and, while in some other
languages, I could provide 2 versions of the constructor (overload it if
I'm using the right terminology), and the compiler would use the
appropriate one, things don't seem to work that way in Python. I'm also
thinking that I might rather name the methods more specifically (such as
Open & Create) instead of both being __init__. What would be the
Pythonic way to go about doing this? Would I make an __init__, Open, &
Create methods, and make 2 calls for each DBF object, like this:

class dbf:
__init__(self):
pass
Create(self, filename, fieldDefs):
pass
Open(self, filename):
pass

# open existing file
f1 = dbf()
f1 = dbf.Open('customer.dbf')
# create a new file
f2 = dbf()
f2 = dbf.Create('states.dbf', [('StateCode', 'C', 2), \
('StateName','C',20)]


Or maybe there's a clean way to be able to 'bundle' the
initialization/construction into the Create & Open method calls. Thanks
for the help,
 
A

Alex Martelli

Greg Brunet wrote:
...
instance. When I first started, I included a filename as a parameter
that would be used to open an existing DBF file. Next I decided to add
the ability to create a new DBF file. This method needs additional
parameters (such as the field definitions), and, while in some other
languages, I could provide 2 versions of the constructor (overload it if
I'm using the right terminology), and the compiler would use the
appropriate one, things don't seem to work that way in Python. I'm also
thinking that I might rather name the methods more specifically (such as
Open & Create) instead of both being __init__. What would be the

Right. The Pythonic way is to provide "factory functions" that prepare
and return the object you need. Cosmetically, you may make the factory
functions part of the class itself, using the staticmethod and classmethod
built-in types of Python 2.2 and later -- no real need, but some people
are very keen on this style, so Python now supports it.
Pythonic way to go about doing this? Would I make an __init__, Open, &
Create methods, and make 2 calls for each DBF object, like this:

class dbf:
__init__(self):
pass
Create(self, filename, fieldDefs):
pass
Open(self, filename):
pass

No, it should rather be something like:

class dbf(object):
# no need to define __init__ if it's empty!
def Create(filename, fieldDefs):
result = dbf()
# use filename and fieldDefs to populate 'result' appropriately
return result
Create = staticmethod(Create)
def Open(filename):
result = dbf()
# use filename to populate 'result' appropriately
return result
Open = staticmethod(Open)

# open existing file
f1 = dbf()
f1 = dbf.Open('customer.dbf')

No, this wouldn't work with the code you propose; it would with
the variant I suggest, but the first of these statements is useless
so you should remove it.
# create a new file
f2 = dbf()
f2 = dbf.Create('states.dbf', [('StateCode', 'C', 2), \
('StateName','C',20)]

Ditto.


Alex
 
M

Miki Tebeka

Hello Greg,
When I first started, I included a filename as a parameter
that would be used to open an existing DBF file. Next I decided to add
the ability to create a new DBF file. This method needs additional
parameters (such as the field definitions), and, while in some other
languages, I could provide 2 versions of the constructor (overload it if
I'm using the right terminology), and the compiler would use the
appropriate one, things don't seem to work that way in Python. I'm also
thinking that I might rather name the methods more specifically (such as
Open & Create) instead of both being __init__. What would be the
Pythonic way to go about doing this? Would I make an __init__, Open, &
Create methods, and make 2 calls for each DBF object, like this:

I see two ways:
1. Module (not class) methods: db = opendb(..), db = createdb(...)
2. Passing keyword arguments.
def __init__(selfk, filename, **kw):
if kw.has_key(create):
....

I vote for the former.

HTH.
Miki
 
G

Greg Brunet

Hi Miki:
I see two ways:
1. Module (not class) methods: db = opendb(..), db = createdb(...)
2. Passing keyword arguments.
def __init__(selfk, filename, **kw):
if kw.has_key(create):
....

I vote for the former.


Thanks for the response. I lie the first also - what function is being
called is more clear than option 2 which would require extra tests
(depending on how many __init__ variations there are). If I can
understand the staticmethod stuff that Alex mentioned in his message, I
may try that path , since I'd like to have the methods actually in the
class definition, but defining them in the module itself is definitely a
good option. Thanks,
 
M

Michele Simionato

Greg Brunet said:
I'm trying to write a package to handle working with DBase files.
There's enough different stuff in handling them (unfortunately) that I'm
not going to try to the interface DB-API 2.0 compliant - it'll be a
lower level wrapper around reading & writing the DBF files. In trying
to design a good framework, however, I'm unsure of how to progress. I
intend to have a class to represent the actual DBF files, and I would
expect to have an __init__ method to be called when creating a new
instance. When I first started, I included a filename as a parameter
that would be used to open an existing DBF file. Next I decided to add
the ability to create a new DBF file. This method needs additional
parameters (such as the field definitions), and, while in some other
languages, I could provide 2 versions of the constructor (overload it if
I'm using the right terminology), and the compiler would use the
appropriate one, things don't seem to work that way in Python. I'm also
thinking that I might rather name the methods more specifically (such as
Open & Create) instead of both being __init__. What would be the
Pythonic way to go about doing this? Would I make an __init__, Open, &
Create methods, and make 2 calls for each DBF object, like this:

class dbf:
__init__(self):
pass
Create(self, filename, fieldDefs):
pass
Open(self, filename):
pass

# open existing file
f1 = dbf()
f1 = dbf.Open('customer.dbf')
# create a new file
f2 = dbf()
f2 = dbf.Create('states.dbf', [('StateCode', 'C', 2), \
('StateName','C',20)]

If I understand you correctly, this could be done with a metaclass
redefining the __call__ method and invoking Open or Create depending
on the number of the arguments:

class MetaTrick(type):
def __call__(cls,*args):
nargs=len(args)
obj=cls.__new__(cls)
if nargs==1:
obj.Open(*args)
elif nargs==2:
obj.Create(*args)
else:
raise 'Wrong number of arguments'

class dbf:
__metaclass__=MetaTrick
def Create(self, filename, fieldDefs):
print filename,fieldDefs
def Open(self, filename):
print filename

f1 = dbf('customer.dbf')
f2 = dbf('states.dbf', [('StateCode', 'C', 2),
('StateName','C',20)])


Alternatively, without metaclass, you could redefine the __new__ method
of the class and use a similar trick.
HTH,


Michele
 
G

Greg Brunet

Hi Michele

Michele Simionato said:
If I understand you correctly, this could be done with a metaclass
redefining the __call__ method and invoking Open or Create depending
on the number of the arguments:

class MetaTrick(type):
def __call__(cls,*args):
nargs=len(args)
obj=cls.__new__(cls)
if nargs==1:
obj.Open(*args)
elif nargs==2:
obj.Create(*args)
else:
raise 'Wrong number of arguments'

class dbf:
__metaclass__=MetaTrick
def Create(self, filename, fieldDefs):
print filename,fieldDefs
def Open(self, filename):
print filename

f1 = dbf('customer.dbf')
f2 = dbf('states.dbf', [('StateCode', 'C', 2),
('StateName','C',20)])


Alternatively, without metaclass, you could redefine the __new__ method
of the class and use a similar trick.
HTH,


Hmm - I see. That could work in this instance, but could get more
complex with a different combination of 'constructor' types. Since
python doesn't seem to handle this overloading for me automatically, I
don't think that it's worth the effort it to manually implement it -
it's likely to be a source of more work & errors down the road. I'm
kind of leaning to using the 'staticmethod' technique that Alex
mentioned. Thanks for another view on it - that (metaclasses) gives me
one more technique to use in the future,
 

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

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top