Skip to content Skip to sidebar Skip to footer

In Python, Count The Number Of Variables In A Class Or Prevent Adding New Class Variables

In python, is there a way to prevent adding new class variables after defining the object? For example: class foo: def __init__(self): self.a = 1 self.b = 2

Solution 1:

I suggest using __setattr__ to avoid the oddities of __slots__.

You always have to be careful when messing with __setattr__, since it takes care of setting all instance attributes, including those you set in __init__. Therefore it has to have some way of knowing when to allow the setting of an attribute, and when to deny it. In this solution I've designated a special attribute that controls whether new attributes are allowed or not:

classA(object):
    def__init__(self):
        self.a = 1
        self.b = 2
        self.c = 3
        self.freeze = Truedef__setattr__(self, attr, value):
        ifgetattr(self, "freeze", False) andnothasattr(self, attr):
            raise AttributeError("You shall not set attributes!")
        super(A, self).__setattr__(attr, value)

Testing:

a = A()
try:
    a.d = 89except AttributeError:
    print"It works!"else:
    print"It doesn't work."
a.c = 42print a.a
print a.c
a.freeze = False
a.d = 28
a.freeze = Trueprint a.d

Result:

It works!
1
42
28

Also see gnibblers answer that wraps this concept neatly up in a class decorator, so it doesn't clutter up the class definition and can be reused in several classes without duplicating code.


EDIT:

Coming back to this answer a year later, I realize a context manager might solve this problem even better. Here's a modified version of gnibbler's class decorator:

from contextlib import contextmanager

@contextmanagerdefdeclare_attributes(self):
    self._allow_declarations = Truetry:
        yieldfinally:
        self._allow_declarations = Falsedefrestrict_attributes(cls):
    cls.declare_attributes = declare_attributes
    def_setattr(self, attr, value):
        disallow_declarations = notgetattr(self, "_allow_declarations", False)
        if disallow_declarations and attr != "_allow_declarations":
            ifnothasattr(self, attr):
                raise AttributeError("You shall not set attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattrreturn cls

And here's how to use it:

@restrict_attributesclassA(object):
    def__init__(self):
        with self.declare_attributes():
            self.a = 1
            self.b = 2
            self.c = 3

So whenever you want to set new attributes, just use the with statement as above. It can also be done from outside the instance:

a = A()
try:
    a.d = 89except AttributeError:
    print"It works!"else:
    print"It doesn't work."
a.c = 42print a.a
print a.c
with a.declare_attributes():
    a.d = 28print a.d

Solution 2:

In python, is there a way to prevent adding new class variables after defining the object?

Yes. __slots__. But do carefully read the notes.

Solution 3:

How about a class decorator based on lazyr's answer

deffreeze(cls):
    _init = cls.__init__
    definit(self, *args, **kw):
        _init(self, *args, **kw)
        self.freeze = True
    cls.__init__ = init 

    def_setattr(self, attr, value):
        ifgetattr(self, "freeze", None) and (attr=="freeze"ornothasattr(self, attr)):
            raise AttributeError("You shall not set attributes!")
        super(cls, self).__setattr__(attr, value)
    cls.__setattr__ = _setattrreturn cls

@freezeclassfoo(object):
    def__init__(self):
        self.a = 1
        self.b = 2
        self.c = 3


bar = foo()
try:
    bar.d = 4except Exception, e:
    print"I want this to always print"

Solution 4:

  1. Preventing adding new attibutes using __slots__ class attribute:

    classfoo(object):
        __slots__ = ['a', 'b', 'c']
        def__init__(self):
            self.a = 1
            self.b = 2
            self.c = 3
    
    bar = foo()
    
    try:
        bar.d = 4except Exception as e:
        print(e,"I want this to always print")
    
  2. Counting attributes:

    print(len([attr for attr indir(bar) if attr[0] != '_' ]))
    

Solution 5:

use this to count no.of attributes of an instance:

>>> classfoo:
    def__init__(self):
        self.a = 1
        self.b = 2
        self.c = 3>>> bar=foo()
>>> bar.__dict__
{'a': 1, 'c': 3, 'b': 2}
>>> len(bar.__dict__)  #returns no. of attributes of bar3

Post a Comment for "In Python, Count The Number Of Variables In A Class Or Prevent Adding New Class Variables"