O MaxCompute usa o Python 2.7 para executar funções definidas pelo usuário (UDFs). Este tópico explica como escrever uma UDF em Python 2, desde a estrutura do código e as restrições de sandbox até os mapeamentos de tipos de dados e referências de recursos.
Estrutura do código da UDF
Uma UDF em Python 2 tem cinco componentes. Um é opcional ou condicional; os outros quatro são sempre obrigatórios.
|
Componente |
Obrigatório |
Finalidade |
|
Declaração de codificação |
Opcional (obrigatório se o código contiver caracteres chineses) |
Declara a codificação do arquivo |
|
Importação de módulo |
Obrigatório |
Importa a assinatura da função e módulos de recursos |
|
Assinatura da função |
Obrigatório |
Define os tipos de dados de entrada e retorno via |
|
Classe Python personalizada |
Obrigatório |
Unidade organizacional da lógica da UDF |
|
Método |
Obrigatório |
Define os parâmetros de entrada e o valor de retorno da UDF; cada classe pode ter apenas um |
Declaração de codificação
Adicione uma declaração de codificação no início de qualquer arquivo de UDF que contenha caracteres chineses. Sem ela, o MaxCompute retorna um erro durante a execução. Os dois formatos abaixo são equivalentes:
#coding:utf-8
# -*- coding: utf-8 -*-
Importação de módulo
Toda UDF deve importar o módulo de assinatura da função:
from odps.udf import annotate
Para referenciar arquivos ou tabelas no código da UDF, importe também de odps.distcache:
from odps.distcache import get_cache_file # for file resources
from odps.distcache import get_cache_table # for table resources
Assinatura da função
O decorador @annotate define os tipos de dados dos parâmetros de entrada e do valor de retorno. O MaxCompute verifica a consistência dos tipos durante a análise semântica e retorna um erro se houver incompatibilidade.
@annotate("bigint,bigint->bigint")
Para ver a sintaxe completa da assinatura, consulte Assinaturas de função e tipos de dados.
Exemplo mínimo
O exemplo a seguir mostra uma UDF completa e funcional que soma dois números inteiros. Ele abrange todos os componentes obrigatórios.
#coding:utf-8
# Import the function signature.
from odps.udf import annotate
# Define input types (BIGINT, BIGINT) and return type (BIGINT).
@annotate("bigint,bigint->bigint")
class MyPlus(object):
def evaluate(self, arg0, arg1):
if None in (arg0, arg1):
return None
return arg0 + arg1
Sempre trate entradas
None
explicitamente. Valores NULL no MaxCompute SQL correspondem a
None
no Python 2. Portanto, não verificar a presença de
None
pode causar erros inesperados.
Limitações
Operações proibidas
O MaxCompute executa o código de UDFs em Python 2 dentro de uma sandbox. As seguintes operações não são permitidas:
Leitura ou gravação em arquivos locais
Início de subprocessos
Início de threads
Abertura de conexões de socket
Chamada de UDFs em Python 2 a partir de sistemas externos
Devido a essas restrições, todo o código enviado deve usar apenas as bibliotecas padrão do Python. Não é possível usar módulos ou módulos de extensão C que executem as operações proibidas listadas acima.
Módulos disponíveis na biblioteca padrão
Todos os módulos escritos puramente em Python da biblioteca padrão (aqueles sem dependência de módulos de extensão C) estão disponíveis.
Os seguintes módulos de extensão C também estão disponíveis:
array,audioopbinascii,bisectcmath,_codecs_cn,_codecs_hk,_codecs_iso2022,_codecs_jp,_codecs_kr,_codecs_tw,_collections,cStringIOdatetime_functools,future_builtins_heapq,_hashlibitertools_json_locale,_lsprofmath,_md5,_multibytecodecoperator_random_sha256,_sha512,_sha,_struct,stroptimeunicodedata_weakrefcPickle
O tamanho máximo de dados graváveis em
sys.stdout
ou
sys.stderr
é de 20 KB. Qualquer saída que exceda esse limite é descartada silenciosamente.
Bibliotecas de terceiros
Bibliotecas de terceiros, como NumPy, vêm pré-instaladas no ambiente Python 2 do MaxCompute como complemento à biblioteca padrão.
O uso de bibliotecas de terceiros está sujeito às mesmas restrições de sandbox. O acesso a dados locais não é permitido e a E/S de rede é limitada. As APIs relacionadas nas bibliotecas afetadas ficam desabilitadas.
Assinaturas de função e tipos de dados
Formato da assinatura
@annotate('<arg_type_list>-><return_type>')
arg_type_list especifica os tipos dos parâmetros de entrada, separados por vírgulas. Aceita duas formas especiais:
*— aceita qualquer número de parâmetros de entrada''(string vazia) — não aceita parâmetros de entrada
return_type especifica o tipo do único valor de retorno.
Tipos de entrada e retorno compatíveis: BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, VARCHAR e os tipos complexos ARRAY, MAP, STRUCT (incluindo tipos complexos aninhados).
Os tipos de dados disponíveis nas assinaturas de função dependem da edição de tipos de dados do MaxCompute usada pelo seu projeto. Para mais detalhes, consulte
.
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\ |
Mapeamento de tipos de dados
Escreva a lógica da UDF em Python usando os tipos do Python 2 correspondentes aos tipos do MaxCompute SQL. Incompatibilidades de tipo causam erros de execução.
|
Tipo do MaxCompute SQL |
Tipo do Python 2 |
Observações |
|
BIGINT |
int |
|
|
STRING |
str |
|
|
DOUBLE |
float |
|
|
BOOLEAN |
bool |
|
|
DATETIME |
int |
Armazenado como milissegundos desde 00:00:00 de quinta-feira, 1º de janeiro de 1970 (Unix epoch). Use o módulo |
|
FLOAT |
float |
|
|
CHAR |
str |
|
|
VARCHAR |
str |
|
|
BINARY |
bytearray |
|
|
DATE |
int |
|
|
DECIMAL |
decimal.Decimal |
|
|
ARRAY |
list |
|
|
MAP |
dict |
|
|
STRUCT |
collections.namedtuple |
Observações adicionais:
NULL no MaxCompute SQL corresponde a
Noneno Python 2.O parâmetro
silentfoi adicionado aodps.udf.int(value). Sesilentestiver definido comoTruee o valor não puder ser convertido paraint, a função retornaráNoneem vez de gerar um erro.
Referenciar recursos
Use o módulo odps.distcache para carregar recursos de arquivo ou tabela no código da UDF durante a inicialização.
Referenciar um arquivo
get_cache_file(resource_name) retorna um objeto semelhante a um arquivo com o conteúdo do recurso de arquivo especificado.
resource_namedeve ser o nome de um recurso de arquivo existente no seu projeto MaxCompute. Se o nome for inválido ou o arquivo não existir, o sistema retornará um erro.Declare o recurso de arquivo ao criar a UDF. Caso contrário, a chamada da UDF retornará um erro.
Chame
close()no objeto retornado após concluir o uso.
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
get_cache_table(resource_name) retorna um gerador. Cada iteração produz um registro na forma de lista.
resource_namedeve ser o nome de um recurso de tabela existente no seu projeto MaxCompute. Se o nome for inválido ou a tabela não existir, o sistema retornará um erro.
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)
Processo de desenvolvimento
O processo de desenvolvimento de UDFs em Python 2 — incluindo configuração, escrita de código, upload do programa Python, criação da UDF, depuração e chamada — é idêntico ao das UDFs em Python 3.
Para o fluxo completo de desenvolvimento, consulte Processo de desenvolvimento.
Para um guia passo a passo usando o MaxCompute Studio, consulte Desenvolver uma UDF em Python.
Ferramentas de desenvolvimento compatíveis:
MaxCompute Studio
DataWorks
Cliente MaxCompute (odpscmd)
Chamar uma UDF em Python 2
Após desenvolver uma UDF em Python 2, chame-a a partir do MaxCompute SQL usando uma das seguintes abordagens:
Dentro de um projeto: Chame a UDF da mesma forma que uma função integrada.
-
Entre projetos: Chame uma UDF definida no projeto B a partir do projeto A usando a seguinte sintaxe:
SELECT B:udf_in_other_project(arg0, arg1) AS res FROM table_t;Para instruções de configuração, consulte Acesso a recursos entre projetos baseado em pacotes.