Multi_index详解

本文深入解析EOS的Multi-Index API,介绍其在智能合约中读取和修改数据库的作用,详细阐述Multi-Index表的定义、使用及操作,包括主键、二级索引、迭代器等关键概念。

Multi-Index API 简介

1、Multi-Index API 是EOS提供的访问数据库的C++接口,使智能合约能够读取和修改EOS数据库中的数据;
2、Multi-Index源文件位置在:eos/contracts/eosiolib/multi_index.hpp;
3、Multi-Index来源于boost库的boost::multi_index,但又有一些重要不同,eos中使用eosio::multi_index;
4、eosio::multi_index在概念上和传统数据库的“表(table)”类似,数据“行(rows)”是独立的对象,数据“列(columns)”是对          象的成员属性;
5、eosio::multi_index提供和传统数据库的“键(key)”类似的成员属性,用于快速查找对象;
6、eosio::multi_index允许使用自定义函数作为索引,但它的返回值是受限制的,只能是支持的键类型;
7、eosio::multi_index支持主键(primary key),但必须是唯一的无符号64位整型(uint64_t);
8、eosio::multi_index按主键排序时,使用升序;
9、Multi-Index表允许多索引排序,最多可以使用16个二级索引;
10、Multi-Index迭代器可以双向迭代,即const_iterator或const_reverse_iterator;  
11、二级索引作为Multi-Index表构造函数的一部分创建,不支持直接构建。

创建Multi-Index表

1、使用C++类(class)或结构(struct)定义对象;
2、在class或struct中,定义一个const成员函数:primary_key(),返回uint64_t类型的主键值;
3、确定二级索引(最多支持16个),二级索引不局限于uint64_t,它支持更多类型;

二级索引支持的键类型:

idx64:64位无符号整型键
idx128:128位无符号整型键
idx256:256位固定大小字典键
idx_double:双精度浮点键
idx_long_double:四倍精度浮点键

4、为每个二级索引定义extractor,即一个函数,用于从Multi-Index表的函数中获取键,这个函数会被indexed_by(后面会讲)用到。

一个完整的Multi-Index表定义:

/定义address表,i64表示索引使用默认的uint64_t类型
 
//@abi table address i64
struct address {
    uint64_t account;
    string name;
    uint64_t phone;
    uint64_t liked;
 
    //定义address表的主键
    uint64_t primary_key() const { return account; }
    //定义extractor,二级索引是phone
    uint64_t get_phone() const {return phone; }
 
    //序列化
    EOSLIB_SERIALIZE(address, (account)(name)(phone)(liked));
};

如何使用Multi-Index表

1、实例化Multi-Index表;

typedef eosio::multi_index< N(address), address,
            indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>>
    address_index;
    
address_index addresses(_self, _self);

2、CRUD函数

  • 增:emplace
  • 删:erase
  • 改:modify
  • 查:get/find

类模板

在multi_index.hpp中,multi_index类的定义使用了类模板:

第一个参数是表名,第二个参数是表对象的类型,其余为可变参数Indices(二级索引),二级索引的数量不能超过16个。

  • 表名(TableName)

multi_index表的名称,最长12个字符,只能由小写字母、数字1-5、“.”组成。

  • T

表对象的类型,即表中“行(row)”的定义。

  • Indices
  1. 最多16个二级索引的list;
  2. 每个二级索引必须是默认可构造的class或struct;
  3. 每个二级索引必须在表结构中定义一个常量函数,返回二级索引类型或其引用;
  4. 建议使用eosio::const_mem_fun模板,它是boost::multi_index::const_mem_fun的一个类型别名。
     

使用模板参数的例子:

typedef eosio::multi_index< N(address), address,
        indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>>
address_index;
address_index addresses(_self, _self);

构造函数

multi_index构造函数有两个参数:code,scope它们都是uint64_t类型,可以用来为表建立访问权限。

  • code

拥有这张multi_index表的账户,该账户拥有对合约数据的读写权限

  • scope

code层级内的范围标识符(即一行数据)

不支持的C++特性

  • eosio::multi_index不支持拷贝构造函数(Copy constructor)
  • eosio::multi_index不支持赋值运算符(Assignment operator)

表的操作

1、emplace

  • 添加一个新对象(row)到表中
const_iterator emplace( unit64_t payer, Lambda&& constructor )

参数:
payer:为新对象使用的存储付费的账户;
constructor:lambda函数,可以让新创建的对象就地初始化。

返回值:
返回一个新创建的对象的主键迭代器

前置条件:
payer是被当前Action授权的有效账户,允许为使用存储付费

操作结果:
带有唯一主键的新对象在multi-index表中被创建;
这个对象会被序列化,然后写入表中;
如果表不存在,则创建表。
二级索引被更新,用以引用新添加的对象;
如果二级索引表不存在,则创建它们。
payer为创建新对象所使用的存储付费;
如果multi-index表和二级索引表需要被创建,则payer为表的创建付费。

异常:
当前接收者(multi_index的code参数)不是表的拥有者时,抛出异常。

2、erase

使用主键从表中删除现有对象(两种形式)

const_iterator erase( const_iterator itr )
 
void erase( const object_type& obj )

参数:
itr:指向待删除对象的迭代器;
obj:待删除对象的引用。

返回值:
使用itr查找对象时,返回被删除对象之后的对象的指针。

操作结果:
对象从表中删除,相关的存储被回收;
表相关的二级索引被更新;
退还被删除对象的payer所支付的存储费用和相关费用。

异常:
待删除对象不存在时、Action无权修改表数据时、给定迭代器无效时,抛出异常。

3、modify

修改表中已存在的对象(两种形式)、

void modify( const_iterator itr, uint64_t payer, Lambda&& updater )
void modify( const object_type &obj, uint64_t payer, Lambda&& updater )

参数:
itr:指向待更新对象的迭代器;
obj:待更新对象的引用;
payer:为更新数据付费的账户,为0表示更新数据的payer和创建时的payer相同;
updater:用于更新目标对象的lambda函数。

前置条件:
itr指向的对象,或obj引用的对象是存在的;
payer是被当前Action授权的有效账户,允许为使用存储付费。

操作结果:
更新后的对象被序列化,然后替换表中的现有对象;
二级索引被更新,被更新对象的主键不变;
payer为更新对象所使用的存储付费;
如果payer和该对象现有的payer相同,只需要为现有对象和更新对象不同的部分付费,如果差额为负,还会退还费用;
如果payer和该对象现有的payer不同,则会退还费用给现有的payer。

异常:
无效的前提条件下调用,会抛出异常,并中止执行;
当前接收者(multi_index的code参数)不是表的拥有者时,抛出异常。

4、get

使用主键从表中查询已存在的对象

const object_type& get( uint64_t primary ) const
  • 参数

primary:要查询对象的主键值

  • 返回值

包含指定主键的对象的常量引用

  • 异常

没有任何对象与给定的主键匹配时,抛出异常。

5、find

  • 使用主键从表中查询已存在的对象
const_iterator find( uint64_t primary ) const
  • 参数

primary:要查询对象的主键值

  • 返回值

返回一个查询到的对象的迭代器;

如果没有查询到指定对象,返回一个end迭代器。

成员访问

1、获取拥有主表的账户名

uint64_t get_code() const

2、在code下的范围的范围id(scope id),在该范围内可以找到期望的主表实例

uint64_t get_scope() const

迭代器

Multi-Index迭代器遵循C++迭代器模式,所有迭代器都是双向迭代,即const_iterator或const_reverse_iterator。

迭代器可以被间接引用,以提供对Multi-Index表中对象的访问。

1、begin & cbegin

返回指向对象类型的、从最小主键值开始的迭代器。

const_iterator begin() const
const_iterator cbegin() const

2、end & cend

返回指向虚拟行的迭代器,代表刚刚过去的最后一行,不能被间接引用;
可以向后推进,不能向前推进。

const_iterator end() const
const_iterator cend() const

3、rbegin & crbegin

返回和begin/cbegin类似的,但反向的迭代器

const_iterator rbegin() const​​​​​​​
const_iterator crbegin() const

4、rend & crend

返回和end/cend类似的,但反向的迭代器

const_iterator rend() const
 
const_iterator crend() const

5、lower_bound

查找小于等于给定主键值的对象

const_iterator lower_bound( uint64_t primary ) const

6、upper_bound

查找大于给定主键值的对象

const_iterator upper_bound( uint64_t primary ) const

7、get_index

返回一个适当类型的二级索引

secondary_index get_index<IndexName>()  
 
secondary_index get_index<IndexName>() const

8、iterator_to

返回给定对象的迭代器

const_iterator iterator_to( const object_type& obj ) const

9、indexed_by

indexed_by结构体用于实例化Multi-Index表的索引,EOS中,最多可以指定16个二级索引。

indexed_by在multi_index.hpp中的定义如下:


template<uint64_t IndexName, typename Extractor>
struct indexed_by {
   enum constants { index_name   = IndexName };
   typedef Extractor secondary_extractor_type;
};
  • 参数IndexName是索引的名称,这个名称是base32编码后的64位整数,且需要符合EOS命名规范:

最多13个字符,前12个字符只能是小写字母、0-5、“.”;
如果有第13个字符,则只能是小写字母a-p、“.”。

  • 参数Extractor是一个函数,用于从Multi-Index表的函数中获取键,返回一个二级索引类型或二级索引类型的引用。

推荐使用eosio::const_mem_fun模板,它是boost::multi_index::const_mem_fun的类型别名。

  • 例子:
eosio::multi_index< N(address), address,
    indexed_by<N(phone), const_mem_fun<address, uint64_t, &address::get_phone>>>

Multi-Index表的名字是N(address);
N是一个宏,可以把base32编码后的字符串转换为uint64_t;
Multi-Index表的对象类型是address;
Multi-Index通过名为N(phone)的二级索引进行检索;
const_mem_fun是一个用于address类型的键提取器(key extractor );
const_mem_fun提取的键类型是uint64_t,通过address::get_phone函数获取键。

10、完整例子:

/@abi action
void likebyphone(uint64_t phone) {
    address_index addresses(_self, _self);
 
    auto phone_index = addresses.get_index<N(phone)>();
    auto itr = phone_index.lower_bound(phone);
    for(; itr != phone_index.end() && itr->phone == phone; ++itr) {
        phone_index.modify(itr, 0, [&](auto& address){
            eosio::print("Liking: ", address.name.c_str(), "\n");
            address.liked++;
        });
    }
}

工具函数

available_primary_key

uint64_t available_primary_key() const

返回一个可用(未使用)的主键值,用于主键严格自增的表,它不会被设置为自定义值。

 

转自:https://blog.youkuaiyun.com/yhc166188/article/details/82724494

<think>根据用户要求,我需要提供关于pandas中reset_index方法的详细解释。参考提供的四个引用内容,我将整合信息并结构化回答。 首先注意系统指令要求:行内数学表达式用$...$,独立公式用$$...$$。但本问题不涉及数学公式,重点在方法解释。 用户明确要求"详细解释",因此我会覆盖以下方面: 1. 方法定义和基本功能 2. 主要参数说明(结合引用[1][3][4]) 3. 使用场景(特别强调groupby后的应用,引用[2]) 4. 示例演示(结合引用[2][3][4]的案例) 特别注意引用[2]提到的与set_index的逆操作关系。 计划回答结构: 1. 方法概述:一句话定义 + 核心功能 2. 参数详解:表格形式列出关键参数(参考引用[1][3][4]) 3. 使用示例:两个典型场景(保留原索引和删除原索引) 4. 常见应用场景:特别说明groupby后重置索引(引用[2]) 5. 注意事项:inplace参数的影响等 最后按要求生成相关问题 </think>### pandas中`reset_index`方法详细解释 `reset_index()`是Pandas中用于重置DataFrame或Series索引的核心方法,主要解决索引混乱或需将索引转换为普通列的场景。以下是其核心要点: --- #### **一、核心功能** 1. **索引重置** 将当前索引(自定义索引、多层索引等)转换为默认整数索引$(0,1,2,\dots)$,同时可选择是否保留原索引。 - **保留原索引**:原索引转为新列添加到数据中(默认`drop=False`)[^2][^3] - **删除原索引**:直接丢弃原索引(`drop=True`)[^1][^4] 2. **与`set_index()`的逆操作** 如引用[^2]强调:`reset_index()`和`set_index()`互为逆操作,常用于索引与列的互换。 --- #### **二、关键参数详解** | 参数 | 说明 | 默认值 | |------|------|--------| | **`drop`** | 是否丢弃原索引。`True`删除,`False`保留为列 | `False` | | **`inplace`** | 是否原地修改数据。`True`直接修改原对象,`False`返回新对象 | `False` | | **`level`** | 针对多层索引(MultiIndex),指定要重置的层级 | `None`(重置所有) | | **`col_fill`** | 当列名冲突时,填充新列名的层级名 | `""`(空字符串) | | **`allow_duplicates`** | 是否允许重置后存在重复列名 | `False` | > 💡 特殊说明: > - **Series对象**:重置后返回DataFrame(原索引转为列,值列名为原Series名)[^3] > - **多层索引**:可通过`level`指定部分索引重置(如`level=1`) --- #### **三、典型使用场景** 1. **`groupby()`聚合后重置索引**(最高频场景[^2]) ```python # 分组聚合后索引变为分组键 grouped = df.groupby('grade').mean() print(grouped) ''' score grade A 34.0 B 58.666667 ''' # 重置索引将分组键恢复为列 reset_df = grouped.reset_index() print(reset_df) ''' grade score 0 A 34.000000 1 B 58.666667 ''' ``` 2. **丢弃旧索引(`drop=True`)** ```python df = pd.DataFrame({"A": [1,2,3], "B": [4,5,6]}, index=['x','y','z']) reset_df = df.reset_index(drop=True) # 删除原索引 ''' A B 0 1 4 1 2 5 2 3 6 ''' ``` 3. **多层索引部分重置** ```python multi_df = df.set_index(['grade', 'name']) partial_reset = multi_df.reset_index(level='grade') # 仅重置grade层级 ``` --- #### **四、注意事项** 1. **避免列名冲突**:若原索引名与列名重复,用`col_fill`指定新列名前缀。 2. **内存优化**:大数据集慎用`inplace=True`,错误操作不可逆。 3. **与`set_index`配合**: ```python df.set_index('id').query('score>60').reset_index() # 链式调用 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值