one-time initialization of class members

Discussion in 'Python' started by James Turk, Jun 13, 2007.

  1. James Turk

    James Turk Guest

    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.
     
    James Turk, Jun 13, 2007
    #1
    1. Advertising

  2. James Turk wrote:
    > 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
     
    Steven Bethard, Jun 13, 2007
    #2
    1. Advertising

  3. James Turk

    James Turk Guest

    On Jun 13, 6:54 pm, Steven Bethard <> wrote:
    > James Turk wrote:
    > > 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


    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.
     
    James Turk, Jun 14, 2007
    #3
  4. James Turk

    Larry Bates Guest

    James Turk wrote:
    > 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
     
    Larry Bates, Jun 14, 2007
    #4
  5. James Turk

    James Turk Guest

    On Jun 13, 8:00 pm, Larry Bates <> wrote:
    > James Turk wrote:
    > > 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


    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.
     
    James Turk, Jun 14, 2007
    #5
  6. On Wed, 13 Jun 2007 23:55:02 +0000, James Turk wrote:

    > On Jun 13, 6:54 pm, Steven Bethard <> wrote:
    >> James Turk wrote:
    >> > 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

    >
    > 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





    --
    Steven.
     
    Steven D'Aprano, Jun 14, 2007
    #6
  7. En Wed, 13 Jun 2007 22:03:50 -0300, Steven D'Aprano
    <> escribió:

    > On Wed, 13 Jun 2007 23:55:02 +0000, James Turk wrote:
    >>> James Turk wrote:
    >>>
    >>> > I have a situation where I have some class members that should only
    >>> be
    >>> > done once. Essentially my problem looks like this:
    >>>

    >> 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


    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>


    --
    Gabriel Genellina
     
    Gabriel Genellina, Jun 14, 2007
    #7
  8. James Turk

    James Turk Guest

    On Jun 13, 9:03 pm, Steven D'Aprano
    <> wrote:
    > On Wed, 13 Jun 2007 23:55:02 +0000, James Turk wrote:
    > > On Jun 13, 6:54 pm, Steven Bethard <> wrote:
    > >> James Turk wrote:
    > >> > 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

    >
    > > 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
    >
    > --
    > Steven.


    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.
     
    James Turk, Jun 14, 2007
    #8
  9. James Turk wrote:
    > 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::

    >>> class LazyClassAttribute(object):

    .... 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
    ....
    >>> class Base(object):

    .... @LazyClassAttribute
    .... def dataset(cls):
    .... print 'calculating dataset'
    .... return 'dataset(%s)' % cls.params
    ....
    >>> class Child1(Base):

    .... params = 'foo'
    ....
    >>> class Child2(Base):

    .... params = 'bar'
    ....
    >>> Child1.dataset

    calculating dataset
    'dataset(foo)'
    >>> Child1.dataset

    'dataset(foo)'
    >>> Child2.dataset

    calculating dataset
    'dataset(bar)'
    >>> Child2.dataset

    '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
     
    Steven Bethard, Jun 14, 2007
    #9
  10. James Turk

    James Turk Guest

    On Jun 13, 11:42 pm, Steven Bethard <> wrote:
    > James Turk wrote:
    > > 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::
    >
    > >>> class LazyClassAttribute(object):

    > ... 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
    > ...
    > >>> class Base(object):

    > ... @LazyClassAttribute
    > ... def dataset(cls):
    > ... print 'calculating dataset'
    > ... return 'dataset(%s)' % cls.params
    > ...
    > >>> class Child1(Base):

    > ... params = 'foo'
    > ...
    > >>> class Child2(Base):

    > ... params = 'bar'
    > ...
    > >>> Child1.dataset

    > calculating dataset
    > 'dataset(foo)'
    > >>> Child1.dataset

    > 'dataset(foo)'
    > >>> Child2.dataset

    > calculating dataset
    > 'dataset(bar)'
    > >>> Child2.dataset

    > '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.
     
    James Turk, Jun 14, 2007
    #10
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. CoolPint
    Replies:
    8
    Views:
    1,002
    Jeff Schwab
    Dec 14, 2003
  2. matro
    Replies:
    1
    Views:
    711
    Victor Bazarov
    Dec 26, 2003
  3. DanielBradley
    Replies:
    3
    Views:
    14,971
    Dave Moore
    Jun 2, 2004
  4. BigMan
    Replies:
    7
    Views:
    489
    Axter
    Mar 1, 2005
  5. hdixon
    Replies:
    3
    Views:
    653
    hdixon
    Jul 9, 2006
Loading...

Share This Page