MaxCompute supports developing user-defined functions (UDFs) in Python 3 to implement custom business logic.
UDF code structure
You can use MaxCompute Studio to write a UDF in Python 3. The code must contain the following components:
Module import: required.
UDF code must include
from odps.udf import annotate, which is used to import the function signature. This way, MaxCompute can identify the function signature that is defined in the code. If you want to reference files or tables in UDF code, the UDF code must includefrom odps.distcache import get_cache_fileorfrom odps.distcache import get_cache_table.-
Function signature: Required.
The format is
@annotate(<signature>), wheresignaturedefines the data types of the function's input parameters and return value. For more information about function signatures, see Function signatures and data types. -
Custom Python class: Required.
The class organizes your UDF code and defines the variables and methods that implement your business logic. You can also reference a built-in third-party library or reference file and table resources in your code. For more information, see Third-party library or Reference resources.
evaluatemethod: required.The evaluate method is contained in the custom Python class. The
evaluatemethod defines the input parameters and return value of the UDF. Each Python class can contain only oneevaluatemethod.
The following code provides a UDF example.
# Import the function signature module.
from odps.udf import annotate
# Define the function signature.
@annotate("bigint,bigint->bigint")
# Define the custom Python class.
class MyPlus(object):
# Implement the evaluate method.
def evaluate(self, arg0, arg1):
if None in (arg0, arg1):
return None
return arg0 + arg1
Limits
Access the Internet by using UDFs
By default, MaxCompute does not allow you to access the Internet by using UDFs. If you want to access the Internet by using UDFs, fill in the network connection application form based on your business requirements and submit the application. The MaxCompute technical support team will contact you promptly to enable network connectivity. For more information about how to fill in the network connection application form, see Network connection process.
Access a VPC by using UDFs
By default, MaxCompute does not allow you to access resources in VPCs by using UDFs. To use UDFs to access resources in a VPC, you must establish a network connection between MaxCompute and the VPC. For more information about related operations, see Access VPC resources from a UDF.
Read table data by using UDFs, UDAFs, or UDTFs
You cannot use UDFs, UDAFs, or UDTFs to read data from the following types of tables:
Table on which schema evolution is performed
Table that contains complex data types
Table that contains JSON data types
Transactional table
Usage notes
Python 3 is not compatible with Python 2, and both cannot be used in the same SQL statement. Consider compatibility before switching.
Python 2 reached its end of life (EOL) in early 2020. We recommend that you migrate your projects based on their type.
UDF development: General workflow
UDF development involves preparing the environment, writing code, uploading and registering the UDF, and then calling it. The following sections walk through this workflow using MaxCompute Studio, DataWorks, and odpscmd.
MaxCompute Studio
-
Prerequisites
Install MaxCompute Studio and connect it to a MaxCompute project before you begin. For more information, see the following topics:
-
Write UDF code.
-
In the Project panel, under the MaxCompute Studio directory, right-click scripts and select .
-
In the Create new MaxCompute python class dialog box, enter a class name for Name, select Python UDF as the type, and then click OK.
-
Write the UDF code in the editor.
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"NoteFor information about how to debug Python UDFs locally, see Test a UDF.
-
-
Upload and register the UDF.
Right-click the target Python program and select Deploy to server…. Configure the function name and click OK. For more information, see Upload a file and register a function.
In this example, the function name is set to UDF_GET_URL_CHAR.
-
Call the UDF.
In the left-side navigation pane, click Project Explore. Right-click the target MaxCompute project, select Open Console, then enter and run the SQL statement to call the UDF.
SET odps.sql.python.version=cp37; -- This command is required to enable Python 3 for the UDF. SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);The following result is returned:
+-----+ | _c0 | +-----+ | a | +-----+
DataWorks
-
Prerequisites
Activate DataWorks and associate it with a MaxCompute project before you begin. For more information, see Connect to MaxCompute by using DataWorks.
-
Write UDF code.
You can develop the UDF code in any Python development tool and package it. The following code is an example.
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" -
Upload and register the UDF.
Upload the packaged code and register the UDF in DataWorks. For more information, see the following topics:
-
Call the UDF.
After you register the UDF, create an ODPS SQL node to write and run SQL statements that call the UDF. For more information about ODPS SQL nodes, see Develop an ODPS SQL task. The following code provides an example of the SQL statement.
SET odps.sql.python.version=cp37; -- This command is required to enable Python 3 for the UDF. SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);
odpscmd
-
Prerequisites
Download and install odpscmd, then configure the config file to connect to a MaxCompute project. For more information, see Connect by using the MaxCompute client (odpscmd).
-
Write UDF code.
You can develop the UDF code in any Python development tool and package it. The following code is an example.
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" -
Upload and register the UDF.
Upload the packaged code and register the UDF by using odpscmd. For more information, see the following topics:
-
Call the UDF.
After you register the UDF, write and run an SQL statement to call it.
SET odps.sql.python.version=cp37; -- This command is required to enable Python 3 for the UDF. SELECT UDF_GET_URL_CHAR("http://www.taobao.com/a.htm", 1);
Install the NumPy library
The built-in Python 3 runtime environment does not include NumPy. If your UDF requires NumPy, manually upload the WHEEL package. When you download the package from PyPI or a mirror site, the file name is in the format numpy-<version>-cp37-cp37m-manylinux1_x86_64.whl. For more information about uploading a package, see Resource operations or Use a third-party package in a Python UDF.
For a list of standard libraries that Python 3 supports, see Python 3 standard library.
Function signatures and data types
Format of function signatures:
@annotate(<signature>)The signature parameter is a string that specifies the data types of input parameters and return value. When you run a UDF, the data types of the input parameters and return value of the UDF must be consistent with the data types specified in the function signature. The data type consistency is checked during semantic parsing. If the data types are inconsistent, an error is returned. Format of a signature:
'arg_type_list -> type'Parameter description:
arg_type_list: specifies the data types of input parameters. If multiple input parameters are used, their data types are separated by commas (,). The following data types are supported: BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(precision,scale), CHAR, and VARCHAR. Complex data types, such as ARRAY, MAP, and STRUCT, and nested complex data types are also supported.arg_type_listcan be represented by an asterisk (*) or left empty ('').If
arg_type_listis represented by an asterisk (*), a random number of input parameters are allowed.If
arg_type_listis left empty (''), no input parameters are used.
type: specifies the data type of the return value. For a UDF, only one column of values is returned. The following data types are supported: BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, and DECIMAL(precision,scale). Complex data types, such as ARRAY, MAP, and STRUCT, and nested complex data types are also supported.
When you write UDF code, you can select a data type based on the MaxCompute data type edition that is used by your MaxCompute project. For more information about MaxCompute data type editions and the data types supported in each edition, see Data type editions.
The following table provides examples of valid function signatures.
Function signature | Description |
| The data types of the input parameters are BIGINT and DOUBLE and the data type of the return value is STRING. |
| A random number of input parameters are used and the data type of the return value is STRING. |
| No input parameters are used and the data type of the return value is DOUBLE. |
| The data type of the input parameters is ARRAY<BIGINT> and the data type of the return value is STRUCT<x:STRING, y:INT>. |
| No input parameters are used and the data type of the return value is MAP<BIGINT, STRING>. |
The following table describes the mappings between the data types that are supported in MaxCompute SQL and the Python 2 data types. You must write Python UDFs based on the mappings to ensure the consistency of data types.
|
MaxCompute SQL type |
Python 3 type |
|
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 |
Referencing resources
You can reference files or tables in Python 2 UDF code by using the odps.distcache module.
-
odps.distcache.get_cache_file(resource_name, mode): Returns the content of a specified file resource in the specifiedmode.resource_nameis a string that specifies the name of an existing table in your MaxCompute project. If the table name is invalid or the table does not exist, an error is returned.-
The
modeparameter is a STRING. The default value is't'. If you setmodeto't', the file is opened in text mode. If you setmodeto'b', the file is opened in binary mode. The return value is a file-like object. If this object is no longer used, you must call the
closemethod to release the open file.
The following code shows how to reference a file.
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) -
odps.distcache.get_cache_table(resource_name): Returns the content of a specified table resource.-
The
resource_nameparameter specifies an existing table resource in the current MaxCompute project. An exception is thrown if the resource name is invalid or the resource does not exist. Supported data types: BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, FLOAT, CHAR, VARCHAR, BINARY, DATE, DECIMAL, ARRAY, MAP, and STRUCT. -
The return value is a Generator. Each iteration yields one table record as an array.
-
The following code shows how to reference a table.
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)Calling UDFs
After developing a Python 3 UDF by following the development workflow, you can call it in MaxCompute SQL as follows:
Enable Python 3
By default, MaxCompute uses Python 2. To use Python 3, include the following session flag in your SQL statement.
set odps.sql.python.version=cp37;
Call the function
Use a UDF in a MaxCompute project: The method is similar to that of using built-in functions. You can use a user-defined function in the same way that you use a built-in function.
Use a UDF across projects: Use a UDF of Project B in Project A. The following statement shows an example:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. For more information about cross-project sharing, see Cross-project resource access based on packages.
Migrate Python 2 UDFs
Python 2 reached its EOL in early 2020. We recommend migrating your projects based on their type:
-
New projects: For new MaxCompute projects or projects where you are writing Python UDFs for the first time, use Python 3 for all Python UDFs.
-
Existing projects: For projects with many Python 2 UDFs, exercise caution when enabling Python 3. To gradually migrate, use the following methods:
-
New jobs and new UDFs: Use Python 3 to write UDFs and enable Python 3 at the session level. For more information about how to enable Python 3, see Enable Python 3.
-
Python 2 UDFs: Rewrite Python 2 UDFs to make them compatible with both Python 2 and Python 3. For more information about how to rewrite the UDFs, see Porting Python 2 Code to Python 3.
NoteIf you write public UDFs shared across multiple MaxCompute projects, ensure they are compatible with both Python 2 and Python 3.
-