O MaxCompute oferece suporte a funções definidas pelo usuário (UDFs) escritas em Python 3, o que permite estender o SQL com lógica de negócios personalizada. Ao chamar uma UDF, o MaxCompute passa o nome da função e os argumentos para o runtime do Python. O runtime executa o método evaluate e retorna o resultado à consulta.
Início rápido
O exemplo mínimo a seguir soma dois inteiros e trata entradas NULL:
from odps.udf import annotate
@annotate("bigint,bigint->bigint")
class MyPlus(object):
def evaluate(self, arg0, arg1):
if None in (arg0, arg1):
return None
return arg0 + arg1
Para executar uma UDF em Python 3, adicione o seguinte sinalizador de sessão antes da instrução SQL:
SET odps.sql.python.version=cp37;
SELECT my_plus(col_a, col_b) FROM my_table;
Estrutura do código da UDF
Toda UDF em Python 3 exige quatro componentes:
|
Componente |
Descrição |
|
Importação de módulo |
|
|
Assinatura da função |
|
|
Classe Python personalizada |
A classe é a unidade organizacional da UDF. Ela define as variáveis e os métodos que implementam a lógica de negócios. As classes também podem referenciar bibliotecas de terceiros pré-instaladas no MaxCompute, além de arquivos e tabelas externos. Consulte Bibliotecas de terceiros e Referenciar recursos. |
|
Método |
Definido na classe, o método |
Limitações
Acesso à internet (aplicado em tempo de execução)
Por padrão, as UDFs não têm acesso à internet. Para ativar esse acesso, envie um Formulário de Solicitação de Conexão de Rede. A equipe de suporte técnico do MaxCompute entrará em contato para concluir a configuração. Para obter instruções, consulte Formulário de Solicitação de Conexão de RedeProcesso de Acesso à Rede.
Acesso à VPC (aplicado em tempo de execução)
Por padrão, as UDFs não acessam nuvens privadas virtuais (VPCs). Para acessar recursos da VPC a partir de uma UDF, crie primeiro uma conexão de rede entre o projeto do MaxCompute e a VPC de destino. Para mais informações, consulte Acessar recursos em uma VPC usando uma UDF.
Leitura de dados de tabela (aplicado em tempo de execução)
UDFs, funções de agregação definidas pelo usuário (UDAFs) e funções com valor de tabela definidas pelo usuário (UDTFs) não podem ler dados dos seguintes tipos de tabela:
Tabelas com esquemas modificados (Evolução de Esquema)
Tabelas com tipos de dados complexos
Tabelas com o tipo de dados JSON
Tabelas transacionais
Notas de uso
Python 2 e Python 3 não são compatíveis. Não misture UDFs em Python 2 e Python 3 na mesma instrução SQL.
O Python 2 atingiu o fim da vida útil (EOL) no início de 2020. Para obter orientações sobre como migrar UDFs existentes, consulte Migrar UDFs em Python 2.
Tratamento de NULL
Trate valores NULL explicitamente no método evaluate:
def evaluate(self, arg):
if arg is None:
return None
return arg.upper()
Desenvolver uma UDF
O MaxCompute permite desenvolver UDFs com o MaxCompute Studio, o DataWorks e o cliente do MaxCompute (odpscmd). Todas as três ferramentas seguem o mesmo fluxo de trabalho:
Escreva o código da UDF
Faça upload do arquivo Python e registre a função
Chame a UDF no SQL
As seções a seguir detalham o fluxo de trabalho para cada ferramenta, utilizando a mesma função de exemplo GetUrlChar, que extrai um segmento de URL por posição.
Usar o MaxCompute Studio
Pré-requisitos
Antes de começar, certifique-se de ter:
Escrever o código da UDF
No painel Project, clique com o botão direito em scripts sob o MaxCompute script module e escolha New > MaxCompute Python.
Na caixa de diálogo Create new MaxCompute python class, insira um nome de classe em Name, selecione python UDF na lista suspensa Kind e clique em OK.
-
Escreva o código da UDF no editor. Exemplo:
Para testes locais de UDF, consulte Testar UDFs .
from odps.udf import annotate @annotate("string,bigint->string") class GetUrlChar(object): def evaluate(self, url, n): if n == 0: return "" try: index = url.find(".htm") if index < 0: return "" a = url[:index] index = a.rfind("/") b = a[index + 1:] c = b.split("-") if len(c) < n: return "" return c[-n] except Exception: return "Internal error"
Fazer upload do arquivo e registrar a função
Clique com o botão direito no arquivo Python na pasta scripts e selecione Deploy to server.... Na caixa de diálogo Submit resource and register function, insira o nome da função e clique em OK. Para detalhes, consulte Fazer upload de um programa Python e criar uma UDF do MaxCompute.
Chamar a UDF
Na aba Project Explore, clique com o botão direito no projeto do MaxCompute, selecione Open Console e execute:
SET odps.sql.python.version=cp37;
SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);
Resultado:
+-----+
| _c0 |
+-----+
| a |
+-----+
Usar o DataWorks
Pré-requisitos
Antes de começar, certifique-se de ter ativado o DataWorks e associado um workspace do DataWorks ao projeto do MaxCompute. Para instruções de configuração, consulte DataWorks.
Escrever o código da UDF
Escreva o código da UDF em qualquer editor Python. Exemplo:
from odps.udf import annotate
@annotate("string,bigint->string")
class GetUrlChar(object):
def evaluate(self, url, n):
if n == 0:
return ""
try:
index = url.find(".htm")
if index < 0:
return ""
a = url[:index]
index = a.rfind("/")
b = a[index + 1:]
c = b.split("-")
if len(c) < n:
return ""
return c[-n]
except Exception:
return "Internal error"
Fazer upload do arquivo e registrar a função
Faça upload do código empacotado no console do DataWorks e crie a UDF. Consulte:
Chamar a UDF
Crie um nó ODPS SQL no console do DataWorks e execute:
SET odps.sql.python.version=cp37;
SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);
Para mais informações sobre nós ODPS SQL, consulte Desenvolver uma tarefa SQL do MaxCompute.
Usar o cliente do MaxCompute (odpscmd)
Pré-requisitos
Antes de começar, certifique-se de ter baixado, instalado e configurado o cliente do MaxCompute (odpscmd). Para instruções de configuração, consulte Cliente do MaxCompute (odpscmd).
Escrever o código da UDF
Escreva o código da UDF em qualquer editor Python. Exemplo:
from odps.udf import annotate
@annotate("string,bigint->string")
class GetUrlChar(object):
def evaluate(self, url, n):
if n == 0:
return ""
try:
index = url.find(".htm")
if index < 0:
return ""
a = url[:index]
index = a.rfind("/")
b = a[index + 1:]
c = b.split("-")
if len(c) < n:
return ""
return c[-n]
except Exception:
return "Internal error"
Fazer upload do arquivo e registrar a função
Faça upload do arquivo Python e registre a UDF usando os seguintes comandos:
Chamar a UDF
Execute o seguinte SQL no cliente:
SET odps.sql.python.version=cp37;
SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);
Bibliotecas de terceiros
O runtime Python 3 integrado ao MaxCompute não inclui o NumPy. Para usar o NumPy, faça upload manual do pacote wheel do NumPy. Baixe o pacote do PyPI ou de um espelho. O nome do arquivo segue o padrão numpy-<version>-cp37-cp37m-manylinux1_x86_64.whl.
Para instruções de upload, consulte Operações de recursos ou Usar pacotes de terceiros em UDFs Python.
Para obter a lista completa das bibliotecas padrão disponíveis no runtime Python 3,7, consulte A Biblioteca Padrão do Python.
Assinaturas de função e tipos de dados
Antes de escrever o código da UDF, defina:
Quais tipos de entrada a função aceita e qual tipo ela retorna
Como a função trata entradas NULL (o MaxCompute pode passar NULLs para qualquer UDF)
A assinatura da função usa o decorador @annotate:
@annotate(<signature>)
O formato da string de assinatura é:
'arg_type_list -> type'
Tipos de entrada (arg_type_list)
Separe vários tipos de entrada com vírgulas. Os seguintes tipos têm suporte:
BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR e tipos complexos ARRAY, MAP, STRUCT (incluindo tipos complexos aninhados).
Dois valores especiais para arg_type_list:
|
Valor |
Significado |
|
|
Aceita qualquer número de argumentos |
|
|
Não aceita argumentos |
Tipo de retorno (type)
As UDFs retornam uma única coluna. Os tipos de retorno com suporte são:
BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale) e tipos complexos ARRAY, MAP, STRUCT (incluindo tipos complexos aninhados).
Os tipos disponíveis dependem da edição de tipos de dados do MaxCompute usada pelo projeto. Para detalhes, consulte Edições de tipos de dados .
Exemplos de assinatura
|
Assinatura |
Descrição |
|
|
Recebe entradas BIGINT e DOUBLE, retorna STRING |
|
|
Recebe qualquer número de entradas, retorna STRING |
|
|
Não recebe entradas, retorna DOUBLE |
|
|
Recebe ARRAY\ |
|
|
Não recebe entradas, retorna MAP\ |
Mapeamentos de tipos do MaxCompute SQL para Python 3
Escreva o código da UDF usando estes mapeamentos de tipos para garantir consistência:
|
Tipo MaxCompute SQL |
Tipo 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
Referencie arquivos ou tabelas no código da UDF usando o módulo odps.distcache.
Referenciar um arquivo
odps.distcache.get_cache_file(resource_name, mode) retorna o conteúdo de um recurso de arquivo.
|
Parâmetro |
Descrição |
|
|
Nome de um recurso de arquivo existente no projeto do MaxCompute. Retorna um erro se o nome for inválido ou se o recurso não existir. |
|
|
Modo de abertura. |
O valor de retorno é um objeto semelhante a um arquivo. Chame close() nele ao terminar para liberar o identificador de arquivo.
from odps.udf import annotate
from odps.distcache import get_cache_file
@annotate('bigint->string')
class DistCacheExample(object):
def __init__(self):
cache_file = get_cache_file('test_distcache.txt')
kv = {}
for line in cache_file:
line = line.strip()
if not line:
continue
k, v = line.split()
kv[int(k)] = v
cache_file.close()
self.kv = kv
def evaluate(self, arg):
return self.kv.get(arg)
Referenciar uma tabela
odps.distcache.get_cache_table(resource_name) retorna o conteúdo de um recurso de tabela.
|
Parâmetro |
Descrição |
|
|
Nome de um recurso de tabela existente no projeto atual do MaxCompute. Retorna uma exceção se o nome for inválido ou se o recurso não existir. |
O valor de retorno é um gerador. Um registro do tipo ARRAY é obtido cada vez que o chamador percorre a tabela. Tipos de coluna com suporte: BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, FLOAT, CHAR, VARCHAR, BINARY, DATE, DECIMAL, ARRAY, MAP, STRUCT.
from odps.udf import annotate
from odps.distcache import get_cache_table
@annotate('->string')
class DistCacheTableExample(object):
def __init__(self):
self.records = list(get_cache_table('udf_test'))
self.counter = 0
self.ln = len(self.records)
def evaluate(self):
if self.counter > self.ln - 1:
return None
ret = self.records[self.counter]
self.counter += 1
return str(ret)
Chamar uma UDF
Ativar Python 3
Os projetos do MaxCompute usam Python 2 para UDFs por padrão. Para executar uma UDF em Python 3, adicione a seguinte linha antes da instrução SQL:
SET odps.sql.python.version=cp37;
Chamar dentro do mesmo projeto
Chame a UDF da mesma forma que uma função integrada:
SET odps.sql.python.version=cp37;
SELECT my_udf(column1, column2) FROM my_table;
Chamar entre projetos
Para usar uma UDF de outro projeto (por exemplo, usar uma UDF do Projeto B no Projeto A), prefixe a chamada da função com o nome do projeto de source:
SELECT B:udf_in_other_project(arg0, arg1) AS res FROM table_t;
Para mais informações, consulte Acessar recursos entre projetos usando pacotes.
Migrar UDFs em Python 2
O Python 2 atingiu o fim da vida útil (EOL) no início de 2020.
Para novos projetos, escreva todas as UDFs Python em Python 3.
Para projetos existentes com UDFs em Python 2, proceda com cautela ao mudar para o Python 3. Duas abordagens estão disponíveis:
Escreva novas UDFs em Python 3 e ative o Python 3 no nível de sessão para novos jobs. Para detalhes, consulte Ativar Python 3.
Reescreva as UDFs existentes em Python 2 para serem compatíveis tanto com Python 2 quanto com Python 3. Para orientações, consulte Portando código Python 2 para Python 3.
Para UDFs compartilhadas entre vários projetos, escreva código compatível tanto com Python 2 quanto com Python 3.