Introdução aos Descritores Python

Gerencie o acesso a atributos com descritores Python

Saiba como criar e aplicar descritores em Python facilmente.

Alex Starostin, QA Engineer, IBM

Photo of Alexander StarostinAlex é engenheiro de controle de qualidade no laboratório de desenvolvimento da IBM Austrália. Ele projeta e desenvolve casos de teste em Python, realiza o controle de qualidade e oferece suporte para teste da equipe de desenvolvimento de dispositivos de segurança da IBM.



28/Mai/2014

Introdução

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.

Protocolo dos 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.


Criando descritores

É 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

  • get imprimirá Getting
  • delete imprimirá Deleting
  • set imprimirá 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ção AttributeError é 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.


Conclusão

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.

Recursos

Aprender

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

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre
ArticleID=825023
ArticleTitle=Introdução aos Descritores Python
publish-date=05282014