How do I block access to parameter creation in a class?

3
import math

class Circulo():

    def __init__(self):
        super()
        self.__raio = None

    def get_perimetro(self):
        return 2 * math.pi * self.raio

    def get_area(self):
        return math.pi * self.raio ** 2

    @property
    def raio(self):
        return self.__raio

    @raio.setter
    def raio(self, x):
        self.__raio = x

I have the class above and I want to encapsulate the access, so that dynamic attributes are not possible in the instance.

ex:

c = Circulo()
c.raio = 2 # ok
c.lado = 2 # AttributeError

I tried to block the dynamic attributes with getattr and setattr , but I was not successful.

def __getattr__(self, item):
    if item in self.__dict__:
        return self.__dict__[item]
    else:
        raise AttributeError('Paramentro ou atributo "%s" inexistente.' % item)

def __setattr__(self, key, value):
    if key in self.__dict__:
        self.__dict__[key] = value
    else:
        raise AttributeError('Paramentro ou atributo "%s" inexistente.' % key)
    
asked by anonymous 17.07.2017 / 05:40

3 answers

4

What you want to do is clearly a function of __slots__ , see documentation : / p>

  

Without a __dict__ variable, instances can not be assigned new variables not listed in the __slots__ definition. Attempts to assign an unlisted variable name raises AttributeError .

That is, if you want only the __raio attribute to exist, just put it in the class:

__slots__ = ("__raio")

See the example:

import math

class Circulo():

    __slots__ = ("__raio")

    def __init__(self):
        super()
        self.__raio = None

    def get_perimetro(self):
        return 2 * math.pi * self.raio

    def get_area(self):
        return math.pi * self.raio ** 2

    @property
    def raio(self):
        return self.__raio

    @raio.setter
    def raio(self, x):
        self.__raio = x

c = Circulo()
c.raio = 2 # ok
c.lado = 2 # AttributeError
  

See working at Ideone .

The result will be:

Traceback (most recent call last):
  File "./prog.py", line 27, in <module>
AttributeError: 'Circulo' object has no attribute 'lado'

Remembering that since __raio belongs to the list defined in __slots__ , the exception is not raised in the class initializer when setting the instance attribute self.__raio = None . By not defining the list correctly, an exception can be raised within the class itself.

    
18.07.2017 / 21:47
3

Try this:

import math

class Circulo():
    def __init__(self):
        super()
        self.__raio = None

    def get_perimetro(self):
        return 2 * math.pi * self.raio

    def get_area(self):
        return math.pi * self.raio ** 2

    @property
    def raio(self):
        return self.__raio

    @raio.setter
    def raio(self, x):
        self.__raio = x

    def __setattr__(self, key, value):
        if not hasattr(self, key):
            raise TypeError("Não pode criar atributos para esta classe")
        object.__setattr__(self, key, value)

c = Circulo()
c.raio = 2 # ok
c.lado = 2 # AttributeError

See running on ideone . And at Coding Ground . Also I put GitHub for future reference .

    
17.07.2017 / 14:15
2

The end with the help of Anderson Woss, looks like this:

import math

class FormaGeometrica:
    __slots__ = ()
    def get_area(self):
        raise NotImplementedError(NotImplemented)
    def get_perimetro(self):
        raise NotImplementedError(NotImplemented)


class Circulo(FormaGeometrica):
    __slots__ = ("raio")

    def __init__(self):
        super()
        self.raio = None

    def get_perimetro(self):
        return 2 * math.pi * self.raio

    def get_area(self):
        return math.pi * self.raio ** 2


if __name__ == '__main__':
    c = Circulo()
    c.raio = 2  # ok
    c.get_area()
    c.get_perimetro()
    c.lado = 2  # AttributeError

Previously, in order to implement the class by restricting the dynamic creation of attributes and methods, I had to rewrite setattr , getattr and control the dict > of the class.

The final solution is like the code below. If anyone has suggestions for improvement, welcome .

from incolumepy.geometria.figura_geometrica import FiguraGeometrica
import math


class Circulo(FiguraGeometrica):
    '''
    >>> c = Circulo()
    >>> c.raio = 1
    >>> c.raio
    1
    >>> c.lado = 2
    Paramentro ou atributo "lado" inexistente.
    '''
    __dict__ = {'raio': None}

    def get_perimetro(self):
        return 2 * math.pi * self.raio

    def get_area(self):
        return math.pi * self.raio ** 2

    def __getattr__(self, item):
        if item not in self.__dict__:
            raise AttributeError('Paramentro ou atributo "%s" inexistente.' % item)
        return self.__dict__[item]

    def __setattr__(self, key, value):
        if key not in self.__dict__:
            raise AttributeError('Paramentro ou atributo "{}" inexistente.'.format(key))
        self.__dict__[key] = value


if __name__ == '__main__':
    import doctest
    doctest.testmod()
    
18.07.2017 / 20:37