A Python Software Foundation encerrará em breve a manutenção do Python 2. O MaxCompute agora oferece suporte ao Python 3, especificamente à versão CPython-3.7.3. Este tópico descreve como escrever uma função de agregação definida pelo usuário (UDAF) em Python 3.
Estrutura do código da UDAF
Use o MaxCompute Studio para escrever o código de uma função de agregação definida pelo usuário (UDAF) em Python 3. O código deve conter os seguintes elementos:
-
Importação de módulos: Obrigatório.
Importe pelo menos
from odps.udf import annotateefrom odps.udf import BaseUDAF. A instruçãofrom odps.udf import annotateimporta o módulo de assinatura da função, permitindo que o MaxCompute reconheça a assinatura definida no código. A instruçãofrom odps.udf import BaseUDAFimporta a classe base para UDAFs em Python. Implemente métodos comoiterate,mergeeterminatena classe derivada.Se o código da UDAF precisar referenciar recursos de arquivo ou tabela, inclua
from odps.distcache import get_cache_filepara recursos de arquivo oufrom odps.distcache import get_cache_tablepara recursos de tabela. -
Assinatura da função: Obrigatório.
O formato é
@annotate(<signature>). O parâmetrosignaturedefine os tipos de dados dos parâmetros de entrada e do valor de retorno da função. Para obter mais informações sobre assinaturas de função, consulte Function signature and data types. -
Classe Python personalizada (classe derivada): Obrigatório.
Esta classe atua como unidade organizacional do código da UDAF e define as variáveis e os métodos que implementam a lógica de negócios. Também é possível referenciar bibliotecas de terceiros integradas, arquivos ou recursos de tabela no código. Para mais detalhes, consulte Third-party libraries ou Reference resources.
-
Implementação dos métodos da classe Python: Obrigatório.
A implementação da classe Python inclui os métodos listados abaixo. Implemente-os conforme a necessidade.
Definição do método
Descrição
BaseUDAF.new_buffer()Retorna um buffer para o valor intermediário da função de agregação. O
bufferdeve ser um objeto Marshal, como LIST ou DICT. O tamanho dobuffernão deve aumentar proporcionalmente ao volume de dados. Em casos extremos, o tamanho dobufferapós a serialização do objeto não pode exceder 2 MB.BaseUDAF.iterate(buffer[, args, ...])Agrega os argumentos
argsao valor intermediário armazenado embuffer.BaseUDAF.merge(buffer, pbuffer)Combina os valores intermediários
bufferepbuffer, armazenando o resultado embuffer.BaseUDAF.terminate(buffer)Converte o conteúdo de
bufferem um tipo de dados primitivo do MaxCompute SQL.
O código a seguir apresenta um exemplo de UDAF.
# Import the function signature module and the base class.
from odps.udf import annotate
from odps.udf import BaseUDAF
# Function signature.
@annotate('double->double')
# Custom Python class.
class Average(BaseUDAF):
# Implement the methods of the Python class.
def new_buffer(self):
return [0, 0]
def iterate(self, buffer, number):
if number is not None:
buffer[0] += number
buffer[1] += 1
def merge(self, buffer, pbuffer):
buffer[0] += pbuffer[0]
buffer[1] += pbuffer[1]
def terminate(self, buffer):
if buffer[1] == 0:
return 0.0
return buffer[0] / buffer[1]
A figura a seguir ilustra a lógica de implementação e o fluxo de cálculo de uma UDAF do MaxCompute para calcular o valor médio (
avg
).

O parâmetro
pbuffer
corresponde a
pr
na figura, enquanto
buffer
corresponde a
r
.
A diferença entre UDAFs em Python 2 e Python 3 reside na versão subjacente do Python. Escreva sua UDAF com base nos recursos da versão correspondente.
Observações de uso
O Python 3 não é compatível com o Python 2, e ambas as versões não podem ser usadas na mesma instrução SQL. Avalie a compatibilidade antes de fazer a migração.
O Python 2 atingiu o fim da vida útil (EOL) no início de 2020. Recomendamos migrar seus projetos de acordo com o tipo de cada um.
Migração de UDAF em Python 2
Como a Python Software Foundation encerrará em breve a manutenção do Python 2, recomendamos migrar seus projetos conforme o tipo:
Novos projetos: Aplica-se a novos projetos do MaxCompute ou a projetos nos quais você está escrevendo uma UDAF em Python pela primeira vez. Recomendamos escrever todas as UDAFs em Python usando a versão 3.
-
Projetos existentes: Aplica-se a projetos do MaxCompute que já possuem muitas UDAFs em Python 2. Ative o Python 3 com cautela. Se planeja migrar gradualmente todas as UDAFs do Python 2 para o Python 3, utilize os métodos a seguir:
Novos jobs e novas UDAFs: Escreva-os em Python 3 e ative o Python 3 no nível da sessão. Para obter mais informações sobre como ativar o Python 3, consulte Enable Python 3.
-
UDAFs em Python 2: Reescreva o código das UDAFs existentes para garantir compatibilidade tanto com Python 2 quanto com Python 3. Para saber mais sobre como reescrever o código, consulte Porting Python 2 Code to Python 3.
NotaSe você pretende criar uma UDAF pública e conceder permissões de uso para vários projetos do MaxCompute, recomendamos tornar a UDAF compatível com Python 2 e Python 3 simultaneamente.
Ativar o Python 3
Por padrão, o MaxCompute utiliza o Python 2. Para usar o Python 3, inclua o seguinte sinalizador de sessão na sua instrução SQL.
set odps.sql.python.version=cp37;
Bibliotecas de terceiros
O ambiente de execução integrado do Python 3 no MaxCompute não possui a biblioteca de terceiros NumPy instalada. Para utilizar uma UDAF que dependa do NumPy, faça o upload manual do pacote WHEEL do NumPy. Ao baixar o pacote NumPy do PyPI ou de um espelho, o nome do arquivo será numpy-<version_number>-cp37-cp37m-manylinux1_x86_64.whl. Para obter mais informações sobre como fazer upload de pacotes, consulte Resource operations ou UDF example: Use a third-party package in a Python UDF.
Assinatura da função e tipos de dados
A assinatura da função segue o formato abaixo.
@annotate(<signature>)
O parâmetro
signature
é uma string que identifica os tipos de dados dos parâmetros de entrada e do valor de retorno. Ao executar uma UDAF, os tipos de dados de entrada e saída devem corresponder exatamente aos tipos especificados na assinatura. Durante a fase de análise da consulta, o sistema valida a chamada da função comparando-a com a assinatura definida. Caso haja incompatibilidade de tipos, um erro será reportado. O formato específico é o seguinte:
'arg_type_list -> type'
onde:
-
arg_type_list: Representa os tipos de dados dos parâmetros de entrada. É possível especificar múltiplos parâmetros separados por vírgulas (,). Os tipos suportados incluem BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR, tipos complexos (ARRAY, MAP, STRUCT) e tipos complexos aninhados.O campo
arg_type_listtambém aceita um asterisco (*) ou uma string vazia ('').Quando
arg_type_listfor um asterisco (*), indica que a função aceita qualquer quantidade de parâmetros de entrada.Quando
arg_type_listfor uma string vazia (''), indica que a função não possui parâmetros de entrada.
Para mais detalhes sobre a sintaxe estendida da anotação Resolve, consulte Dynamic parameters for UDAFs and UDTFs.
type: Representa o tipo de dado do valor de retorno. Uma UDAF retorna apenas uma coluna. Os tipos suportados incluem BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), tipos complexos (ARRAY, MAP, STRUCT) e tipos complexos aninhados.
Ao escrever o código da UDAF, selecione os tipos de dados apropriados com base na edição de tipos de dados do seu projeto MaxCompute. Para obter mais informações sobre as edições de tipos de dados e os tipos suportados em cada uma, consulte
.
Veja a seguir exemplos válidos de assinaturas de função.
|
Exemplo de assinatura de função |
Descrição |
|
|
Os tipos dos parâmetros de entrada são BIGINT e DOUBLE, e o tipo do valor de retorno é STRING. |
|
|
A função aceita qualquer número de parâmetros de entrada, e o tipo do valor de retorno é STRING. |
|
|
A função não possui parâmetros de entrada, e o tipo do valor de retorno é DOUBLE. |
|
|
O tipo do parâmetro de entrada é ARRAY |
Para garantir que os tipos de dados na sua UDAF em Python sejam consistentes com os tipos suportados pelo MaxCompute, utilize os mapeamentos corretos. A tabela a seguir descreve esses mapeamentos.
|
Tipo do MaxCompute SQL |
Tipo do Python 3 |
|
BIGINT |
INT |
|
STRING |
UNICODE |
|
DOUBLE |
FLOAT |
|
BOOLEAN |
BOOL |
|
DATETIME |
DATETIME.DATETIME |
|
FLOAT |
FLOAT |
|
CHAR |
UNICODE |
|
VARCHAR |
UNICODE |
|
BINARY |
BYTES |
|
DATE |
DATETIME.DATE |
|
DECIMAL |
DECIMAL.DECIMAL |
|
ARRAY |
LIST |
|
MAP |
DICT |
|
STRUCT |
COLLECTIONS.NAMEDTUPLE |
Referenciar recursos
As UDAFs em Python podem referenciar recursos de arquivo e tabela utilizando o módulo odps.distcache.
-
odps.distcache.get_cache_file(resource_name): Retorna um objeto semelhante a um arquivo para o recurso de arquivo especificado.-
O parâmetro
resource_nameé do tipo STRING e corresponde ao nome de um recurso de arquivo existente no projeto atual do MaxCompute. Se o nome do recurso for inválido ou se o recurso não existir, uma exceção será lançada.NotaPara acessar um recurso a partir de uma UDAF, declare o recurso referenciado durante a criação da UDAF. Caso contrário, um erro será reportado.
O valor retornado é um objeto semelhante a um arquivo. Após terminar de usar esse objeto, chame o método
closepara liberar o arquivo de recurso aberto.
-
-
odps.distcache.get_cache_table(resource_name): Retorna um objeto gerador para o recurso de tabela especificado.O parâmetro
resource_nameé do tipo STRING e corresponde ao nome de um recurso de tabela existente no projeto atual do MaxCompute. Se o nome do recurso de tabela for inválido ou se o recurso não existir, uma exceção será lançada.O valor retornado é do tipo GENERATOR. O chamador percorre o gerador para recuperar o conteúdo da tabela. Cada iteração retorna um registro da tabela na forma de um array.
Para obter mais informações sobre o uso, consulte Reference resources (Python 3 UDF) e Reference resources (Python 3 UDTF).
Observações de uso
Após desenvolver uma UDAF em Python 3 seguindo as instruções em development flow, chame-a em uma instrução SQL do MaxCompute. Os métodos de chamada são os seguintes:
Usar uma UDF dentro de um projeto do MaxCompute: O método é semelhante ao uso de built-in functions. Utilize a função definida pelo usuário da mesma forma que usaria uma função integrada.
Usar uma UDF entre projetos: Utilize uma UDF do Projeto B dentro do Projeto A. A instrução a seguir mostra um exemplo:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Para obter mais informações sobre compartilhamento entre projetos, consulte Cross-project resource access based on packages.
Para obter o procedimento completo de desenvolvimento e chamada de uma UDAF em Python 3 usando o MaxCompute Studio, consulte Develop a Python UDF.
Parâmetros dinâmicos para UDAFs
Assinatura da função
Para obter mais informações sobre o formato da assinatura de função de uma UDAF em Python, consulte Function signature and data types.
-
Utilize um asterisco (
*) na lista de parâmetros para aceitar parâmetros de entrada em qualquer quantidade e de qualquer tipo. Por exemplo,@annotate('double,*->string')indica que o primeiro parâmetro é do tipo DOUBLE, seguido por uma lista de parâmetros de qualquer quantidade e tipo. Nesse caso, escreva o código para determinar a quantidade e os tipos dos parâmetros de entrada e execute as operações correspondentes. Isso é semelhante à funçãoprintfna linguagem C.NotaUm asterisco (
*) tem um significado diferente quando usado na lista de valores de retorno. -
Utilize um asterisco (
*) no valor de retorno de uma UDTF para indicar qualquer quantidade de valores de retorno do tipo STRING. A quantidade de valores retornados depende do número de aliases definidos no momento da chamada da função. Por exemplo, para@annotate("bigint,string->double,*"), o método de chamada éUDTF(x, y) as (a, b, c). Neste exemplo, três aliases são definidos apósas:a,bec. O editor consideraacomo sendo do tipo DOUBLE, pois o tipo da primeira coluna no valor de retorno foi especificado na anotação, e considerabeccomo sendo do tipo STRING. Como três valores de retorno foram especificados, quando a UDTF chamarforward, o argumento deforwarddeve ser um array de comprimento 3. Caso contrário, ocorrerá um erro de tempo de execução.NotaEsse tipo de erro não pode ser detectado em tempo de compilação. Portanto, quem chama a UDTF deve definir a quantidade de aliases na instrução SQL seguindo as regras estabelecidas pela própria UDTF. Como a quantidade de valores de retorno de uma função de agregação é fixa em 1, esse recurso não se aplica a uma UDAF.
Exemplo de UDAF
from odps.udf import annotate
from odps.udf import BaseUDAF
@annotate('bigint,*->string')
class MultiColSum(BaseUDAF):
def new_buffer(self):
return [0]
def iterate(self, buffer, *args):
for arg in args:
buffer[0] += int(arg)
def merge(self, buffer, pbuffer):
buffer[0] += pbuffer[0]
def terminate(self, buffer):
return str(buffer[0])
Uma UDAF pode ter apenas um valor de retorno. No exemplo anterior, o valor retornado é a soma de múltiplos parâmetros de entrada, que depois é agregada e somada em várias linhas. O código a seguir fornece um exemplo de uso.
-- Sums multiple input parameters.
SELECT my_multi_col_sum(a,b,c,d,e) from values (1,"2","3","4","5"), (6,"7","8","9","10") t(a,b,c,d,e);
-- The return value is 55.