one-time initialization of class members

J

James Turk

Hi,

I have a situation where I have some class members that should only be
done once. Essentially my problem looks like this:

class Base(object):
dataset = None

def __init__(self, param):
if type(self).dataset is None:
# code to load dataset based on param, expensive

class ChildClass1(Base):
def __init__(self):
Base.__init__(self, data_params)

class AnotherChildClass(Base):
def __init__(self):
Base.__init__(self, other_data_params)


This seems to work, initialization is only done at the first creation
of either class. I was just wondering if this is the 'pythonic' way
to do this as my solution does feel a bit hackish.
 
S

Steven Bethard

James said:
Hi,

I have a situation where I have some class members that should only be
done once. Essentially my problem looks like this:

class Base(object):
dataset = None

def __init__(self, param):
if type(self).dataset is None:
# code to load dataset based on param, expensive

class ChildClass1(Base):
def __init__(self):
Base.__init__(self, data_params)

class AnotherChildClass(Base):
def __init__(self):
Base.__init__(self, other_data_params)


This seems to work, initialization is only done at the first creation
of either class. I was just wondering if this is the 'pythonic' way
to do this as my solution does feel a bit hackish.

What should happen with code like::

ChildClass1('foo')
ChildClass1('bar')

The 'param' is different, but 'dataset' should only get set the first time?

STeVe
 
J

James Turk

What should happen with code like::

ChildClass1('foo')
ChildClass1('bar')

The 'param' is different, but 'dataset' should only get set the first time?

STeVe

ChildClass doesn't take the parameter in it's constructor, it supplies
it for the BaseClass. Every ChildClass of the same type should use
the same dataset.
 
L

Larry Bates

James said:
Hi,

I have a situation where I have some class members that should only be
done once. Essentially my problem looks like this:

class Base(object):
dataset = None

def __init__(self, param):
if type(self).dataset is None:
# code to load dataset based on param, expensive

class ChildClass1(Base):
def __init__(self):
Base.__init__(self, data_params)

class AnotherChildClass(Base):
def __init__(self):
Base.__init__(self, other_data_params)


This seems to work, initialization is only done at the first creation
of either class. I was just wondering if this is the 'pythonic' way
to do this as my solution does feel a bit hackish.
I could be missing something but dataset is shared among all the class
instances. If you reset it based on param it will be reset every time
you create a new instance of the Base class with a different param. Is
that really what you want to do? If so just use:

class Base(object):
dataset = None

def __init__(self, param):
if self.dataset is None:
# code to load dataset based on param, expensive


-Larry
 
J

James Turk

I could be missing something but dataset is shared among all the class
instances. If you reset it based on param it will be reset every time
you create a new instance of the Base class with a different param. Is
that really what you want to do? If so just use:

class Base(object):
dataset = None

def __init__(self, param):
if self.dataset is None:
# code to load dataset based on param, expensive

-Larry

I'm sorry, I somehow omitted the fact that the dataset does indeed
need to vary based on the child class, actually this is the main
difference between child classes.
 
S

Steven D'Aprano

ChildClass doesn't take the parameter in it's constructor, it supplies
it for the BaseClass. Every ChildClass of the same type should use
the same dataset.

Then each type of ChildClass should be a sub-class, and provide it's own
dataset:


class BaseClass:
dataset = None
# blah blah blah...


class ChildClass1(BaseClass):
dataset = SomethingUseful

class ChildClass2(BaseClass):
dataset = SomethingElse
 
G

Gabriel Genellina

En Wed, 13 Jun 2007 22:03:50 -0300, Steven D'Aprano
Then each type of ChildClass should be a sub-class, and provide it's own
dataset:


class BaseClass:
dataset = None
# blah blah blah...


class ChildClass1(BaseClass):
dataset = SomethingUseful
class ChildClass2(BaseClass):
dataset = SomethingElse

But the OP also stated that creating the dataset is expensive. The
original code does what you say (each ChildClass is a subclass and
provides its own dataset) so I think is an acceptable solution:

py> def build_dataset(x):
.... print "building dataset:",x
.... return [x]
....
py> class Base(object):
.... dataset = None
.... def __init__(self, param):
.... if type(self).dataset is None:
.... type(self).dataset = build_dataset(param)
....
py> class ChildClass1(Base):
.... def __init__(self):
.... Base.__init__(self, "Params for ChildClass1")
....
py> class AnotherChildClass(Base):
.... def __init__(self):
.... Base.__init__(self, "Params for AnotherChildClass")
....
py> c1 = ChildClass1()
building dataset: Params for ChildClass1
py> c2 = AnotherChildClass()
building dataset: Params for AnotherChildClass
py> c3 = ChildClass1()
py> print Base.dataset
None
py> print ChildClass1.dataset
['Params for ChildClass1']
py> print AnotherChildClass.dataset
['Params for AnotherChildClass']
py> print c1.dataset
['Params for ChildClass1']
py> print c3.dataset
['Params for ChildClass1']
py> print c1.dataset is c3.dataset
True
py>
 
J

James Turk

Then each type of ChildClass should be a sub-class, and provide it's own
dataset:

class BaseClass:
dataset = None
# blah blah blah...

class ChildClass1(BaseClass):
dataset = SomethingUseful

class ChildClass2(BaseClass):
dataset = SomethingElse

It actually occured to me that I could use a @classmethod to do the
loading and take that out of the BaseClass constructor. What I have
makes more sense and eliminates the unecessary constructors.

ie.

class BaseClass:
@classmethod
def loadData(params):
#expensive load here

class ChildClass1(BaseClass):
dataset = BaseClass.loadData(params)

This is pretty much along the lines of what you suggested, thank you
for the hint in the right direction.

I realized that this still doesn't meet my needs exactly as I only
want the expensive dataset to be loaded if/when a class is actually
used (there are potentially many of these and only a few will be
used). I believe I have two options:

1) put each ChildClass in a separate file
2) in each ChildClass constructor put a check if the dataset has been
loaded yet, so that the first time an instance is created it can call
the BaseClass.loadData

for now I have chosen the second option, which is to change the child
classes to resemble

class ChildClass(BaseClass):

dataset = None

def __init__(self):
if BaseClass.dataset is None:
self(type).dataset = BaseClass.loadData(params)


I am still doing the self(type) access, but I like it more now that
I've taken it out of the BaseClass constructor.
 
S

Steven Bethard

James said:
It actually occured to me that I could use a @classmethod to do the
loading and take that out of the BaseClass constructor. What I have
makes more sense and eliminates the unecessary constructors.

ie.

class BaseClass:
@classmethod
def loadData(params):
#expensive load here

class ChildClass1(BaseClass):
dataset = BaseClass.loadData(params)

This is pretty much along the lines of what you suggested, thank you
for the hint in the right direction.

I realized that this still doesn't meet my needs exactly as I only
want the expensive dataset to be loaded if/when a class is actually
used (there are potentially many of these and only a few will be
used).

Seems like you want a lazy class attribute. How about something like::
.... def __init__(self, func):
.... self.func = func
.... def __get__(self, obj, cls=None):
.... value = self.func(cls)
.... setattr(cls, self.func.__name__, value)
.... return value
........ @LazyClassAttribute
.... def dataset(cls):
.... print 'calculating dataset'
.... return 'dataset(%s)' % cls.params
........ params = 'foo'
........ params = 'bar'
....calculating dataset
'dataset(foo)'calculating dataset
'dataset(bar)''dataset(bar)'

The idea is basically similar to the @classmethod approach except that
instead of @classmethod, we use a custom descriptor that calls the
method the first time it's accessed and then stores that value
afterwards. This means that instead of explicitly calling the
@classmethod, the method will be called whenever the attribute is first
accessed.

STeVe
 
J

James Turk

Seems like you want a lazy class attribute. How about something like::

... def __init__(self, func):
... self.func = func
... def __get__(self, obj, cls=None):
... value = self.func(cls)
... setattr(cls, self.func.__name__, value)
... return value
...
... @LazyClassAttribute
... def dataset(cls):
... print 'calculating dataset'
... return 'dataset(%s)' % cls.params
...
... params = 'foo'
...
... params = 'bar'
...
calculating dataset
'dataset(foo)'
calculating dataset
'dataset(bar)'
'dataset(bar)'

The idea is basically similar to the @classmethod approach except that
instead of @classmethod, we use a custom descriptor that calls the
method the first time it's accessed and then stores that value
afterwards. This means that instead of explicitly calling the
@classmethod, the method will be called whenever the attribute is first
accessed.

STeVe

This is a pretty interesting idea, I hadn't thought of using a
decorator to get this behavior. I'm evaluating it and will see if it
fits in with the rest of the system well, but it certainly is a unique
solution to this problem.
 

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,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top