Propriedades em Python

Em algumas situações, ao criamos uma classe, não desejamos que os atributos que compõem um objeto dessa classe sejam alterados sem prévia validação. Para isso, costumamos definir os atributos como privados e então escrever métodos get_NOME_ATRIBUTO() e set_NOME_ATRIBUTO(), onde são colocados os testes antes de modificar o objeto. Podemos usar properties em Python para tornar nosso código independente de montes de métodos get e set. Antes de vermos as properties, vamos dar uma passada rápida sobre atributos privados.

Atributos Privados em Python

Antes de mais nada, Python não possui um mecanismo explícito para que definamos um atributo como privado, ao contrário de outras linguagens como Java. O que existe é uma espécie de truque, onde o nome dos atributos privados é precedido por dois underscores (“__”). Veja o exemplo abaixo:

>>> class Ponto:
...     def __init__(self, x, y):
...         self.__x = x
...         self.__y = y
...
>>> p = Ponto(2,3)
>>> print p.__x
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
AttributeError: Ponto instance has no attribute '__x'

No código acima, criamos uma classe com dois atributos (__x e __y). Veja que ao tentar acessar o atributo __x de fora da classe, obtemos uma mensagem de erro, dizendo que tal atributo não existe. Isso mesmo, para fora da classe ele não é visível (ao menos não como __x). O que acontece, na realidade, é que o interpretador Python altera o nome dos atributos que se iniciam com “__”, colocando “_NomeClasse” na frente do nome do atributo. Por exemplo, o código acima ao ser interpretado pelo Python, tem  suas variáveis __x e __y transformadas em _Ponto__x e _Ponto__y. Veja:

>>> dir(p)
['_Ponto__x', '_Ponto__y', '__doc__', '__init__', '__module__']

Se tentarmos acessar os atributos através desses nomes, conseguimos acessar seus valores normalmente:

>>> print p._Ponto__x
2
>>> print p._Ponto__y
3

Ou seja, os atributos não são realmente privados. O que acontece é que o interpretador modifica os nomes deles.

Properties

Python oferece um mecanismo builtin para construção de propriedades para uma classe. Propriedades são elementos acessados externamente como se fossem atributos, mas que internamente (à classe), são manipulados por funções. Vamos a um exemplo:

class Ponto:

    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    def get_x(self):
        return self.__x
    def get_y(self):
        return self.__y

    def set_x(self, x):
        if x > 0:
            self.__x = x

    def set_y(self, y):
        if y > 0:
            self.__y = y

p = Ponto(2, 3)
print p.get_x()
p.set_x(10)

Cada acesso à variável __x deve ser feito através das funções get e set definidas para essa variável. Mas, também podemos fazer isso de modo que o acesso à variável (de fora da classe) seja mais “elegante”. Vamos montar uma property chamada x, de forma que qualquer acesso a p.x irá chamar as funções atribuídas à variável.

class Ponto(object):

    def __init__(self, x, y):
        self.__x = x
        self.__y = y

    def get_x(self):
        return self.__x

    def get_y(self):
        return self.__y

    def set_x(self, x):
        if x > 0:
            self.__x = x

    def set_y(self, y):
        if y > 0:
            self.__y = y

    x = property(fget=get_x, fset=set_x)

p = Ponto(2, 3)
p.x = -1000
print p.x

O resultado da execução do código acima irá mostrar na tela o número 2, que foi o valor atribuído a x na construção do objeto. Ao executar a penúltima linha (p.x = -1000), o método set_x() será chamado automaticamente, pois foi configurado como a função fset da propriedade x. Como o método set_x() não permite a alteração da variável interna __x caso o novo valor seja menor ou igual a zero, então esse valor não é alterado. Ao executar a última linha (print p.x), é chamado o método get_x(), configurado como o método fget na construção da propriedade. Ele irá retornar o valor de __x, que permanece ainda sendo o valor 2.

É importante observar a linha (x = property(get_x, set_x)). É ela que cria essa propriedade x que pode ser acessada externamente. Como parâmetros, passamos as duas funções que vão ser chamadas ao ser feita alguma operação sobre essa propriedade. Assim, de fora de nossa classe, os acessos são feitos à property x, quando na verdade estamos acessando internamente o atributo __x. Ou seja, para quem está utilizando nossa classe, instanciando objetos dela, x é como se fosse um atributo público, visível de fora da classe. Mas, isso não impede que sejam feitos acessos externos à classe às variáveis _Ponto__x e _Ponto__y sem passar por método algum.

A assinatura da função que cria a propriedade é:

property([fget[, fset[, fdel[, doc]]]])

Mais informações em: http://docs.python.org/library/functions.html#property

Anúncios

3 comentários sobre “Propriedades em Python

  1. Na Docs do Python, vi que tem como usar os decorators para fazer a mesma coisa que você usou na linha 21. Para um iniciante, seria melhor partir para os decorators +property ou ir acostumando a fazer como você fez na linha 21?

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s