Re: Creating new instances of subclasses.

2009-01-12 Thread J. Clifford Dyer
On Fri, 2009-01-09 at 10:46 -0800, Dennis Lee Bieber wrote:
 On Wed, 07 Jan 2009 11:38:29 -0500, J. Cliff Dyer j...@unc.edu
 declaimed the following in comp.lang.python:
 
  I want to be able to create an object of a certain subclass, depending
  on the argument given to the class constructor.
  
  I have three fields, and one might need to be a StringField, one an
  IntegerField, and the last a ListField.  But I'd like my class to
  delegate to the proper subclass automatically, so I can just do:
  
   f1 = Field('abc')
   f2 = Field('123')
   f3 = Field('D,E,F')
 
   And how do you differentiate a string that contains a comma from a
 purported list?
 
   What is expected for:
 
   ambiguous = Field('He said Blast, it won''t work')

My strings don't look like that.  Nor is that relevant to my question.
I created a simple, uncluttered example to illustrate my issue with
instantiating the proper subclass for a given argument.  I did not try
to make sure my example handled all edge cases properly.

Cheers,
Cliff


--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-09 Thread J. Cliff Dyer
Thanks for the solutions everyone!  I'm not sure which I'll end up
using, but I think I've got a better grasp of the problem now.  

Cool stuff.

Cheers,
Cliff

On Thu, 2009-01-08 at 06:52 -0800, Paul McGuire wrote:
 On Jan 7, 12:00 pm, Paul McGuire pt...@austin.rr.com wrote:
  On Jan 7, 10:38 am, J. Cliff Dyer j...@unc.edu wrote:
 
   I want to be able to create an object of a certain subclass, depending
   on the argument given to the class constructor.
 
   I have three fields, and one might need to be a StringField, one an
   IntegerField, and the last a ListField.  But I'd like my class to
   delegate to the proper subclass automatically, so I can just do:
 
f1 = Field('abc')
f2 = Field('123')
f3 = Field('D,E,F')
 
  O-O is not always the solution to every problem.  Since inheritance is
  getting in your way, try using a class-level factory method.  Instead
  of using the Field constructor, use a staticmethod of Field, something
  like:
 
  @staticmethod
  def make_Field(a)
  if is_list(a):
  return ListField(a)
  elif is_integer(a):
  return IntegerField(a)
  else:
  return StringField(a)
 
  and then get rid of all those __new__ methods, too.
 
  -- Paul
 
 After looking this over a bit more, I decided I didn't like make_Field
 having to know the criteria for creating the different subclasses, but
 wanted to put the smarts into the subclasses themselves.  Here is an
 excerpt that shows this working:
 
 class Field(object):
 def __init__(self, input):
 super(Field, self).__init__(input)
 self.data = input
 
 @staticmethod
 def make_Field(a):
 subs = (ListField, IntegerField, StringField)
 ret = None
 for cls in subs:
 try:
 ret = cls(a)
 except TypeError:
 continue
 else:
 break
 return ret
 
 class IntegerField(Field):
 def __new__(cls, a):
 if not is_integer(a):
 raise TypeError()
 return Field.__new__(cls, a)
 
 ...
 ListField has a similar __new__ method, and StringField just creates
 the object, with no validation.
 
 make_Field still has to know what order to list the subclasses in
 (StringField is the most permissive, and so must come last in the list
 of subclasses), but the specific type tests are moved into the
 subclasses, which is a more appropriate place I think.
 
 -- Paul
 --
 http://mail.python.org/mailman/listinfo/python-list
 

--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-08 Thread Nick Craig-Wood
J. Cliff Dyer j...@unc.edu wrote:
  I want to be able to create an object of a certain subclass, depending
  on the argument given to the class constructor.
 
  I have three fields, and one might need to be a StringField, one an
  IntegerField, and the last a ListField.  But I'd like my class to
  delegate to the proper subclass automatically, so I can just do:
 
  f1 = Field('abc')
  f2 = Field('123')
  f3 = Field('D,E,F')
  f1.data
  'abc'
  f2.data
  123
  f3.data
  ['D','E','F']
  type(f1)
 class '__main__.StringField'
  type(f2)
 class '__main__.StringField'
  type(f3)
 class '__main__.ListField'
 
  I've come up with a solution, but I suspect there's something cleaner I
  can do with the inheritance structure of __new__.  I don't like
  explicitly leapfrogging over Field.__new__ to object.__new__.
 
  My attempt is below:
 
  def is_list(arg):
  if ',' in arg:  return True
  else:  return False
 
  def is_integer(arg):
  try:  int(arg)
  except ValueError:  return False
  else:  return True
 
  class Field(object):
  def __new__(cls, a):
  if is_list(a):
  return ListField(a)
  elif is_integer(a):
  return IntegerField(a)
  else:
  return StringField(a)
  
  def __init__(self, input):
  super(Field, self).__init__(input)
  self.data = input
 
  class IntegerField(Field):
  def __new__(cls, a):
  return object.__new__(cls, a)
  def __init__(self, s):
  super(IntegerField, self).__init__(s)
  self.s = int(self.s)
  
  class ListField(Field):
  def __new__(cls, a):
  return object.__new__(cls, a)
  def __init__(self, s):
  super(ListField, self).__init__(s)
  self.s = s.split(',')
 
  class StringField(Field):
  def __new__(cls, a):
  return object.__new__(cls, a)
 
  Is there a cleaner way to do this?  The main problem is that
  Field.__new__ gets in the way of properly constructing the subclasses
  once I've used it to select the proper subclass in the first place.

How about this minor modification?

# rest as above

class Field(object):
def __new__(cls, a):
if cls != Field:
return object.__new__(cls, a)
if is_list(a):
return ListField(a)
elif is_integer(a):
return IntegerField(a)
else:
return StringField(a)

def __init__(self, input):
super(Field, self).__init__(input)
self.data = input

class IntegerField(Field):
def __init__(self, s):
super(IntegerField, self).__init__(s)
self.s = int(s)

class ListField(Field):
def __init__(self, s):
super(ListField, self).__init__(s)
self.s = s.split(',')

class StringField(Field):
pass

Or you could go for the full metaclass self registration scheme like
this, which is actually less code since I delegated the is this ok
test to the subclass - failing it returns a ValueError.

class Field(object):
registry = []
class __metaclass__(type):
def __init__(cls, name, bases, dict):
cls.registry.append(cls)
def __new__(cls, a):
if cls != Field:
return object.__new__(cls, a)
for subcls in cls.registry:
if subcls == Field:
continue
try:
return subcls(a)
except ValueError:
pass
raise ValueError(Couldn't find subclass)
def __init__(self, input):
super(Field, self).__init__(input)
self.data = input

# Raise a ValueError in init if not suitable args for this subtype

class IntegerField(Field):
def __init__(self, s):
s = int(s)
super(IntegerField, self).__init__(s)
self.s = s

class ListField(Field):
def __init__(self, s):
if ',' not in s:
raise ValueError(Not a list)
super(ListField, self).__init__(s)
self.s = s.split(',')

class StringField(Field):
pass



-- 
Nick Craig-Wood n...@craig-wood.com -- http://www.craig-wood.com/nick
--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-08 Thread Paul McGuire
On Jan 7, 12:00 pm, Paul McGuire pt...@austin.rr.com wrote:
 On Jan 7, 10:38 am, J. Cliff Dyer j...@unc.edu wrote:

  I want to be able to create an object of a certain subclass, depending
  on the argument given to the class constructor.

  I have three fields, and one might need to be a StringField, one an
  IntegerField, and the last a ListField.  But I'd like my class to
  delegate to the proper subclass automatically, so I can just do:

   f1 = Field('abc')
   f2 = Field('123')
   f3 = Field('D,E,F')

 O-O is not always the solution to every problem.  Since inheritance is
 getting in your way, try using a class-level factory method.  Instead
 of using the Field constructor, use a staticmethod of Field, something
 like:

 @staticmethod
 def make_Field(a)
     if is_list(a):
         return ListField(a)
     elif is_integer(a):
         return IntegerField(a)
     else:
         return StringField(a)

 and then get rid of all those __new__ methods, too.

 -- Paul

After looking this over a bit more, I decided I didn't like make_Field
having to know the criteria for creating the different subclasses, but
wanted to put the smarts into the subclasses themselves.  Here is an
excerpt that shows this working:

class Field(object):
def __init__(self, input):
super(Field, self).__init__(input)
self.data = input

@staticmethod
def make_Field(a):
subs = (ListField, IntegerField, StringField)
ret = None
for cls in subs:
try:
ret = cls(a)
except TypeError:
continue
else:
break
return ret

class IntegerField(Field):
def __new__(cls, a):
if not is_integer(a):
raise TypeError()
return Field.__new__(cls, a)

...
ListField has a similar __new__ method, and StringField just creates
the object, with no validation.

make_Field still has to know what order to list the subclasses in
(StringField is the most permissive, and so must come last in the list
of subclasses), but the specific type tests are moved into the
subclasses, which is a more appropriate place I think.

-- Paul
--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-07 Thread Paul McGuire
On Jan 7, 10:38 am, J. Cliff Dyer j...@unc.edu wrote:
 I want to be able to create an object of a certain subclass, depending
 on the argument given to the class constructor.

 I have three fields, and one might need to be a StringField, one an
 IntegerField, and the last a ListField.  But I'd like my class to
 delegate to the proper subclass automatically, so I can just do:

  f1 = Field('abc')
  f2 = Field('123')
  f3 = Field('D,E,F')

O-O is not always the solution to every problem.  Since inheritance is
getting in your way, try using a class-level factory method.  Instead
of using the Field constructor, use a staticmethod of Field, something
like:

@staticmethod
def make_Field(a)
if is_list(a):
return ListField(a)
elif is_integer(a):
return IntegerField(a)
else:
return StringField(a)

and then get rid of all those __new__ methods, too.

-- Paul
--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-07 Thread Terry Reedy

J. Cliff Dyer wrote:

I want to be able to create an object of a certain subclass, depending
on the argument given to the class constructor.

I have three fields, and one might need to be a StringField, one an
IntegerField, and the last a ListField.  But I'd like my class to
delegate to the proper subclass automatically, so I can just do:


f1 = Field('abc')
f2 = Field('123')
f3 = Field('D,E,F')
f1.data

'abc'

f2.data

123

f3.data

['D','E','F']

type(f1)

class '__main__.StringField'

type(f2)

class '__main__.StringField'

type(f3)

class '__main__.ListField'

I've come up with a solution, but I suspect there's something cleaner


Make your master class _Field and make Field a factory function that 
returns the proper subclass instance.  The body of Field could be the 
body of __new__ below.  Then dump the __new__ methods.


tjr


 I

can do with the inheritance structure of __new__.  I don't like
explicitly leapfrogging over Field.__new__ to object.__new__.

My attempt is below:

def is_list(arg):
if ',' in arg:  return True
else:  return False

def is_integer(arg):
try:  int(arg)
except ValueError:  return False
else:  return True

class Field(object):
def __new__(cls, a):
if is_list(a):
return ListField(a)
elif is_integer(a):
return IntegerField(a)
else:
return StringField(a)

def __init__(self, input):

super(Field, self).__init__(input)
self.data = input

class IntegerField(Field):
def __new__(cls, a):
return object.__new__(cls, a)
def __init__(self, s):
super(IntegerField, self).__init__(s)
self.s = int(self.s)

class ListField(Field):

def __new__(cls, a):
return object.__new__(cls, a)
def __init__(self, s):
super(ListField, self).__init__(s)
self.s = s.split(',')

class StringField(Field):
def __new__(cls, a):
return object.__new__(cls, a)

Is there a cleaner way to do this?  The main problem is that
Field.__new__ gets in the way of properly constructing the subclasses
once I've used it to select the proper subclass in the first place.

Cheers,
Cliff



--
http://mail.python.org/mailman/listinfo/python-list


Re: Creating new instances of subclasses.

2009-01-07 Thread Bruno Desthuilliers

J. Cliff Dyer a écrit :

I want to be able to create an object of a certain subclass, depending
on the argument given to the class constructor.

I have three fields, and one might need to be a StringField, one an
IntegerField, and the last a ListField.  But I'd like my class to
delegate to the proper subclass automatically, so I can just do:


f1 = Field('abc')
f2 = Field('123')
f3 = Field('D,E,F')
f1.data

'abc'

f2.data

123

f3.data

['D','E','F']

type(f1)

class '__main__.StringField'

type(f2)

class '__main__.StringField'

type(f3)

class '__main__.ListField'

I've come up with a solution, but I suspect there's something cleaner I
can do with the inheritance structure of __new__.  I don't like
explicitly leapfrogging over Field.__new__ to object.__new__.

My attempt is below:

def is_list(arg):
if ',' in arg:  return True
else:  return False

def is_integer(arg):
try:  int(arg)
except ValueError:  return False
else:  return True

class Field(object):
def __new__(cls, a):
if is_list(a):
return ListField(a)
elif is_integer(a):
return IntegerField(a)
else:
return StringField(a)

def __init__(self, input):

super(Field, self).__init__(input)
self.data = input

class IntegerField(Field):
def __new__(cls, a):
return object.__new__(cls, a)
def __init__(self, s):
super(IntegerField, self).__init__(s)
self.s = int(self.s)

class ListField(Field):

def __new__(cls, a):
return object.__new__(cls, a)
def __init__(self, s):
super(ListField, self).__init__(s)
self.s = s.split(',')

class StringField(Field):
def __new__(cls, a):
return object.__new__(cls, a)

Is there a cleaner way to do this?  The main problem is that
Field.__new__ gets in the way of properly constructing the subclasses
once I've used it to select the proper subclass in the first place.


Not only that, but the base class should know nothing of its subclasses. 
As Paul suggested, a better solution would be to make Field a factory 
function (and rename the Field class to BaseField).


Also and FWIW, since your Field subclasses are responsible for doing the 
conversion, you could as well use this to find out the concrete class to 
use for a given value, ie (not tested):


class Field(object):
__classes = []
__default = None

@classmethod
def register(cls, subclass, is_default=False):
if is_default:
if cls.__default is not None:
raise WhateverError(only one default subclass, thanks)
cls.__default = subclass

cls.__classes.append(subclass)

def __new__(cls, value):
for subclass in cls.__classes:
try:
return subclass(value)
except (TypeError, ValueError):
continue

if cls.__default is not None:
return cls.__default(value)

raise ValueError(no appropriate subclass for '%s' % value)


class BaseField(object):
# common code here

class FloatField(BaseField):
def __init__(self, value):
self.value = float(value)

Field.register(FloatField)

class ListField(BaseField):
def __init__(self, value):
if , in value:
self.value = value.split(,)
raise ValueError(could not convert '%s' to a list % value)

Field.register(ListField)

class IntegerField(BaseField):
def __init__(self, value):
self.value = int(value)

Field.register(IntegerField)

class StringField(self, value):
def __init__(self, value):
self.value = str(value)

Field.register(StringField, is_default=True)

Now this may just be overkill for your needs - I don't have enough 
context to know !-)



--
http://mail.python.org/mailman/listinfo/python-list