Postgres-XC 聚合原理 以及 如何编写聚合函数

本文涉及的产品
PolarDB Agent Express,2核4GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介:

Postgres-XC聚合与PostgreSQL的聚合有一定的区别, 因为Postgres-XC的数据存储在datanode, 聚合时数据可能分布在多个datanode上.
Postgres-XC支持传统的聚合方法, 聚合操作可以将数据从所有的数据节点传到coordinator节点后, 在coordinator节点进行聚合. 但是这种方法对于数据量较大的情况效率明显偏低.
Postgres-XC还支持另一种聚合方式, 就是数据在各自的datanode执行, 形成结果后, 将datanode聚合的结果传输到coordinator节点再次聚合.

集群聚合与单节点聚合的区别

Postgres-XC相比PostgreSQL的聚合增加了cfunc :
sfunc( internal-state, next-data-values ) ---> next-internal-state
这个过程是在datanode节点完成的. input 是该datanode节点上的所有行(一次1行的进行调用).

cfunc( internal-state, internal-state ) ---> next-internal-state
这个过程是在coordinator节点完成的. input是datanode节点的最终结果.

ffunc( internal-state ) ---> aggregate-value
这个过程是在coordinator节点完成的. input是cfunc的结果.

集群聚合函数的语法

CREATE AGGREGATE name ( input_data_type [ , ... ] ) (  
    SFUNC = sfunc,  
    STYPE = state_data_type  
    [ , CFUNC = cfunc ]  
    [ , FINALFUNC = ffunc ]  
    [ , INITCOND = initial_condition ]  
    [ , INITCOLLECT = initial_collection_condition ]  
    [ , SORTOP = sort_operator ]  
)  

单点聚合函数的语法

CREATE AGGREGATE name (  
    BASETYPE = base_type,  
    SFUNC = sfunc,  
    STYPE = state_data_type  
    [ , FINALFUNC = ffunc ]  
    [ , INITCOND = initial_condition ]  
    [ , SORTOP = sort_operator ]  
)  

sfunc和stype是必须的. 如果没有定义cfunc那么这个聚合就只支持传统的PostgreSQL聚合方法.
如果定义了cfunc, 那么这个聚合支持传统的聚合方法, 同时还支持分布式聚合方法.

例子

创建sfunc

digoal=# create or replace function d_sum(int,int) returns int as $$  
select $1+$2;  
$$ language sql strict;  

创建聚合

digoal=# create AGGREGATE d_sum(int)          
(  
sfunc=d_sum,  
stype=int,  
cfunc=d_sum,  
initcond='0',  
initcollect='0'  
);  

创建测试表

digoal=# create table t1(id int, info text) distribute by (id) to group gp0;  
digoal=# insert into t1 select generate_series(1,10),'test';  
digoal=# insert into t1 values (null,'test');  

比较d_sum和sum的聚合结果

digoal=# select d_sum(id) from t1;  
 d_sum   
-------  
    55  
(1 row)  
digoal=# select sum(id) from t1;  
 sum   
-----  
  55  
(1 row)  

比较sum和d_sum的执行计划

digoal=# explain (analyze,verbose,buffers) select d_sum(id) from t1;  
                                                              QUERY PLAN                                                              

------------------------------------------------------------------------------------------------------------------------------------  
---  
 Aggregate  (cost=250.00..250.01 rows=1 width=4) (actual time=1.657..1.657 rows=1 loops=1)  
   Output: d_sum((d_sum(t1.id)))  
   ->  Materialize  (cost=0.00..0.00 rows=0 width=0) (actual time=0.703..0.743 rows=5 loops=1)  
         Output: (d_sum(t1.id))  
         ->  Data Node Scan on "__REMOTE_GROUP_QUERY__"  (cost=0.00..0.00 rows=1000 width=4) (actual time=0.702..0.740 rows=5 loops=  
1)  
               Output: d_sum(t1.id)  
               Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
               Remote query: SELECT d_sum(group_1.id)  FROM (SELECT id, info FROM ONLY t1 WHERE true) group_1     
 Total runtime: 1.689 ms  
(9 rows)  

d_sum, sum两者都是有了分布式聚合.

digoal=# explain (analyze,verbose,buffers) select sum(id) from t1;  
                                                              QUERY PLAN                                                              

------------------------------------------------------------------------------------------------------------------------------------  
---  
 Aggregate  (cost=2.50..2.51 rows=1 width=4) (actual time=0.674..0.674 rows=1 loops=1)  
   Output: pg_catalog.sum((sum(t1.id)))  
   ->  Materialize  (cost=0.00..0.00 rows=0 width=0) (actual time=0.507..0.659 rows=5 loops=1)  
         Output: (sum(t1.id))  
         ->  Data Node Scan on "__REMOTE_GROUP_QUERY__"  (cost=0.00..0.00 rows=1000 width=4) (actual time=0.507..0.655 rows=5 loops=  
1)  
               Output: sum(t1.id)  
               Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
               Remote query: SELECT sum(group_1.id)  FROM (SELECT id, info FROM ONLY t1 WHERE true) group_1     
 Total runtime: 0.705 ms  
(9 rows)  

在没有cfunc的情况下, 数据必须从所有的datanode汇总到coordinator节点后执行sfunc聚合. 如下:

digoal=# drop AGGREGATE d_sum(int);  
DROP AGGREGATE  
digoal=# create AGGREGATE d_sum(int)   
(  
sfunc=d_sum,  
stype=int,  
initcond='0'  
);  
CREATE AGGREGATE  
digoal=# select d_sum(id) from t1;  
 d_sum   
-------  
    55  
(1 row)  
digoal=# explain (analyze,verbose,buffers) select d_sum(id) from t1;  
                                                 QUERY PLAN                                                   
------------------------------------------------------------------------------------------------------------  
 Aggregate  (cost=250.00..250.01 rows=1 width=4) (actual time=1.981..1.981 rows=1 loops=1)  
   Output: d_sum(id)  
   ->  Data Node Scan on t1  (cost=0.00..0.00 rows=1000 width=4) (actual time=0.567..0.610 rows=11 loops=1)  
         Output: id, info  
         Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
         Remote query: SELECT id, info FROM ONLY t1 WHERE true  
 Total runtime: 2.015 ms  
(7 rows)  

聚合数据处理流程简介

-1. 数据汇总聚合方法数据处理流程 :

Two phased aggregation - is used when the entire aggregation takes place on the Coordinator node.   
In first phase called transition phase, Postgres-XC creates a temporary variable of data type stype to hold the current internal state of the aggregate.   
创建初始变量 tmpvar(stype)  

At each input row, the aggregate argument value(s) are calculated and the state transition function is invoked with the current state value and the new argument value(s) to calculate a new internal state value.   
per row迭代计算tmpvar : sfunc(tmpvar, vardict parameters) -> tmpvar   

After all the rows have been processed, in the second phase or finalization phase the final function is invoked once to calculate the aggregate's return value.   
If there is no final function then the ending state value is returned as-is.  
final函数 : ffunc(tmpvar) -> result(对应聚合函数输出类型)   

-2. 分布式聚合方法数据处理流程 :

Three phased aggregation - is used when the process of aggregation is divided between Coordinator and Datanodes.   
In this mode, each Postgres-XC Datanode involved in the query carries out the first phase named transition phase.   
This phase is similar to the first phase in the two phased aggregation mode discussed above, except that, every Datanode applies this phase on the rows available at the Datanode.   
The result of transition phase is then transferred to the Coordinator node. Second phase called collection phase takes place on the Coordinator.   
Postgres-XC Coordinator node creates a temporary variable of data type stype to hold the current internal state of the collection phase.   
For every input from the Datanode (result of transition phase on that node), the collection function is invoked with the current collection state value and the new transition value (obtained from the Datanode) to calculate a new internal collection state value.   
After all the transition values from data nodes have been processed, in the third or finalization phase the final function is invoked once to calculate the aggregate's return value.   
If there is no final function then the ending collection state value is returned as-is.  

聚合中的数据类型简介

sfunc( internal-state, next-data-values ) ---> next-internal-state  
cfunc( internal-state, internal-state ) ---> next-internal-state  
ffunc( internal-state ) ---> aggregate-value  

聚合语法中的 input_data_type 对应 next-data-values
聚合语法中的 state_data_type 对应 internal-state
聚合最终输出类型有两种aggregate-value 或者 internal-state.
-1. 当定义了finalfunc时是finalfunc的返回类型.
-2. 当没有定义finalfunc时, 输出类型是cfunc或者sfunc的返回类型, 也就是internal-state类型, 既stype.
从流程来分析, sfunc的第一个参数的数据类型以及返回数据类型都必须和stype定义的类型相同.
sfunc的第二个参数必须和聚合的input类型相同.
cfunc的两个参数以及返回类型都必须和stype定义的类型相同.
ffunc的输入参数必须和stype定义的类型相同.
所以整个聚合涉及了3个数据类型:
-1. 聚合的输入类型,
-2. stype , (同时initcond, initcollect类型=stype);
-3. 以及 finalfunc的返回类型.

聚合对空值的处理简介

与sfunc,cfunc,finalfunc的定义有关, 是否strict. 是则忽略null值, 不进行调用.
不是strict则需要函数自己处理null值. 所以要特别小心.

An aggregate function can provide an initial condition, that is, an initial value for the internal transition or collection state value. This is specified and stored in the database as a value of type text, but it must be a valid external representation of a constant of the state value data type. If it is not supplied then the state value starts out null.  

If the collection function is declared "strict", then it cannot be called with null inputs. With such a collection function, aggregate execution behaves as follows. Null state transition results are ignored (the function is not called and the previous collection state value is retained). If the initial state value is null, then at the first non-null state transition result replaces the collection state value, and the collection function is invoked at subsequent rows with all-nonnull transition values. This is handy for implementing aggregates like max.  

If the state transition function is declared "strict", then it cannot be called with null inputs. With such a transition function, aggregate execution behaves as follows. Rows with any null input values are ignored (the function is not called and the previous state value is retained). If the initial state value is null, then at the first row with all-nonnull input values, the first argument value replaces the state value, and the transition function is invoked at subsequent rows with all-nonnull input values. This is handy for implementing aggregates like max. Note that this behavior is only available when state_data_type is the same as the first input_data_type. When these types are different, you must supply a nonnull initial condition or use a nonstrict transition function.  

If the state transition and/or collection function is not strict, then it will be called unconditionally at each input row, and must deal with null inputs and null transition/collection values for itself. This allows the aggregate author to have full control over the aggregate's handling of null values.  

If the final function is declared "strict", then it will not be called when the ending state value is null; instead a null result will be returned automatically. (Of course this is just the normal behavior of strict functions.) In any case the final function has the option of returning a null value. For example, the final function for avg returns null when it sees there were zero input rows.  

例如count, id有一条为null :

digoal=# select count(id) from t1;  
 count   
-------  
    10  
(1 row)  
digoal=# select count(*) from t1;  
 count   
-------  
    11  
(1 row)  

假如把d_sum(int,int)函数改成called on null input.
这种情况下, 即使row取到的是空值也会调用func. 因此null+val=null, 最后得到的就是null了.

digoal=# alter function d_sum(int,int) called on null input;  
ALTER FUNCTION  
digoal=# select d_sum(id) from t1;  
 d_sum   
-------  

(1 row)  

聚合的优化

使用btree.

sort_operator  
The associated sort operator for a MIN- or MAX-like aggregate. This is just an operator name (possibly schema-qualified). The operator is assumed to have the same input data types as the aggregate (which must be a single-argument aggregate).  

Aggregates that behave like MIN or MAX can sometimes be optimized by looking into an index instead of scanning every input row. If this aggregate can be so optimized, indicate it by specifying a sort operator. The basic requirement is that the aggregate must yield the first element in the sort ordering induced by the operator; in other words:  

SELECT agg(col) FROM tab;  
must be equivalent to:  
SELECT col FROM tab ORDER BY col USING sortop LIMIT 1;  

Further assumptions are that the aggregate ignores null inputs, and that it delivers a null result if and only if there were no non-null inputs. Ordinarily, a data type's < operator is the proper sort operator for MIN, and > is the proper sort operator for MAX. Note that the optimization will never actually take effect unless the specified operator is the "less than" or "greater than" strategy member of a B-tree index operator class.  

定义类似count(*)这样的无参数聚合

digoal=# select * from pg_aggregate where aggfnoid::text ~ 'count';  
     aggfnoid     |      aggtransfn       |   aggcollectfn   | aggfinalfn | aggsortop | aggtranstype | agginitval | agginitcollect   
------------------+-----------------------+------------------+------------+-----------+--------------+------------+----------------  
 pg_catalog.count | int8inc_any           | int8_sum_to_int8 | -          |         0 |           20 | 0          | 0  
 pg_catalog.count | int8inc               | int8_sum_to_int8 | -          |         0 |           20 | 0          | 0  
digoal=# \df+ int8inc_any  
                                                                        List of functions  
   Schema   |    Name     | Result data type | Argument data types |  Type  | Volatility |  Owner   | Language | Source code |        
      Description               
------------+-------------+------------------+---------------------+--------+------------+----------+----------+-------------+------  
------------------------------  
 pg_catalog | int8inc_any | bigint           | bigint, "any"       | normal | immutable  | postgres | internal | int8inc_any | incre  
ment, ignores second argument  
(1 row)  

digoal=# \df+ int8inc  
                                                           List of functions  
   Schema   |  Name   | Result data type | Argument data types |  Type  | Volatility |  Owner   | Language | Source code | Descripti  
on   
------------+---------+------------------+---------------------+--------+------------+----------+----------+-------------+----------  
---  
 pg_catalog | int8inc | bigint           | bigint              | normal | immutable  | postgres | internal | int8inc     | increment  
(1 row)  
digoal=# select proisstrict from pg_proc where proname='int8inc';  
 proisstrict   
-------------  
 t  
(1 row)  
digoal=# select proisstrict from pg_proc where proname='int8inc_any';  
 proisstrict   
-------------  
 t  
(1 row)  

以上count实际上分了两种, 一种是count(列名字), 一种是count(*). int8inc_any用于count(列名), int8inc用于count(*).
举例 :

-- 创建sfunc  
digoal=# create or replace function d_count(int8) returns int8 as $$       
select $1+1;   
$$ language sql strict;  
-- 创建cfunc  
digoal=# create or replace function d_count(int8,int8) returns int8 as $$  
select $1+$2;  
$$ language sql strict;  
-- 创建聚合  
digoal=# create aggregate d_count(*)                                       
(              
sfunc=d_count,  
stype=int8,  
cfunc=d_count,  
initcond='0',  
initcollect='0'  
);  
digoal=# select d_count(*) from t1;  
 d_count   
---------  
      11  
(1 row)  
digoal=# explain (analyze,verbose,buffers) select d_count(*) from t1;  
                                                              QUERY PLAN                                                              

------------------------------------------------------------------------------------------------------------------------------------  
---  
 Aggregate  (cost=250.00..250.01 rows=1 width=0) (actual time=1.308..1.308 rows=1 loops=1)  
   Output: sj.d_count(*)  
   ->  Materialize  (cost=0.00..0.00 rows=0 width=0) (actual time=0.712..0.725 rows=5 loops=1)  
         Output: (d_count(*))  
         ->  Data Node Scan on "__REMOTE_GROUP_QUERY__"  (cost=0.00..0.00 rows=1000 width=0) (actual time=0.712..0.721 rows=5 loops=  
1)  
               Output: d_count(*)  
               Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
               Remote query: SELECT d_count(*)  FROM (SELECT id, info FROM ONLY t1 WHERE true) group_1     
 Total runtime: 1.339 ms  
(9 rows)  

注意, 为什么d_count(id)得到的结果不是10呢? 因为我没有定义d_count("any"), 只定义了d_count(*) :

digoal=# select d_count(id) from t1;        
 d_count   
---------  
       2  
       3  
       4  
       9  
      10  
       5  
       6  
       7  
       8  
      11  

(11 rows)  

因为这里的d_count是普通函数, 而不是聚合.
实际上调用的是d_count(int8)函数.
将sfunc,cfunc改名后就更能看出来了.

digoal=# alter function d_count(int8) rename to s_d_count;  
ALTER FUNCTION  
Time: 18.591 ms  
digoal=# alter function d_count(int8,int8) rename to c_d_count;  
ALTER FUNCTION  
Time: 4.049 ms  
digoal=# create aggregate d_count(*)                                       
(              
sfunc=s_d_count,  
stype=int8,  
cfunc=c_d_count,  
initcond='0',  
initcollect='0'  
);  
CREATE AGGREGATE  
Time: 21.752 ms  
digoal=# explain (analyze,verbose,buffers) select d_count(*) from t1;  
                                                              QUERY PLAN                                                              

------------------------------------------------------------------------------------------------------------------------------------  
---  
 Aggregate  (cost=250.00..250.01 rows=1 width=0) (actual time=1.732..1.732 rows=1 loops=1)  
   Output: sj.d_count(*)  
   ->  Materialize  (cost=0.00..0.00 rows=0 width=0) (actual time=0.986..1.008 rows=5 loops=1)  
         Output: (d_count(*))  
         ->  Data Node Scan on "__REMOTE_GROUP_QUERY__"  (cost=0.00..0.00 rows=1000 width=0) (actual time=0.985..1.002 rows=5 loops=  
1)  
               Output: d_count(*)  
               Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
               Remote query: SELECT d_count(*)  FROM (SELECT id, info FROM ONLY t1 WHERE true) group_1     
 Total runtime: 1.766 ms  
(9 rows)  

Time: 3.088 ms  
digoal=# explain (analyze,verbose,buffers) select d_count(id) from t1;  
ERROR:  function d_count(integer) does not exist  
LINE 1: explain (analyze,verbose,buffers) select d_count(id) from t1...  
                                                 ^  
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.  

定义类似count(*)和count(列名)的函数还有一个count(列名)是怎么定义的呢

digoal=# create or replace function s_d_count(int8,"anyelement") returns int8 as $$  
select $1+1;  
$$ language sql strict;  
CREATE FUNCTION  
Time: 4.715 ms  
digoal=# create aggregate d_count("anyelement")                                       
(              
sfunc=s_d_count,  
stype=int8,  
cfunc=c_d_count,  
initcond='0',  
initcollect='0'  
);  
CREATE AGGREGATE  
Time: 69.536 ms  
digoal=# select d_count(id) from t1;  
 d_count   
---------  
      10  
(1 row)  
Time: 3.694 ms  
digoal=# explain (analyze,verbose,buffers) select d_count(id) from t1;  
                                                              QUERY PLAN                                                              

------------------------------------------------------------------------------------------------------------------------------------  
---  
 Aggregate  (cost=250.00..250.01 rows=1 width=4) (actual time=1.913..1.913 rows=1 loops=1)  
   Output: d_count((d_count(t1.id)))  
   ->  Materialize  (cost=0.00..0.00 rows=0 width=0) (actual time=0.893..0.923 rows=5 loops=1)  
         Output: (d_count(t1.id))  
         ->  Data Node Scan on "__REMOTE_GROUP_QUERY__"  (cost=0.00..0.00 rows=1000 width=4) (actual time=0.892..0.917 rows=5 loops=  
1)  
               Output: d_count(t1.id)  
               Node/s: datanode_1, datanode_2, datanode_3, datanode_4, datanode_5  
               Remote query: SELECT d_count(group_1.id)  FROM (SELECT id, info FROM ONLY t1 WHERE true) group_1     
 Total runtime: 1.947 ms  
(9 rows) 

第二个d_count聚合复用了前面定义的c_d_count(int8,int8).

digoal=# select * from pg_aggregate where aggfnoid::text ~ 'd_count';  
  aggfnoid  |  aggtransfn  | aggcollectfn | aggfinalfn | aggsortop | aggtranstype | agginitval | agginitcollect   
------------+--------------+--------------+------------+-----------+--------------+------------+----------------  
 sj.d_count | sj.s_d_count | c_d_count    | -          |         0 |           20 | 0          | 0  
 sj.d_count | sj.s_d_count | c_d_count    | -          |         0 |           20 | 0          | 0  
(2 rows)  
Time: 2.811 ms  

[注意]

-1. 如果你定义的聚合既支持分布式聚合同时又支持传统的数据汇总聚合, Postgres-XC会根据成本选择合适的聚合方法, 所以在这种情况下, 必须确保两种聚合方法得到的聚合结果是一致的, 否则会有问题.
-2. 聚合最好不要与函数名重复. 否则会难以排错.

[参考]

-1. http://postgres-xc.sourceforge.net/docs/1_0_3/xaggr.html
-2. http://postgres-xc.sourceforge.net/docs/1_0_3/sql-createaggregate.html
-3. http://blog.163.com/digoal@126/blog/static/16387704020121118112533410/

目录
相关文章
|
机器学习/深度学习 传感器 编解码
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(中)
本综述根据三个基本的CV任务和不同的数据流类型,全面调查了100多种不同的视觉Transformer,并提出了一种分类法,根据其动机、结构和应用场景来组织代表性方法。由于它们在训练设置和专用视觉任务上的差异,论文还评估并比较了不同配置下的所有现有视觉Transformer。此外,论文还揭示了一系列重要但尚未开发的方面,这些方面可能使此类视觉Transformer能够从众多架构中脱颖而出,例如,松散的高级语义嵌入,以弥合视觉Transformer与序列式之间的差距。最后,提出了未来有前景的研究方向。
一文详解视觉Transformer在CV中的现状、趋势和未来方向(分类/检测/分割/多传感器融合)(中)
|
存储 关系型数据库 定位技术
YashanDB在地理信息系统(GIS)领域的关键功能和技术优势
本文介绍了GIS技术及其在崖山数据库(YashanDB)中的应用与优化。GIS是一种用于管理和分析地理数据的系统,具备数据捕获、存储、展示和分析等功能。YashanDB针对GIS领域提供了原生GIS数据库引擎,支持多种空间数据类型,并通过R-tree空间索引优化查询性能。测试对比显示,YashanDB在创建R-tree索引及处理大规模空间数据时优于PostgreSQL,展现了其高效的空间计算能力,适用于数字孪生城市等实际场景。
YashanDB在地理信息系统(GIS)领域的关键功能和技术优势
|
Java 关系型数据库 数据库连接
springboot+mybatis配置多数据源实战
一般一个项目中只会连接一个数据库.但是随着需求变更,会要求同一个项目中连接多个数据库,本文就讲一下如何在一个项目中对多个数据库进行连接.本文基于springboot+mybatis介绍如何进行多数据源连接(本文演示配置两个数据库,配置多个同理).
springboot+mybatis配置多数据源实战
|
传感器 Web App开发 编解码
基于51单片机的智能热水器设计
基于51单片机的智能热水器设计
554 0
|
关系型数据库 MySQL 数据库
Navicat备份数据库
涵盖`Navicat`数据库备份、数据安全及备份策略等主题。文库采用精美主题,提升阅读体验。
487 1
Navicat备份数据库
|
自然语言处理 并行计算 数据可视化
免费开源法律文档比对工具:技术解析与应用
这款免费开源的法律文档比对工具,利用先进的文本分析和自然语言处理技术,实现高效、精准的文档比对。核心功能包括文本差异检测、多格式支持、语义分析、批量处理及用户友好的可视化界面,广泛适用于法律行业的各类场景。
852 1
|
存储 自然语言处理 固态存储
初次使用 Elasticsearch 遇多种分词难题?那是你没掌握这些原理
命名有包含搜索关键词的文档,但结果却没有?存进去的文档被分成哪些词(term)了?自定义分词规则,但感觉好麻烦呢,无从下手?
6389 0
初次使用 Elasticsearch 遇多种分词难题?那是你没掌握这些原理
|
机器学习/深度学习 自然语言处理 算法
机器学习和深度学习之间的区别
机器学习和深度学习在实际应用中各有优势和局限性。机器学习适用于一些数据量较小、问题相对简单、对模型解释性要求较高的场景;而深度学习则在处理大规模、复杂的数据和任务时表现出色,但需要更多的计算资源和数据,并且模型的解释性较差。在实际应用中,需要根据具体的问题和需求,结合两者的优势,选择合适的方法来解决问题。
961 0
defineProps和defineEmits
本文介绍了Vue 3中的`defineProps`和`defineEmits`函数的用法,其中`defineProps`用于声明组件的属性(props),以便从父组件接收数据,而`defineEmits`用于定义组件可以触发的自定义事件,从而实现子组件向父组件传递数据的功能。
|
存储 索引 Python
Python中的pop()方法:删除和提取列表元素的利器
概念: Python是一种功能强大的编程语言,拥有许多内置函数和方法来处理不同类型的数据结构。其中,列表(list)是一种常用的数据结构,用于存储和管理多个元素。而pop()方法则是Python列表中的一个重要方法,它能够删除并返回列表中指定索引位置的元素。