Como criar UDFs de Python

Esse tópico ajuda você a criar UDFs de Python.

Neste tópico:

Nota

As UDFs de Python vetorizadas permitem que você defina funções Python que recebem lotes de linhas de entrada como DataFrames do Pandas e retornam lotes de resultados como matrizes ou séries do Pandas. A interface de lote resulta em um desempenho muito melhor com cenários de inferência de machine learning. Para obter mais informações, consulte UDFs vetorizadas de Python.

Como escolher seus tipos de dados

Antes de escrever seu código:

  • Escolha os tipos de dados que sua função deve aceitar como argumentos e o tipo de dados que sua função deve retornar.

  • Leve em conta questões relacionadas ao fuso horário.

  • Decida como lidar com valores NULL.

Para obter mais informações sobre como o Snowflake mapeia os tipos de dados Python e SQL, consulte Mapeamentos de tipos de dados SQL-Python.

Valores TIMESTAMP_LTZ e fusos horários

Uma UDF de Python fica bastante isolada do ambiente no qual é chamada. Entretanto, o fuso horário é herdado do ambiente de chamada. Se a sessão do chamador definir um fuso horário padrão antes de chamar a UDF de Python, a UDF de Python tem o mesmo fuso horário padrão. Para obter mais informações sobre fusos horários, consulte TIMEZONE.

Valores NULL

Para todos os tipos do Snowflake exceto Variant, um argumento NULL de SQL para uma UDF de Python é traduzido como o valor None de Python, e um valor None de Python retornado é traduzido de volta para um NULL de SQL.

Um valor do tipo Variant pode ser um NULL de SQL ou um null de JSON VARIANT. Para obter mais informações sobre o VARIANT NULL do Snowflake, consulte Valores NULL.

  • Um null JSON VARIANT é traduzido para um None de Python.

  • Um NULL de SQL é traduzido para um objeto de Python com o atributo is_sql_null.

Para obter um exemplo, consulte Tratamento de NULL em UDFs de Python.

Criação de UDFs de Python que ficam dentro das restrições impostas pelo Snowflake

Para garantir a estabilidade dentro do ambiente do Snowflake, o Snowflake impõe as seguintes restrições a UDFs de Python. A menos que o contrário seja indicado, essas limitações são aplicadas quando a UDF é executada, não quando ela é criada.

O treinamento de modelos de machine learning (ML) podem consumir muitos recursos às vezes. Os warehouses otimizados para Snowpark são de um tipo de warehouse virtual do Snowflake que pode ser usado para cargas de trabalho que exigem uma grande quantidade de recursos de memória e computação. Para obter mais informações sobre modelos de machine learning e Snowpark Python, consulte Treinamento dos modelos de machine learning com Snowpark Python.

Memória

Evite consumir memória demais.

  • Grandes valores de dados podem consumir uma grande quantidade de memória.

  • Uma profundidade de pilha excessiva pode consumir uma grande quantidade de memória.

UDFs retornam um erro se elas consumirem memória demais. O limite específico está sujeito a mudanças.

Se os UDFs falharem devido ao alto consumo de memória, considere usar Warehouses otimizados para Snowpark.

Hora

Evite algoritmos que demorem muito tempo por chamada.

Se uma UDF leva tempo demais para ser concluída, o Snowflake interrompe a instrução SQL e retorna um erro ao usuário. Isso limita o impacto e o custo de erros como loops infinitos.

Criação do módulo

Quando uma instrução SQL chama sua UDF de Python, o Snowflake chama uma função Python que você escreveu. Sua função de Python é chamada de “método do manipulador”, ou “manipulador” abreviadamente. O manipulador é uma função implementada dentro de um módulo fornecido pelo usuário.

Como em qualquer função de Python, sua função deve ser declarada como parte de um módulo.

O manipulador é chamado uma vez para cada linha passada para a UDF de Python. O módulo que contém a função não é reimportado para cada linha. O Snowflake pode chamar a função do manipulador do mesmo módulo mais de uma vez.

Para otimizar a execução de seu código, o Snowflake supõe que a inicialização pode ser lenta, enquanto a execução da função do manipulador é rápida. O Snowflake define um tempo limite mais longo para executar a inicialização (incluindo o tempo para carregar sua UDF e o tempo para inicializar o módulo) do que para executar o manipulador (o tempo para chamar seu manipulador com uma linha de entrada).

Informações adicionais sobre a criação do módulo podem ser encontradas em Como criar UDFs de Python.

Como otimizar a inicialização e controlar o estado global em UDFs escalares

A maioria das UDFs escalares deve seguir as diretrizes abaixo:

  • Se você precisar inicializar o estado compartilhado que não muda entre linhas, inicialize-o no módulo e não na função do manipulador.

  • Escreva sua função do manipulador de forma a ser thread-safe.

  • Evite armazenar e compartilhar o estado dinâmico em linhas diferentes.

Se sua UDF não puder seguir essas orientações, saiba que o Snowflake espera que UDFs escalares sejam processadas de forma independente. Confiar em um estado compartilhado entre invocações pode resultar em comportamento inesperado, pois o sistema pode processar linhas em qualquer ordem e espalhar essas invocações por várias instâncias. Além disso, pode haver várias execuções da mesma função do manipulador dentro do mesmo intérprete de Python em vários threads.

UDFs devem evitar confiar em um estado compartilhado entre diferentes chamadas para a função do manipulador. Entretanto, há duas situações em que você pode querer que uma UDF armazene um estado compartilhado:

  • Código que contém uma lógica de inicialização cara que você não quer repetir para cada linha.

  • Código que use um estado compartilhado em linhas diferentes, como um cache.

Quando for necessário manter um estado global que será compartilhado em diferentes invocações do manipulador, você deve proteger o estado global contra corridas de dados usando os primitivos de sincronização descritos em threading — Thread-based parallelism.

Otimização para escala e desempenho

Uso de UDFs vetorizadas de Python com bibliotecas Data Science

Quando seu código usar bibliotecas de aprendizado de máquina ou bibliotecas Data Science, use UDFs vetorizadas de Python para definir as funções Python que recebem linhas de entrada em lotes nos quais a operação dessas bibliotecas é otimizada.

Para obter mais informações, consulte UDFs vetorizadas de Python.

Escrever manipuladores de UDF com um único thread

Escreva UDF manipuladores com um único thread. O Snowflake lidará com o particionamento dos dados e o escalonamento da UDF nos diferentes recursos computacionais do warehouse virtual.

Colocar uma inicialização cara no módulo

Coloque código de inicialização caro no escopo do módulo. Lá, ele será executado quando a UDF for inicializada. Evite executar novamente o código de inicialização caro em cada invocação do manipulador da UDF.

Tratamento de erros

Uma função de Python usada como UDF pode usar as técnicas normais de tratamento de exceções de Python para capturar erros dentro da função.

Se uma exceção ocorrer dentro da função e não for capturada pela função, o Snowflake gerará um erro incluindo o rastreamento de pilha para a exceção. Quando o registro de exceções não tratadas está ativado, o Snowflake registra dados sobre exceções não tratadas em uma tabela de eventos.

Você pode gerar uma exceção explicitamente sem capturá-la para encerrar a consulta e produzir um erro de SQL. Por exemplo:

if (x < 0):
  raise ValueError("x must be non-negative.");
Copy

Ao depurar, você pode incluir valores no texto da mensagem de erro do SQL. Para fazer isso, coloque todo um corpo de método de Python em um bloco try-catch; anexe valores de argumento à mensagem do erro capturado; e gere uma exceção com a mensagem estendida. Para evitar revelar dados sensíveis, remova valores de argumento antes de lançar um ambiente de produção.

Boas práticas de segurança

Para ajudar a garantir que seu manipulador funcione de forma segura, consulte as práticas recomendadas descritas em Práticas de segurança para UDFs e procedimentos.