O artigo anterior artigo anterior sobre o Python versão 3—também conhecido como Python 3000ou Py3K— abrange algumas mudanças simples no Python,
que romperão com as questões de compatibilidade com versões anteriores, como a nova função print() no tipo de dados bytes e alterações no tipo de string. Esse artigo, Parte 2, explora alguns dos tópicos mais avançados, como classes abstratas de base (ABCs), metaclasses, decoradores e anotações de funções, suporte a número inteiro literal, a hierarquia de tipo numérico e alterações em exceções lançadas e capturadas, a maioria também rompendo com as questões de compatibilidade à versão 2x anterior.
Nas versões anteriores do Python, as transformações em um método tinham de ser feitas após a definição de tal método. Nos métodos maiores, esse requisito mantinha um importante componente de definição longe da definição da interface externa fornecida no Python Enhancement Proposal (PEP) 318 (consulte Recursos para obter um link). Este trecho mostra um exemplo desse requisito de transformação:
Lista 1. Transformações de Métodos no Python Antes da Versão 3
def myMethod(self):
# efetuar alguma ação
myMethod = transformMethod(myMethod)
|
Para tornar situações como esta mais legíveis e evitar a reutilização do mesmo nome de método várias vezes, os decoradores de métodos foram introduzidos na versão 2.4 do Python.
Decoradores são métodos que modificam outros métodos e retornam um método ou outro objeto que pode ser chamado. Eles são denotados com um símbolo "arroba"
(@) antes do nome do decorador—uma sintaxe semelhante às anotações Java™. A Lista 2 mostra decoradores em ação.
Lista 2. Um Método do Decorador
@transformMethod
def myMethod(self):
# efetuar alguma ação
|
Decoradores são açúcares sintáticos puros—ou (de acordo com a Wikipedia) "inclusões na sintaxe de uma linguagem de computador que não afetam sua funcionalidade, mas a torna 'mais doce' para uso." Um uso comum dos decoradores é a anotação dos métodos estáticos. As Listagens 1 e 2 são equivalentes, mas a Lista 2 é mais fácil de ler.
Você define um decorador como qualquer outro método:
def mod(method):
method.__name__ = "John"
return method
@mod
def modMe():
pass
print(modMe.__name__)
|
Além disso, o Python 3 agora suporta decoradores não apenas para métodos, mas também para classes. Dessa forma, em vez de usar:
class myClass:
pass
myClass = doSomethingOrNotWithClass(myClass)
|
é possível usar o seguinte:
@doSomethingOrNotWithClass
class myClass:
pass
|
Metaclasses são classes cujas instâncias são outras classes. O Python 3
manteve o tipo de metaclasse, usado para criar outras metaclasses ou criar dinamicamente classes em tempo de execução. Ainda é válido usar a sintaxe:
>>>aClass = type('className',
(object,),
{'magicMethod': lambda cls : print("blah blah")})
|
que aceita como seu argumento uma cadeia como o nome da classe, um conjunto de objetos herdados (que pode ser um conjunto vazio) e um dicionário (que também pode ser vazio) contendo métodos que podem ser inclusos. É claro que também é possível herdar de tipos e criar sua própria metaclasses:
class meta(type):
def __new__(cls, className, baseClasses, dictOfMethods):
return type.__new__(cls, className, baseClasses, dictOfMethods)
|
Nota: Se os dois últimos exemplos ainda não fazem sentido, recomendamos que você leia a série sobre metaclasses de David Mertz e Michele Simionato. Consulte Recursos para obter um link.
Observe que agora, em uma definição de classe, argumentos com palavra-chave são permitidos após a lista de classes base—no geral,
class Foo(*bases, **kwds): pass. A metaclasse é transmitida na definição de classe, usando, de forma conveniente, o argumento com palavra-chave metaclass. Por exemplo:
>>>class aClass(baseClass1, baseClass2, metaclass = aMetaClass): pass |
A antiga sintaxe das metaclasses era designar a metaclasse para o atributo__metaclass__:
class Test(object):
__metaclass__ = type
|
Além disso, um novo atributo—__prepare__—foi incluído. Use tal atributo para criar o dicionário para o novo namespaceda classe. Ele é chamado antes da avaliação do corpo da classe, como mostra a Lista 3.
Lista 3. Uma Metaclasse Simples que Usa o Atributo __prepare__
def meth():
print("Calling method")
class MyMeta(type):
@classmethod
def __prepare__(cls, name, baseClasses):
return {'meth':meth}
def __new__(cls, name, baseClasses, classdict):
return type.__new__(cls, name, baseClasses, classdict)
class Test(metaclass = MyMeta):
def __init__(self):
pass
attr = 'an attribute'
t = Test()
print(t.attr)
|
Um exemplo mais interessante, retirado literalmente do PEP 3115 e mostrado na Lista 4, cria uma metaclasse com uma lista de nomes de seus métodos, enquanto mantém a ordem na qual os métodos de classes foram declarados.
Lista 4. Uma Metaclasse que Preserva a Ordem dos Membros da Classe
# O dicionário customizado
class member_table(dict):
def __init__(self):
self.member_names = []
def __setitem__(self, key, value):
# se a chave ainda não estiver definida, inclua-a na
# lista de chaves.
se a chave não for própria:
self.member_names.append(key)
# Chame a superclasse
dict.__setitem__(self, key, value)
# A metaclasse
class OrderedClass(type):
# A função de preparação
@classmethod
def __prepare__(metacls, name, bases): # Nenhuma palavra-chave neste caso
return member_table()
# A inovação da metaclasse
def __new__(cls, name, bases, classdict):
# Observe que substituímos classdict por um
# dict comum antes de transmiti-lo para a superclasse, assim
# o registro de nomes dos membros não continua depois que a classe
# for criada.
result = type.__new__(cls, name, bases, dict(classdict))
result.member_names = classdict.member_names
return result
|
Há alguns motivos para as alterações nas metaclasses. Os objetos armazenam seus métodos em um dicionário, sem nenhuma ordem. No entanto, há casos nos quais a preservação da ordem dos membros de classe declarados seria útil. Isso é feito "envolvendo" a metaclasse antes da criação da classe, quando as informações ainda estão disponíveis— útil, por exemplo, na criação de estruturas C. Esse novo mecanismo permite que outras possibilidades interessantes sejam implementadas no futuro, como símbolos de inserção no corpo do namespacede classe criado durante a criação da classe e mais referências de símbolos. O PEP 3115 também menciona que há motivos estéticos para a alteração da sintaxe, mas este é um debate que não pode ser resolvido objetivamente. (Consulte Recursos para obter um link para o PEP 3115).
Como foi mencionado no artigo anterior desta série, ABCs são classes que não podem ser instanciadas. Os programadores de linguagens Java ou C++ devem se familiarizar com este conceito. O Python 3 inclui uma nova estrutura—abc—que fornece suporte para trabalhar com ABCs.
O módulo abc tem uma metaclasse (ABCMeta) e decoradores (@abstractmethod e @abstractproperty). Se um ABC tiver um
@abstractmethod ou um
@abstractproperty, ele não poderá ser instanciado,
mas deverá ser substituído em uma subclasse. Por exemplo, o código:
>>>from abc import * >>>class C(metaclass = ABCMeta): pass >>>c = C() |
está correto, mas não faça o seguinte:
>>>from abc import * >>>class C(metaclass = ABCMeta): ... @abstractmethod ... def absMethod(self): ... pass >>>c = C() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class C with abstract methods absMethod |
Ou melhor, use o código:
>>>class B(C):
... def absMethod(self):
... print("Now a concrete method")
>>>b = B()
>>>b.absMethod()
Now a concrete method
|
A clase ABCMeta sobrescreve os atributos
__instancecheck__ e __subclasscheck__, providenciando uma maneira de sobrecarregar a função integrada isinstance()
e issubclass(). Para incluir uma subclasse virtual em seu ABC, use o método register() que
ABCMeta fornece. O exemplo simples:
>>>class TestABC(metaclass=ABCMeta): pass >>>TestABC.register(list) >>>TestABC.__instancecheck__([]) True |
equivale ao uso de
isinstance(list, TestABC). Você deve ter notado que o Python 3 usa __instancecheck__
em vez de __issubclass__ e __subclasscheck__ no lugar de
__issubclass__, o que parece mais natural. O motivo é que a reversão dos argumentos
isinstance(subclass, superclass), por exemplo,
para superclass.__isinstance__(subclass) pode causar confusão. Assim, a sintaxe
superclass.__instancecheck__(subclass)
vence.
No módulo collections, é possível usar vários ABCs para
testar se uma classe fornece uma interface específica:
>>>from collections import Iterable >>>issubclass(list, Iterable) True |
A Tabela 1 mostra os ABCs do framework de coletas.
Tabela 1. Os ABCs do Framework de Coletas
| ABC | Herda |
|---|---|
Contêiner
| |
Hashable
| |
Iterable
| |
Agente Iterativo
|
Iterable
|
Tamanho
| |
Pode ser chamado
| |
Sequência
| Tamanho,
Iterable,
Contêiner
|
Sequencia_mutável
|
Sequência
|
Configurar
| Tamanho,
Iterable,
Contêiner
|
Configuração_mutável
|
Configurar
|
Mapeamento
| Tamanho,
Iterable,
Contêiner
|
Mapeamento_mutável
|
Mapeamento
|
Visualização_de_mapeamento
|
Tamanho
|
Visualização_de_chaves
| Visualização_de_mapeamento,
Configurar |
Visualização_de_itens
| Visualização_de_mapeamento,
Configurar |
Visualização_de_valores
|
Visualização_de_mapeamento
|
Agora o Python 3 suporta uma hierarquia de tipo de ABCs que representa classes numéricas. Esses ABCs residem nos módulos numbers e incluem Number,
Complex, Real,
Rational e Integral. A Figura 1 mostra a hierarquia de números.
É possível, obviamente, usá-la para implementar seu próprio tipo numérico ou outro ABC numérico.
Figura 1. A Hierarquia Numérica
Um novo módulo, fractions, implementa o
ABC numéricoRational. Esse módulo fornece suporte para números aritméticos racionais. Se você usa
dir(fractions.Fraction), notará que ele tem atributos como imag,
real e __complex__. Seguindo a torre numérica, isso ocorre porque Rationals são herdados de
Reals, que são herdados de
Complex.
Lançando e capturando exceções
No Python 3, as cláusulas de except foram alteradas para lidarem com um problema de ambiguidade sintática. Anteriormente, no Python versão 2.5,
uma construção try . . . except como:
>>>try:
... x = float('not a number')
... except (ValueError, NameError):
... print "can't convert type"
|
podia ser escrita incorretamente como:
>>> try:
... x = float('not a number')
... except ValueError, NameError:
... print "can't convert type"
|
O problema com a forma anterior é que a exceção
ValueError nunca será capturada porque o intérprete irá capturar
ValueError e ligar o objeto de exceção ao nome NameError. Isso pode ser observado no próximo exemplo:
>>> try:
... x = float('blah')
... except ValueError, NameError:
... print "NameError is ", NameError
...
NameError is invalid literal for float(): not a number
|
Assim, para lidar com as ambiguidades, a vírgula (,)
é substituída pela palavra-chave as quando você deseja ligar o objeto de exceção a outro nome. Se deseja obter várias exceções, os parênteses (()) são necessários. O código na Lista 5 mostra dois exemplos legítimos no Python 3.
Lista 5. Manipulação de Exceções no Python 3
# ligar o objeto ValueError à exceção de nome local
try:
x = float('blah')
except ValueError as ex:
print("value exception occurred ", ex)
# obter duas exceções diferentes simultaneamente
try:
x = float('blah')
except (ValueError, NameError):
print("caught both types of exceptions")
|
Outra alteração na manipulação de exceção é o encadeamento de exceções—implícito ou explícito. A Lista 6 mostra um exemplo de uma cadeia de exceção implícita.
Lista 6. Uma Cadeia de Exceção Implícita no Python 3
def divide(a, b):
try:
print(a/b)
except Exception as exc:
def log(exc):
fid = open('logfile.txt') # missing 'w'
print(exc, file=fid)
fid.close()
log(exc)
divide(1,0)
|
O método divide() tenta executar uma divisão por zero e levanta uma exceção:
ZeroDivisionError. Mas, na cláusula de exceções, dentro do método log() aninhado,
print(exc, file=fid) tenta gravar em um arquivo que não está aberto para gravação. O Python 3 levanta as exceções mostradas na Lista 7.
Lista 7. A Análise Retrospectiva do Exemplo de Exceção em Cadeia
Traceback (most recent call last):
File "chainExceptionExample1.py", line 3, in divide
print(a/b)
ZeroDivisionError: int division or modulo by zero
Durante a manipulação da exceção acima, outra exceção ocorreu:
Traceback (most recent call last):
File "chainExceptionExample1.py", line 12, in <module>
divide(1,0)
File "chainExceptionExample1.py", line 10, in divide
log(exc)
File "chainExceptionExample1.py", line 7, in log
print(exc, file=fid)
File "/opt/python3.0/lib/python3.0/io.py", line 1492, in write
self.buffer.write(b)
File "/opt/python3.0/lib/python3.0/io.py", line 696, in write
self._unsupported("write")
File "/opt/python3.0/lib/python3.0/io.py", line 322, in _unsupported
(self.__class__.__name__, name))
io.UnsupportedOperation: BufferedReader.write() not supported
|
Observe que as duas exceções foram manipuladas. Nas versões anteriores do Python,
ZeroDivisionError seria perdido. Como isso acontecia? Um atributo __context__ como ZeroDivisionError agora faz parte de todos os objetos de exceção. Neste caso, o atributo
__context__ de
IOError levantou a "retenção" de
ZeroDivisionError no atributo
__context__.
Além do atributo __context__, objetos de exceção têm um atributo __cause__, que sempre é inicializado como None. O propósito desse atributo é fornecer uma forma explícita de registrar a causa de uma exceção. O atributo __cause__ é definido com a seguinte sintaxe:
>>> raise EXCEPTION from CAUSE |
Isso é exatamente igual à codificação:
>>>exception = EXCEPTION >>>exception.__cause__ = CAUSE >>>raise exception |
mas muito mais elegante. O exemplo na Lista 8 mostra o encadeamento de exceções explícito.
Lista 8. Encadeamento de Exceções Explícitos no Python 3
class CustomError(Exception):
pass
try:
fid = open("aFile.txt") # 'w' ausente novamente
print("blah blah blah", file=fid)
except IOError as exc:
raise CustomError('something went wrong') from exc
|
Como no exemplo anterior, a função print() levanta uma exceção, pois o arquivo não estava aberto para gravação. A Lista 9 mostra o traceback
Lista 9. A Análise Retrospectiva da Exceção
Traceback (most recent call last):
File "chainExceptionExample2.py", line 5, in <module>
fid = open("aFile.txt")
File "/opt/python3.0/lib/python3.0/io.py", line 278, in __new__
return open(*args, **kwargs)
File "/opt/python3.0/lib/python3.0/io.py", line 222, in open
closefd)
File "/opt/python3.0/lib/python3.0/io.py", line 615, in __init__
_fileio._FileIO.__init__(self, name, mode, closefd)
IOError: [Errno 2] No such file or directory: 'aFile.txt'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "chainExceptionExample2.py", line 8, in <modulei>
raise CustomError('something went wrong') from exc
__main__.CustomError: something went wrong
|
Observe que, no traceback, a linha "The above exception was the direct cause
of the following exception", que é seguida por outra análise retrospectiva, resulta em CustomError, "something went
wrong."
Finalmente, outro atributo ainda incluído nos objetos de exceção é o atributo
__traceback__. Se uma exceção obtida não tiver seu atributo __traceback__, a nova análise retrospectiva será definida. Este é um exemplo simples:
from traceback import format_tb
try:
1/0
except Exception as exc:
print(format_tb(exc.__traceback__)[0])
|
Observe que format_tb retorna uma lista e há apenas uma exceção nesta lista.
Suporte a número inteiro literal e sintaxe
O Python suporta strings de números inteiros de diferentes
bases—octal, decimal e hexadecimal—e agora o suporte a binários foi incluído. A representação dos literais octais mudou: Literais octais agora são representados com um 0o ou 0O prefixado (ou seja, um zero seguido da letra o maiúscula ou minúscula) e o literal que será avaliado. Por exemplo, um octal 13 ou decimal 11 é representado como:
>>>0o13 11 |
Os novos literais binários são representados com um
0b ou 0B prefixado (ou seja, um zero seguido da letra b maiúscula ou minúscula). A representação do decimal 21 em binários é então:
>>>0b010101 21 |
Os métodos oct() e hex()
foram removidos.
Anotações de Funções associam expressões com partes de uma função, como parâmetros, no momento da compilação. As anotações de funções, por si só, são insignificantes—ou seja, não são processadas, a menos que uma biblioteca de terceiros o faça. O propósito das anotações de funções é padronizar a forma como parâmetros ou valores de retorno de uma função são anotados. A sintaxe para as anotações de funções é:
def methodName(param1: expression1, ..., paramN: expressionN)->ExpressionForReturnType:
...
|
Por exemplo, aqui estão anotações para os parâmetros de uma função:
def execute(program:"name of program to be executed", error:"if something goes wrong"):
...
|
O seguinte exemplo anota o valor de retorno de uma função. Isso é útil para a verificação do tipo de retorno de uma função:
def getName() -> "isString":
...
|
A gramática completa das anotações de funções pode ser encontrada no PEP 3107 (consulte Recursos para obter um link).
O release final do Python 3 foi publicado em dezembro de 2008. Desde lá, eu tive a chance de verificar alguns blogs e como as pessoas estão reagindo em relação a problemas de compatibilidade. Embora não seja possível ter um consenso oficial, os blogs que eu li certamente parecem polarizados. Algumas pessoas na comunidade de desenvolvimento Linux® realmente não parecem aprovar a transição para a versão 3 devido à quantidade massiva de código que precisa ser transmitida. Por outro lado, muitos desenvolvedores da Web farão a transição devido às alterações no suporte unicode.
Eu o aconselho a, no mínimo, antes de qualquer opinião, consultar os PEPs e as listas de correspondências de desenvolvimento para decidir se passará ou não para a nova versão. Os PEPs explicam em termos racionais uma determinada alteração e os benefícios obtidos com a implementação. Essas alterações foram realmente boas e profundamente discutidas. Os tópicos abrangidos nesta série foram apresentados para que o programador Python comum possa obter um rápido entendimento quanto às alterações sem precisar percorrer todos os PEPs.
Aprender
- Comece pela Parte 1 desta série de duas partes:
Instruções do Python 3, Parte 1: O que Há de Novo.
- Leia os PEPs relevantes do Python 3:
- PEP 318: Decoradores para Funções e Métodos
- PEP 3107: Anotações de Funções
- PEP 3129: Decoradores de Classe
- PEP 3127: Sintaxe e Suporte Literal de Número Inteiro
- PEP 3115: Metaclasses no Python 3000
- PEP 3119: Apresentação das Classes Abstratas de Base
- PEP 3141: Um Tipo de Hierarquia de Números
- PEP 3109: Exceções Levantadas no Python 3000
- PEP 3110: Exceções Obtidas do Python 3000
- PEP 3102: Argumentos apenas com Palavras-chave
- Leia a entrada da Wikipedia sobre
metaclasses.
- Leia a série developerWorks de David Mertz e Michele Simionato,
Metaclass programming in Python
(developerWorks, Fevereiro de 2003).
- Verifique a Arquivos de Ajuda do Python 3.
- A Wikipedia fornece uma boa explicação de
deques.
Obter produtos e tecnologias
- Obtenha a Última
Versão do Python.
