DRY and static attribute for multiple classes.

Discussion in 'Python' started by Marc Aymerich, Feb 1, 2011.

  1. Hi all,
    I want to provide an encapsulated static attribute called _registry
    for several classes.

    I try to use inheritance in order to make it DRY: all classes inherit
    from a BaseClass that implements the _registry encapsulation. But with
    inheritance it doesn't work how I want, because a single instance of
    the _registry is shared between all of the inherited classes, and I
    want to have an independent _registry for every class.

    How can I do that without coping all the code in every class?

    Many Thanks!!
     
    Marc Aymerich, Feb 1, 2011
    #1
    1. Advertising

  2. Marc Aymerich

    Peter Otten Guest

    Marc Aymerich wrote:

    > Hi all,
    > I want to provide an encapsulated static attribute called _registry
    > for several classes.
    >
    > I try to use inheritance in order to make it DRY: all classes inherit
    > from a BaseClass that implements the _registry encapsulation. But with
    > inheritance it doesn't work how I want, because a single instance of
    > the _registry is shared between all of the inherited classes, and I
    > want to have an independent _registry for every class.
    >
    > How can I do that without coping all the code in every class?


    If you want to go fancy use a metaclass:

    >>> class Base(object):

    .... class __metaclass__(type):
    .... def __init__(self, *args):
    .... type.__init__(self, *args)
    .... self.per_class = []
    ....
    >>> class A(Base): pass

    ....
    >>> A().per_class is A().per_class

    True
    >>> class B(Base): pass

    ....
    >>> B().per_class is B().per_class

    True
    >>> A().per_class is B().per_class

    False
     
    Peter Otten, Feb 1, 2011
    #2
    1. Advertising

  3. On Feb 2, 12:11 am, Peter Otten <> wrote:
    > Marc Aymerich wrote:
    > > Hi all,
    > > I want to provide an encapsulated static attribute called _registry
    > > for several classes.

    >
    > > I try to use inheritance in order to make it DRY: all classes inherit
    > > from a BaseClass that implements the _registry encapsulation. But with
    > > inheritance it doesn't work how I want, because a single instance of
    > > the _registry is shared between all of the inherited classes, and I
    > > want to have an independent _registry for every class.

    >
    > > How can I do that without coping all the code in every class?

    >
    > If you want to go fancy use a metaclass:
    >
    > >>> class Base(object):

    >
    > ...     class __metaclass__(type):
    > ...             def __init__(self, *args):
    > ...                     type.__init__(self, *args)
    > ...                     self.per_class = []



    Many thanks Peter!!
     
    Marc Aymerich, Feb 2, 2011
    #3
  4. Marc Aymerich

    Peter Otten Guest

    Marc Aymerich wrote:

    > On Feb 2, 12:11 am, Peter Otten <> wrote:
    >> Marc Aymerich wrote:
    >> > Hi all,
    >> > I want to provide an encapsulated static attribute called _registry
    >> > for several classes.

    >>
    >> > I try to use inheritance in order to make it DRY: all classes inherit
    >> > from a BaseClass that implements the _registry encapsulation. But with
    >> > inheritance it doesn't work how I want, because a single instance of
    >> > the _registry is shared between all of the inherited classes, and I
    >> > want to have an independent _registry for every class.

    >>
    >> > How can I do that without coping all the code in every class?

    >>
    >> If you want to go fancy use a metaclass:
    >>
    >> >>> class Base(object):

    >>
    >> ... class __metaclass__(type):
    >> ... def __init__(self, *args):
    >> ... type.__init__(self, *args)
    >> ... self.per_class = []

    >
    >
    > Many thanks Peter!!


    Here's a variant that doesn't rely on the metaclass:

    class Base(object):
    registries = {}
    @property
    def per_class(self):
    cls = type(self)
    try:
    return self.registries[cls]
    except KeyError:
    result = self.registries[cls] = []
    return result

    class A(Base): pass
    class B(Base): pass

    assert A().per_class is A().per_class
    assert B().per_class is B().per_class
    assert A().per_class is not B().per_class
     
    Peter Otten, Feb 2, 2011
    #4
  5. On Feb 2, 12:11 am, Peter Otten <> wrote:
    > Marc Aymerich wrote:
    > > Hi all,
    > > I want to provide an encapsulated static attribute called _registry
    > > for several classes.

    >
    > > I try to use inheritance in order to make it DRY: all classes inherit
    > > from a BaseClass that implements the _registry encapsulation. But with
    > > inheritance it doesn't work how I want, because a single instance of
    > > the _registry is shared between all of the inherited classes, and I
    > > want to have an independent _registry for every class.

    >
    > > How can I do that without coping all the code in every class?

    >
    > If you want to go fancy use a metaclass:
    >
    > >>> class Base(object):

    >
    > ...     class __metaclass__(type):
    > ...             def __init__(self, *args):
    > ...                     type.__init__(self, *args)
    > ...                     self.per_class = []
    > ...>>> class A(Base): pass
    > ...
    > >>> A().per_class is A().per_class

    > True
    > >>> class B(Base): pass

    > ...
    > >>> B().per_class is B().per_class

    > True
    > >>> A().per_class is B().per_class

    >
    > False



    Hi!,
    Unfortunately per_class attribute losses the "independence" when I try
    to mix it with django models.Model .

    from django.db import models
    class Plugin(models.base.ModelBase):
    class __metaclass__(type):
    def __init__(self, *args):
    type.__init__(self, *args)
    self.per_class = []

    class BaseService(models.Model):
    class Meta:
    abstract = True

    __metaclass__ = Plugin

    class VirtualHost(BaseService):
    name = models.CharField(max_length=10)

    class SystemUser(BaseService):
    name = models.CharField(max_length=10)


    >>> VirtualHost.per_class is SystemUser.per_class

    True

    What am I doing wrong?

    Thanks again!
     
    Marc Aymerich, Feb 2, 2011
    #5
  6. On Feb 2, 12:18 pm, Peter Otten <> wrote:
    > Marc Aymerich wrote:
    > > On Feb 2, 12:11 am, Peter Otten <> wrote:
    > >> Marc Aymerich wrote:
    > >> > Hi all,
    > >> > I want to provide an encapsulated static attribute called _registry
    > >> > for several classes.

    >
    > >> > I try to use inheritance in order to make it DRY: all classes inherit
    > >> > from a BaseClass that implements the _registry encapsulation. But with
    > >> > inheritance it doesn't work how I want, because a single instance of
    > >> > the _registry is shared between all of the inherited classes, and I
    > >> > want to have an independent _registry for every class.

    >
    > >> > How can I do that without coping all the code in every class?

    >
    > >> If you want to go fancy use a metaclass:

    >
    > >> >>> class Base(object):

    >
    > >> ...     class __metaclass__(type):
    > >> ...             def __init__(self, *args):
    > >> ...                     type.__init__(self, *args)
    > >> ...                     self.per_class = []

    >
    > > Many thanks Peter!!

    >
    > Here's a variant that doesn't rely on the metaclass:
    >
    > class Base(object):
    >     registries = {}
    >     @property
    >     def per_class(self):
    >         cls = type(self)
    >         try:
    >             return self.registries[cls]
    >         except KeyError:
    >             result = self.registries[cls] = []
    >             return result
    >
    > class A(Base): pass
    > class B(Base): pass
    >
    > assert A().per_class is A().per_class
    > assert B().per_class is B().per_class
    > assert A().per_class is not B().per_class


    Wow, many many thanks Peter, this works perfectly for me :)
     
    Marc Aymerich, Feb 2, 2011
    #6
  7. Notice that Peter's approach also works without inheritance:

    registries = {}

    @property
    def per_class(self):
    cls = type(self)
    try:
    return registries[cls]
    except KeyError:
    result = registries[cls] = []
    return result

    class A(object): per_class=per_class
    class B(object): per_class=per_class
    assert A().per_class is A().per_class
    assert B().per_class is B().per_class
    assert A().per_class is not B().per_class

    (you can also put the per_class property in a module and import it
    with
    class A(object): from module import per_class).

    There is no need to use inheritance if you only need one method.
    Notice also that if you
    are working with a complex third party framework (say Django) that may
    use metaclasses
    or strange tricks (such as __slots__) the safer way is to avoid both
    inheritance and metaclasses.
    HTH,

    Michele
     
    Michele Simionato, Feb 2, 2011
    #7
  8. On Feb 2, 5:58 pm, Michele Simionato <>
    wrote:
    > Notice that Peter's approach also works without inheritance:
    >
    > registries = {}
    >
    > @property
    > def per_class(self):
    >    cls = type(self)
    >    try:
    >       return registries[cls]
    >    except KeyError:
    >       result = registries[cls] = []
    >       return result
    >
    > class A(object): per_class=per_class
    > class B(object): per_class=per_class
    > assert A().per_class is A().per_class
    > assert B().per_class is B().per_class
    > assert A().per_class is not B().per_class
    >
    > (you can also put the per_class property in a module and import it
    > with
    > class A(object): from module import per_class).
    >
    > There is no need to use inheritance if you only need one method.
    > Notice also that if you
    > are working with a complex third party framework (say Django) that may
    > use metaclasses
    > or strange tricks (such as __slots__) the safer way is to avoid both
    > inheritance and metaclasses.
    > HTH,
    >
    >  Michele


    Hi Michele, thanks for your contributions. Actually the Petter
    proposal it's working for me (AFAIK). I think because my base class is
    an abstract class. In particular my adaptation of his proposal (with
    the rest of the current BaseService class) is:


    from django.db import models
    from django.contrib.contenttypes import generic
    class BaseService(models.Model):

    class Meta:
    abstract = True

    _service_registry = []
    _plugin_registry = {}

    @classmethod
    def plugin_register(cls, str):
    # Per subclass register
    try: cls._plugin_registry[cls].append(str)
    except KeyError: cls._plugin_registry[cls] = []

    @classmethod
    def get_plugin_registred(cls):
    # Per subclass register
    try:
    return cls._plugin_registry[cls]
    except KeyError:
    result = cls._plugin_registry[cls] = []
    return result

    @property
    def extentions_attributes(self):
    attributes = []
    for model in _plugin_registry:
    for attribute in model.get_attributes(self):
    attributes.append(attribute[0])
    return attributes

    @classmethod
    def service_register(cls):
    cls._service_registry.append(cls)

    @classmethod
    def get_service_registred(cls):
    return cls._service_registry

    @property
    def attributes(self):
    return []


    IMHO Django handles very well inheritance, and subclassing is their
    major method in order to use the framework. I completely agree with
    metaclasses should be avoided when you're working with frameworks :)

    BTW, is there any alternative to the inheritance pattern in order to
    provide this set of methods for multiple classes? I'm pretty new in
    OOP :)

    Thanks!
     
    Marc Aymerich, Feb 2, 2011
    #8
  9. Marc Aymerich

    Peter Otten Guest

    Marc Aymerich wrote:

    > On Feb 2, 12:11 am, Peter Otten <> wrote:
    >> Marc Aymerich wrote:


    > Hi!,
    > Unfortunately per_class attribute losses the "independence" when I try
    > to mix it with django models.Model .
    >
    > from django.db import models
    > class Plugin(models.base.ModelBase):
    > class __metaclass__(type):
    > def __init__(self, *args):
    > type.__init__(self, *args)
    > self.per_class = []
    >
    > class BaseService(models.Model):
    > class Meta:
    > abstract = True
    >
    > __metaclass__ = Plugin
    >
    > class VirtualHost(BaseService):
    > name = models.CharField(max_length=10)
    >
    > class SystemUser(BaseService):
    > name = models.CharField(max_length=10)
    >
    >
    >>>> VirtualHost.per_class is SystemUser.per_class

    > True
    >
    > What am I doing wrong?


    I'm surprised that you are seeing the per_class-attribute at all as you are
    defining it in the metaclass of the metaclass, as far as I can tell.
    I think the following should work:

    from django.db import models

    class Plugin(models.base.ModelBase):
    def __init__(self, *args):
    super(Plugin, self).__init__(*args)
    self.per_class = []

    class BaseService(models.Model):
    class Meta:
    abstract = True

    __metaclass__ = Plugin

    class VirtualHost(BaseService):
    name = models.CharField(max_length=10)

    class SystemUser(BaseService):
    name = models.CharField(max_length=10)

    assert VirtualHost.per_class is not SystemUser.per_class

    But I have never worked with Django, and the approach based on dictionary
    lookup is less likely to interfere with the dark corners of the framework.

    Peter
     
    Peter Otten, Feb 3, 2011
    #9
  10. On Feb 3, 10:24 am, Peter Otten <> wrote:
    > Marc Aymerich wrote:
    > > On Feb 2, 12:11 am, Peter Otten <> wrote:
    > >> Marc Aymerich wrote:

    > > Hi!,
    > > Unfortunately per_class attribute losses the "independence" when I try
    > > to mix it with django models.Model .

    >
    > > from django.db import models
    > > class Plugin(models.base.ModelBase):
    > >     class __metaclass__(type):
    > >         def __init__(self, *args):
    > >             type.__init__(self, *args)
    > >             self.per_class = []

    >
    > > class BaseService(models.Model):
    > >     class Meta:
    > >         abstract = True

    >
    > >     __metaclass__ = Plugin

    >
    > > class VirtualHost(BaseService):
    > >     name = models.CharField(max_length=10)

    >
    > > class SystemUser(BaseService):
    > >     name = models.CharField(max_length=10)

    >
    > >>>> VirtualHost.per_class is SystemUser.per_class

    > > True

    >
    > > What am I doing wrong?

    >
    > I'm surprised that you are seeing the per_class-attribute at all as you are
    > defining it in the metaclass of the metaclass, as far as I can tell.
    > I think the following should work:
    >
    > from django.db import models
    >
    > class Plugin(models.base.ModelBase):
    >     def __init__(self, *args):
    >         super(Plugin, self).__init__(*args)
    >         self.per_class = []
    >
    > class BaseService(models.Model):
    >     class Meta:
    >         abstract = True
    >
    >     __metaclass__ = Plugin
    >
    > class VirtualHost(BaseService):
    >     name = models.CharField(max_length=10)
    >
    > class SystemUser(BaseService):
    >     name = models.CharField(max_length=10)
    >
    > assert VirtualHost.per_class is not SystemUser.per_class
    >
    > But I have never worked with Django, and the approach based on dictionary
    > lookup is less likely to interfere with the dark corners of the framework..
    >
    > Peter


    Wow Peter, thanks for the correction, I've never used a metaclass
    before :) With your correction seems that it works perfectly on
    djando

    >>> VirtualHost._plugin_registry.append('0000000')
    >>> VirtualHost._plugin_registry

    ['0000000']
    >>> SystemUser._plugin_registry

    []
    >>>
     
    Marc Aymerich, Feb 3, 2011
    #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. yoda
    Replies:
    2
    Views:
    298
  2. egbert

    DRY and class variables

    egbert, Sep 8, 2011, in forum: Python
    Replies:
    0
    Views:
    141
    egbert
    Sep 8, 2011
  3. Martin DeMello

    Rubyforge, RAA and DRY

    Martin DeMello, Mar 3, 2005, in forum: Ruby
    Replies:
    1
    Views:
    89
    Daniel Berger
    Mar 3, 2005
  4. bwv549
    Replies:
    1
    Views:
    107
  5. Becca Girl
    Replies:
    0
    Views:
    112
    Becca Girl
    Jul 27, 2007
Loading...

Share This Page