Uma das features que eu mais gosto em Python são as properties. Elas são possíveis graças aos descriptors, mas isso é assunto pra outro post. E na versão 2.6, as properties ganharam 3 atributos: getter, setter e deleter. Veja mais aqui

Primeiro, vou mostrar como elas sempre funcionaram. Ai vai um exemplo “dummy”, não levem em conta a funcionalidade em si (sou péssimo em exemplos):

class Conta(object):
    def __init__(self, saldo = 0.0):
        self.saldo = saldo

    def depositar(self, valor):
        if valor > 0:
            self.saldo += valor
            return True
        return False

Certo, uma classe Conta, onde eu posso depositar um valor, que incrementa meu ‘saldo’. Mas como ver o saldo? Os “Javeiros” de plantão perguntariam logo: “Onde está o getter do saldo?!”. E eu respondo: “Não tem :-)”. Simplesmente porque não precisa. Se você quiser ver o saldo, acesse o atributo direto:

>>> c = Conta(100.00)
>>> print c.saldo
100.00
>>> c.depositar(20.00)
>>> print c.saldo
120.00

Mas isso não fere o encapsulamento? Um dos princípios básico da OO!?! É aí que entra as properties. Em Python, atributos são acessados diretamente, só se você precisar fazer algo quando acessá-lo, ai você cria um getter, e transforma o atributo numa property. Vejamos como ficaria a classe Conta:

from datetime import datetime

class Conta(object):
    def __init__(self, saldo = 0.0):
        self._saldo = saldo
        self._ultimo_acesso = None

    def depositar(self, valor):
        if valor > 0:
            self._saldo += valor
            return True
        return False

    def _get_saldo(self):
        self._ultimo_acesso = datetime.now()
        return self._saldo

    saldo = property(_get_saldo)

Agora, ao acessar a ‘property’ saldo, estaremos chamando o método _get_saldo(). O cliente da classe continua com a mesma API. Veja que eu usei um underline (_) na frente dos atributos, isso em Python é uma convenção, pra informar que o atributo é “privado”, já que não existem atributos realmente privados.

>>> c = Conta(100.00)
>>> print c.saldo
100.00
>>> print c._ultimo_acesso
2008-12-28 14:30:05.598644

Ou seja, getters e setters? Somente quando precisar. Confesso que depois de conhecer essa feature, dá dor na vista ler códigos em Java, PHP, etc…com vários getX, getY, getZ somente com uma linha retornando o atributo equivalente.

Uma property é um descriptor que conecta funções ao acesso a um determinado atributo. Tem a seguinte assinatura:

property(fget=None, fset=None, fdel=None, doc=None)

Vamos criar um setter para a classe acima:

def _set_saldo(self, novo_saldo):
    if novo_saldo > 0:
        self._saldo = novo_saldo

# a property agora seria
saldo = property(_get_saldo, _set_saldo)

Aqui vai o código na íntegra, com alguns doctests: conta.py.

Sim, mas o que mudou no Python 2.6? Veja como eu poderia criar o getter e setter:

@property
def saldo(self):
    self._ultimo_acesso = datetime.now()
    return self._saldo

@saldo.setter
def saldo(self, novo_saldo):
    if novo_saldo > 0:
        self._saldo = novo_saldo

Aqui está o código moficado: conta2.py.

Referências: