DRY and static attribute for multiple classes.

M

Marc Aymerich

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

Peter Otten

Marc said:
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 __metaclass__(type):
.... def __init__(self, *args):
.... type.__init__(self, *args)
.... self.per_class = []
....False
 
M

Marc Aymerich

Marc said:
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 __metaclass__(type):
...             def __init__(self, *args):
...                     type.__init__(self, *args)
...                     self.per_class = []


Many thanks Peter!!
 
P

Peter Otten

Marc said:
Marc said:
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
 
M

Marc Aymerich

Marc said:
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 __metaclass__(type):
...             def __init__(self, *args):
...                     type.__init__(self, *args)
...                     self.per_class = []
...>>> class A(Base): pass
...
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)

True

What am I doing wrong?

Thanks again!
 
M

Marc Aymerich

Marc said:
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 :)
 
M

Michele Simionato

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
 
M

Marc Aymerich

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

Peter Otten

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)

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
 
M

Marc Aymerich

Marc said:
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)

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

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,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top