Os descritores Python foram introduzidos no Python 2.2, junto com as novas classes de estilo, mas continuam sendo muito pouco usados. Eles são uma maneira de criar atributos gerenciados. Entre suas muitas vantagens, os atributos gerenciados são usados para proteger um atributo contra alterações ou para atualizar automaticamente os valores de um atributo dependente.
Os descritores ampliam o conhecimento sobre o Python e melhoram as qualificações de criação de código. Este artigo apresenta o protocolo de descritor e demonstra como criar e usar descritores.
O protocolo de descritor do Python é simplesmente uma maneira de especificar o que acontece quando uma referência a um atributo em um modelo é feita. Ele permite que um programador gerencie o acesso a atributos de forma rápida e eficiente:
- set
- get
- delete
Em outras linguagens de programação, os descritores são chamados de setter e getter, nos quais funções públicas são usadas para obter e definir uma variável privada. O Python não possui o conceito de variáveis privadas, e o protocolo de descritor pode ser considerado uma maneira de obter resultado semelhante nessa linguagem.
Em geral, um descritor é um atributo de objeto com um comportamento de ligação, cujo acesso a atributos é substituído por métodos no protocolo de descritor. Esses métodos são: __get__, __set__ e __delete__. Se um desses métodos for definido para um objeto, ele será considerado um descritor. Observe melhor esses métodos na Listagem 1.
Lista 1. Métodos de descritor
__get__(self, instance, owner) __set__(self, instance, value) __delete__(self, instance) |
No qual:
__get__ acessa o atributo. Ele retorna o valor do atributo, ou gera a exceção AttributeError se o atributo solicitado não está presente.
__set__ é chamado em uma operação de designação de atributo. Ele não retorna nada.
__delete__ controla uma operação de exclusão. Ele não retorna nada.
É importante observar que descritores são designados a uma classe e não a uma instância. Se a classe for modificada, o próprio descritor será substituído ou excluído e não terá seu código acionado.
Quando descritores são necessários
Considere um atributo email. É necessário verificar se o formato do email está correto antes de designar um valor ao atributo. Esse descritor permite que o email seja processado por meio de uma expressão regular e que seu formato seja validado antes de ser designado a um atributo.
Em muitos outros casos, os descritores de protocolo do Python controlam o acesso a atributos, como a proteção do atributo name.
É possível criar descritores de várias formas:
- Criando uma classe e substituindo qualquer um dos métodos de descritor:
__set__,__ get__e__delete__. Esse método é usado quando é necessário o mesmo descritor em várias classes e atributos diferentes (por exemplo, para validação de tipo). - Usando um tipo de propriedade, que é uma maneira mais simples e flexível de usar um descritor.
- Usando o poder dos decoradores de propriedade, que são uma combinação de método de tipo de propriedade e decoradores do Python.
Todos os exemplos abaixo são semelhantes do ponto de vista operacional. A diferença está na implementação.
Criando descritores usando métodos de classe
A Listagem 2 demonstra a simplicidade do controle de designação de atributos no Python.
Lista 2. Criando descritores usando métodos de classe
class Descriptor(object):
def __init__(self):
self._name = ''
def __get__(self, instance, owner):
print "Getting: %s" % self._name
return self._name
def __set__(self, instance, name):
print "Setting: %s" % name
self._name = name.title()
def __delete__(self, instance):
print "Deleting: %s" %self._name
del self._name
class Person(object):
name = Descriptor() |
Use esse código e veja a saída:
>>> user = Person() >>> user.name = 'john smith' Setting: john smith >>> user.name Getting: John Smith 'John Smith' >>> del user.name Deleting: John Smith |
Uma classe de descritor foi criada substituindo os métodos __set__(), __get__() e __delete__() da classe-pai de forma que
getimprimirá Gettingdeleteimprimirá Deletingsetimprimirá Setting
e altera o valor do atributo para o título (primeira letra em maiúsculas, as demais em minúscula) antes da designação. Isso é útil, por exemplo, ao armazenar e imprimir nomes.
A conversão para maiúsculas também pode ser movida para o método __get__(). O _value terá o valor original e será convertido a título na solicitação get.
Criando descritores usando tipo de propriedade
Embora o descritor especificado na Listagem 2 seja válido e funcional, outro método de criação é por meio do tipo de propriedade. Com property(), é fácil criar um descritor utilizável para qualquer atributo. A sintaxe para a criação de property() é property(fget=None, fset=None, fdel=None, doc=None) no qual:
- fget – método get do atributo
- fset – método set do atributo
- fdel – método delete do atributo
- doc – docstring
Reescreva o exemplo usando a propriedade, como na Listagem 3.
Lista 3. Criando descritores usando tipo de propriedade
class Person(object):
def __init__(self):
self._name = ''
def fget(self):
print "Getting: %s" % self._name
return self._name
def fset(self, value):
print "Setting: %s" % value
self._name = value.title()
def fdel(self):
print "Deleting: %s" %self._name
del self._name
name = property(fget, fset, fdel, "I'm the property.") |
Use esse código e veja a saída:
>>> user = Person() >>> user.name = 'john smith' Setting: john smith >>> user.name Getting: John Smith 'John Smith' >>> del user.name Deleting: John Smith |
O resultado é claramente o mesmo. Observe que os métodos fget, fset e fdel são opcionais, mas, se um deles não for especificado, a exceção AttributeError será gerada ao tentar realizar a respectiva operação. Por exemplo, uma propriedade name é declarada com None como fsete, em seguida, o desenvolvedor tenta designar valor ao atributo name. A exceçãoAttributeError é gerada.
Isso pode ser usado para definir atributos de leitura no sistema.
name = property(fget, None, fdel, "I'm the property") user.name = 'john smith' |
Saída:
Traceback (most recent call last): File stdin, line 21, in mоdule user.name = 'john smith' AttributeError: can't set attribute |
Criando descritores usando decoradores de propriedade
Descritores podem ser criados com decoradores do Python, como na Listagem 4. Um decorador do Python é uma alteração específica na sintaxe da linguagem que permite uma alteração mais conveniente de funções e métodos. Nesse caso, métodos de gerenciamento de atributos são alterados. Para saber mais sobre o aplicativo de decoradores do Python, consulte o artigo do developerWorks, Decorators make magic easy.
Lista 4. Criando descritores com decoradores de propriedade
class Person(object):
def __init__(self):
self._name = ''
@property
def name(self):
print "Getting: %s" % self._name
return self._name
@name.setter
def name(self, value):
print "Setting: %s" % value
self._name = value.title()
@name.deleter
def name(self):
print ">Deleting: %s" % self._name
del self._name |
Criando descritores no tempo de execução
Todos os exemplos anteriores funcionam com o atributo name. A limitação dessa abordagem é a necessidade de substituir separadamente __set__(),
__get__() e __delete__() para cada atributo. A Listagem 5 apresenta uma solução possível para quando um desenvolvedor deseja incluir atributos de propriedade no tempo de execução. A solução usa o tipo de propriedade para desenvolver um descritor de dados.
Lista 5. Criando descritores no tempo de execução
class Person(object):
def addProperty(self, attribute):
# create local setter and getter with a particular attribute name
getter = lambda self: self._getProperty(attribute)
setter = lambda self, value: self._setProperty(attribute, value)
# construct property attribute and add it to the class
setattr(self.__class__, attribute, property(fget=getter, \
fset=setter, \
doc="Auto-generated method"))
def _setProperty(self, attribute, value):
print "Setting: %s = %s" %(attribute, value)
setattr(self, '_' + attribute, value.title())
def _getProperty(self, attribute):
print "Getting: %s" %attribute
return getattr(self, '_' + attribute) |
Vamos experimentar com este código:
>>> user = Person()
>>> user.addProperty('name')
>>> user.addProperty('phone')
>>> user.name = 'john smith'
Setting: name = john smith
>>> user.phone = '12345'
Setting: phone = 12345
>>> user.name
Getting: name
'John Smith'
>>> user.__dict__
{'_phone': '12345', '_name': 'John Smith'} |
Esse código criou os atributos name e phone no tempo de execução. Eles podem ser acessados pelo nome correspondente, mas são armazenados no dicionário de namespace de objetos como _name e _phone, conforme especificado no método _setProperty. Basicamente, name e phone acessam os atributos _name e _phone internos.
Você pode apresentar uma dúvida em relação a um atributo _name no sistema quando o desenvolvedor tenta incluir o atributo de propriedade name. A resposta é que ele substituirá o atributo _name atual com o novo atributo de propriedade. Esse código permite controlar a forma como os atributos são tratados dentro de uma classe.
Os descritores do Python permitem um gerenciamento de atributos eficiente e flexível com novas classes de estilo. Em conjunto com decoradores, eles permitem uma programação elegante, possibilitando a criação de Setters e Getters e de atributos de leitura. Também permite executar validação de atributos mediante solicitação por valor ou por tipo. Descritores podem ser aplicados em muitas áreas, mas use-os com prudência para evitar o código desnecessariamente complexo que é resultado da substituição do comportamento normal de um objeto.
Aprender
- Guia do Python - Chamando Descritores: saiba como aplicar descritores nessa seção do guia do Python.
- Guia do Python - Implementando Descritores: explore a implementação de descritores nessa seção do guia do Python.
- Propriedades do Python: leia esta explicação sobre as propriedades do Python.
- Decorators make magic easy (David Mertz, developerWorks, dezembro de 2006): examine os decoradores do Python e seus recursos para metaprogramação.
- A área de Software Livre no developerWorks fornece muitas informações sobre ferramentas de software livre e de como utilizar tecnologias de software livre.
- Na zona Linux do developerWorks, encontre vários artigos de instruções e tutoriais, bem como downloads, fóruns de discussão e muitos outros recursos para desenvolvedores e administradores Linux.
- especializadas em artigos que abrangem diversas soluções baseada na web.
- Fique por dentro dos Eventos técnicos e webcasts do developerWorks com ênfase em uma série de produtos IBM e tópicos do segmento de mercado de TI.
- Participe de um briefing gratuito do developerWorks Live! para se informar sobre os produtos e ferramentas IBM, além das tendências do mercado de TI.
- Acompanhe as demos on demand do developerWorks , que abrangem desde demos de instalação e configuração de produtos para iniciantes até funcionalidades avançadas para desenvolvedores experientes.
- Siga o developerWorks no Twitter.
Obter produtos e tecnologias
- Avalie produtos
IBM da maneira que for melhor para você: faça download da versão de teste de um produto, avalie um produto online, use-o em um ambiente de nuvem ou passe algumas horas na SOA Sandbox para saber mais sobre como implementar arquitetura orientada a serviço (SOA) de maneira eficiente.
Discutir
- Confira blogs do developerWorks e participe da comunidade do developerWorks.
- Participe da comunidade do developerWorks. Entre em contato com outros usuários do developerWorks e explore os blogs, fóruns, grupos e wikis voltados para desenvolvedores.
