Brownie 开发智能合约(入门使用)

2023-11-17

22328f7da197388d6e8f2a0b0fcfe746.png

简介

上篇文章,使用了 Remix 在线 IDE,个人感觉 Remix 在入门智能合约开发时,是很好的上手工具,因为 Remix 帮我们处理好了编译、部署的过程,并且还通过 JavaScript VM 准备好了本地区块链方便我们测试,可谓开箱即用,但毕竟是线上 IDE,功能还是有限。

这里我们使用 Brownie 框架来开发智能合约,Brownie 框架是基于 Python 编写的智能合约开发框架,它可以帮我们快速完成编译、部署、测试等智能合约开发的全流程。

文档:https://eth-brownie.readthedocs.io/en/stable/

Web3.py 基础

因为 Brownie 主要基于 Web3.py 这个库开发而来,在从 Python 角度了解以太坊

编写简单的智能合约

首先,通过 Solidity 编写一个简单智能合约,没错,我们并不能通过 Python 来编写智能合约,利用 Python,只是为了让这个过程更加自动化与工程化,智能合约代码如下:

// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

contract Storage {
    struct People {
        string name;
        uint256 age;
    }
    People[] public people;

    function addPerson(string memory _name, uint256 _age) public {
        people.push(People(_name, _age));
    }
}

上述代码中,通过 struct 关键字定义了一个名为 People 的对象,该对象中有 name 与 age 两个属性,然后基于 People 对象,实例化了 people 数组,然后定义了 addPerson 函数,该方法会接收_name 与_age 参数,然后实例化 People 对象,最后将 People 对象添加到数组中。

这里有个细节,就是参数_name 是字符串,所以需要使用 memory 关键字标注一下。Solidity 中,存储变量的方式有 storage 与 memory 两种。

  • storage 变量:永久存储在区块链中的变量

  • memory 变量:临时的,当外部函数对某合约调用完成时,内存型变量即被移除

Solidity 中的 string 的本质是字符数组(Char Array),如果你不通过 memory 声明,就算_name 是函数参数,Solidity 也会通过storage持续存储它。

编译智能合约与连接本地区块链网络

创建名为【web3py_storage】的文件夹,然后在其中创建 Storage.sol 文件并将智能合约代码复制到文件中。

通过 vscode 打开 webpy_simple_storage 文件夹,创建 base.py,在 base.py 实现对智能合约的编译以及连接上区块链网络的操作。

阅读 web3.py 智能合约相关的文档:https://web3py.readthedocs.io/en/stable/contracts.html

通过文档可知,web3.py 不支持 solidity 的编译,文档中建议我们安装 py-solc-x 库来实现 solidity 的编译,简单安装一下,然后通过 install_solc 方法来下载对应版本的 solidity 编译器。

因为我们的智能合约使用了 Solidity ^0.6.0,所以下载 0.6.0 版本的 solidity 编译器则可,然后按文档的方式设置编译 Solidity 时的配置则可,相关代码如下:

import os
import json
from web3 import Web3

# 编译 solidity
# https://github.com/iamdefinitelyahuman/py-solc-x
from solcx import compile_standard, install_solc

with open('./Storage.sol', 'r', encoding='utf-8') as f:
    storage_file = f.read()

# 下载0.6.0版本的Solidity编译器
install_solc('0.6.0')

# 编译Solidity
compiled_sol = compile_standard(
    {
        "language": "Solidity",
        # Solidity文件
        "sources": {"Storage.sol": {"content": storage_file}},
        "settings": {
            "outputSelection": {
                "*": {
                    # 编译后产生的内容
                    "*": ["abi", "metadata", "evm.bytecode", "evm.bytecode.sourceMap"]
                }
            }
        },
    },
    # 版本,与编写智能合约时Solidity使用的版本对应
    solc_version="0.6.0",
)

# 编译后的结果写入文件
with open('compiled_code.json', 'w') as f:
    json.dump(compiled_sol, f)

compile_standard 方法编译后的结果写入 compiled_code.json,将其格式化,如下图:

a5ad100895e1d35872f9c747e639cfd6.png

从上图可知,Solidity 编译后的字节码也在 compiled_code.json 中,将 json 文件中重要的数据读取出来,代码如下:

# 智能合约编译后的字节码(上链的数据)
bytecode = compiled_sol["contracts"]["Storage.sol"]["Storage"]["evm"][
    "bytecode"
]["object"]

# ABI (Application Binary Interface),用于与智能合约中的方法进行交互的接口
abi = json.loads(
    compiled_sol["contracts"]["Storage.sol"]["Storage"]["metadata"]
)["output"]["abi"]
  • bytecode:智能合约编译后的字节码,智能合约上链其实就是将这部分数据存储到区块链中。

  • abi:我们的程序与智能合约交互的接口,它定义了我们的程序可以怎么与当前这个智能合约交互。

至此,智能合约的编译流程就结束了,然后我们通过 web3.py 连接到以太坊中。

与 Remix IDE 不同,web3.py 没有通过 JavaScript VM 实现的本地区块链网络,虽然有 web3 [tester],但不够完善,这里我们通过 Genache 来实现本地网络。

Genache:https://www.trufflesuite.com/ganache

下载好后,直接运行,然后点击【QUICKSTART】,选择【ETHEREUM】。

7a2f63ce3b0cc15b020ce49b85802571.png

Ganache 会在本地快速创建区块链网络:

02e3f65e423ed8943687dc8748917cb0.png

从上图中,可以看出,Ganache 会为我们创建 10 个账号,创建出的网络可以通过 http://127.0.0.1:7545 连接。

要实现连接,还需要一个信息,那就是 Ganache 创建的区块链网络,其 chain id 是多少?图中只展示了 NETWORK ID(5777),查阅文档,可知 chain id 为 1337(https://ethereum.stackexchange.com/questions/91072/setup-ganache-with-metamask-what-and-where-is-a-chain-id)。

通常,我们不会将这些常量硬编码到代码中,而是通过配置文件或环境变量的形式引入,这里使用环境变量的形式。Python 中使用环境变量比较好的方式是使用 python-dotenv 这个库,pip 安装一下,然后再项目根目录中创建名为.env 的文件,写入如下内容:

RINKEBY_RPC_URL=http://127.0.0.1:7545
ACCOUNT_ADDRESS=0x4A151d2855eEFba23Eb9B7943253D29E061cFeFD
PRIVATE_KEY=0xc6ba82d2e7bc2ab41f578a57b8822767b9875e339d2f93d3fe8eef25f5cb39aa

然后代码里使用一下:

from dotenv import load_dotenv

load_dotenv()

w3 = Web3(Web3.HTTPProvider(os.getenv("RINKEBY_RPC_URL")))
chain_id = 1337

my_address = os.getenv("ACCOUNT_ADDRESS")
private_key = os.getenv("PRIVATE_KEY")

Web3.py 部署智能合约

部署的流程比较简单,直接给出代码:

from base import *

# 构建智能合约对象
storage = w3.eth.contract(abi=abi, bytecode=bytecode)
# 当前区块链中最后一个交易的nonce
nonce = w3.eth.get_transaction_count(my_address)

# 部署智能合约 - 创建交易
transaction = storage.constructor().buildTransaction(
    {"chainId": chain_id, "from": my_address, "nonce": nonce}
)
# 签名当前交易 - 证明是你发起的交易
signed_txn = w3.eth.account.sign_transaction(transaction, private_key=private_key)
print("Deploying Contract!")

# 开始部署 - 发送交易
tx_hash = w3.eth.send_raw_transaction(signed_txn.rawTransaction)
print('Waiting for deploy transaction to finish...')
# 等待智能合约部署结果,部署完后,会获得合约的地址
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print('Deployed Done!')
print(f'contract address: {tx_receipt.contractAddress}')

上述代码中,一开始通过 w3.eth.contract 方法实例化合约对象,需要传入 abi 与 bytecode(base.py 提供了)。

然后对合约进行部署,部署的过程其实也是在创建交易,这就涉及到:

  • 创建交易对象

  • 签名交易

  • 发送交易

  • 等待交易完成

上述代码刚好就是这几个步骤,需要注意的点是 nonce,每个交易都需要 nonce,这个 nonce 是顺序的,所有我们需要获取最后一个交易的 nonce,运行代码,结果如下图:

96e8d636411292eb077932e3c5787411.png

部署后,智能合约的地址:0x8395Fd53331cea813e3838F6bB42B9668BEBf0C2

Web3.py 调用部署的智能合约

部署完后,我们获得了合约部署后的地址,使用该地址,可以构建出合约对象,然后我们就可以调用合约里的方法了。回顾一开始我们编写的合约,其实只有 addPerson 这一个方法,该方法会将传入方法的数据存到区块链网络中,这改变了区块链的状态,所以算是一次交易操作,凡是交易操作就需要签名,从而证明这个操作是你做的。

完整代码如下:

from base import *

# 调用deploy.py会获得contract_address
contract_address = '0x5071ad6611B322647B88ACF5CBeBCA71Bead0c6f'

nonce = w3.eth.get_transaction_count(my_address)

# 实例化合约对象
storage = w3.eth.contract(address=contract_address, abi=abi)
# 调用addPerson方法
transaction = storage.functions.addPerson('二两', 28).buildTransaction({
    "chainId": chain_id,
    "from": my_address,
    "nonce": nonce
})
# 签名
signed_transaction = w3.eth.account.sign_transaction(transaction, private_key=private_key)
# 发送交易
tx_hash = w3.eth.send_raw_transaction(signed_transaction.rawTransaction)
print('add new Person to contract...')
# 等待交易完成
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
# 获得people数组中存储的值
result = storage.functions.people(0).call()
print(f'get person info: {result}')

因为 addPerson 方法会改变区块链,即需要消耗 Gas 的交易行为,这类行为都需要使用私钥进行签名,然后才能发送交易,调用完 addPerson 函数后,再从 people 数组获取下标为 0 的数据。

16f030958780bd4f82861ba35f17ec2f.png

这里提一下 ABI,让大家有更直观的理解,在上述代码中,为啥可以调用 addPerson 函数和 people 数组?因为编译后获得的智能合约的 ABI 中存在 addPerson 与 people,复制 compiled_code.json 中 abi 的内容:

"abi": [
                    {
                        "inputs": [
                            {
                                "internalType": "string",
                                "name": "_name",
                                "type": "string"
                            },
                            {
                                "internalType": "uint256",
                                "name": "_age",
                                "type": "uint256"
                            }
                        ],
                        "name": "addPerson",
                        "outputs": [],
                        "stateMutability": "nonpayable",
                        "type": "function"
                    },
                    {
                        "inputs": [
                            {
                                "internalType": "uint256",
                                "name": "",
                                "type": "uint256"
                            }
                        ],
                        "name": "people",
                        "outputs": [
                            {
                                "internalType": "string",
                                "name": "name",
                                "type": "string"
                            },
                            {
                                "internalType": "uint256",
                                "name": "age",
                                "type": "uint256"
                            }
                        ],
                        "stateMutability": "view",
                        "type": "function"
                    }
                ],

以 addPerson 函数为例,其 type 为 function,name 为 addPerson,inputs 表示调用该方法需传入的参数,也给出了 type,通过 abi,程序才知道当前的智能合约提供什么功能。

部署到 Rinkeby 测试网络

通过上面的操作,我们已经可以将智能合约部署到测试网络中了,那如何部署到测试网络中?Web3.py 不像 Remix IDE 提供 Inject Web3 的功能,要部署到测试网络,我们需要借助第三方服务,与之相关的服务有:alchemy、infura 等。

简单而言,在这些服务对应的网站上,注册账号,创建应用,然后拿到开发用的 key,然后使用这个 key 与这些服务交互,我们会连接到这些服务上,然后服务会为我们将应用发布到测试网络或以太坊主网上,与我们平时使用百度、高德等 API 平台没啥差别,都是创建应用获得 key。

这里我们使用 infura 服务,infura 服务大体的工作方式如下图,简单来说,我们不需要将自己本地的计算机加入到以太坊网络中,成为其中的节点(挺麻烦的,要拉数据、足够的网速和足够的硬盘空间),而是直接通过 infura 服务连接(本质是使用 infura 的节点)。

3e78910b211b3ab9ee2d65f1593aa1c2.png

从图中可知,我们通过 infura 提供的 ITX API 便可以与以太坊网络交互了,然后你创建应用,在应用的设置页,可以看到相应的信息,需要注意的是,【ENDPOINTS】处需要选择 rinkeby 测试网络,如下图:

551ce3082235912469c0dd49c7c3f0dd.png

有了这些设置后,我们修改一下.env 文件中的内容:

RINKEBY_RPC_URL=https://rinkeby.infura.io/v3/<project_id>
ACCOUNT_ADDRESS=<账号地址>
PRIVATE_KEY=<对应的私钥>
CHAIN_ID=4

RINKEBY_RPC_URL 给我 Infura 给的 http 地址,ACCOUNT_ADDRES 与 PRIVATE_KEY 可以在 MetaMask 钱包中获取(获取 Rinkeby 上的),为了方便,我将 CHAIN_ID 也放到.env 中了,不同的链具有通过的 CHAIN_ID,可以通过 https://chainlist.org/ 查询:

cf758200cfa960f3552e51d66108ee53.png

代码中连接网络的方式不需要改变,只是我们将 CHAIN_ID 抽到.env 中了,getenv 函数会返回字符串格式,需要强转一下。

w3 = Web3(Web3.HTTPProvider(os.getenv("RINKEBY_RPC_URL")))
chain_id = int(os.getenv("CHAIN_ID"))

然后我们部署,然后调用合约中的方法,使用 play_storage.py 时,因为合约地址变了,所以你需要同步修改一下 contract_address 变量,调用后,可以通过 etherscan 查看:

36903b772a10047aa38180728b04abab.png

项目代码:GitHub - ayuLiao/web3py_storage: use web3.py play ethereum contract

如果你在使用 Infura 时,发现总是 403,可以尝试删除掉原本的 project,创建一个新的 project。

Brownie 基础

上面通过 Web3.py 实现了智能合约的部署与交互,可以发现还是比较麻烦的,每次触发交易时,都需要进行签名操作等,Brownie 框架基于 Web3.py,它将很多步骤都帮我们静默完成了,如果你不了解 Web3.py,直接上 Brownie 框架,个人感觉也不好,因为会显得比较黑盒。

安装 Brownie

我们通过 pip 安装一下 Brownie,阅读文档,会发现 Brownie 建议使用 pipx 来安装,pipx 会在全局创建一个虚拟环境,然后将 Brownie 安装在虚拟环境中,研究后发现,这是因为 Brownie 依赖比较多,安装过程比较慢,如果你通过 venv 方式,每个项目都要来一次,挺费时间的,因我 Windows 的环境问题,我懒得折腾,我自己管理员开 Terminal 直接 pip 安装:

pip install eth-brownie

安装完后,根据文档,我们还需要安装一下 ganache-cli(github.com/trufflesuite/ganache),命令行版的 ganache,npm 全局安装一下则可。

npm install ganache-cli@latest --global

在 Terminal 中输入 brownieganache-cli 都可以正常使用则表示安装成功。

快速使用 Brownie

创建名为【brownie_storage】的文件夹,进入该文件夹,然后通过 brownie init 初始化项目,会获得如下结构,每个文件夹的作用也标准了:

C:\USERS\AYU\WORKPLACE\BLOCKCHAIN\BROWNIE_STORAGE
├───build                # 编译、部署等结果存放目录
│   ├───contracts
│   ├───deployments
│   └───interfaces
├───contracts            # 智能合约的目录
├───interfaces           # 接口的目录
├───reports              # JSON报告文件的目录(使用GUI的用户才会使用)
├───scripts              # 脚本的目录
└───tests                # 测试脚本目录

在使用 Brownie 编写代码前,先使用 ganache-cli 启动本地的以太坊网络,方便测试:

b608d699dedd645ddb506862cf688e2f.png

然后,我们将 Storage.sol 复制到 contracts 目录中,通过 brownie compile 命令编译智能合约,该命令会将 contracts 目录下所有的智能合约都进行编译,编译完成后,在 build/contracts 会出现同名的 json 文件,与 Web3.py 类似,这里记录着智能合约的 bytecode、abi 等信息。

完成编译后,接着进行部署,在 scripts 目录下创建 deploy.py,其代码如下:

from brownie import accounts, config, network, Storage


def deploy_storage():
    account = get_account()
    # Instantiate Storage contract
    storage = Storage.deploy({"from": account})
    # call addPerson function
    transaction = storage.addPerson('二两', 28, {"from": account})
    # wait transaction finish
    transaction.wait(1)
    # call people function to get data from people array
    result = storage.people(0)
    print('result: ', result)


def get_account():
    if network.show_active() == 'development':
        return accounts[0]
    else:
        # add new account to brownie accounts
        # account config data from brownie-config.yaml
        return accounts.add(config['wallets']['account_key'])

def main():
    deploy_storage()

在 Windows 中,brownie 不支持 python 中有中文注释,估计是没有兼容好。

相比于 Web3.py,brownie 简单了很多,你只需导入 Storage,然后调用其 deploy 方法则可,因为 Storage 其实是动态载入的,brownie 本身并没有这个类,所以我们不可以直接通过 python 去运行 deploy.py 文件,而是需要使用 brownie run .\scripts\deploy.py 命令去运行:

ec70921d94f0a1609efa1bc5ed10e433.png

上述代码中,定义了 get_account 函数,该函数会判断当前处于哪个区块链,从而使用想要的方式获得 account,brownie 默认处于 development(本地开发网络),如果不处于 development,则通过 brownie 提供的 accounts.add 函数添加账户对象,比如后面我们会部署到 Rinkeby,就需要从钱包里拿私钥(账号公钥信息可以通过私钥推导获得),这里为了方便,直接放在配置文件中。

brownie 提供的 config 模型,会自动从项目根目录的 brownie-config.yaml 中获取,在这里,该文件内容如下:

dotenv: .env
wallets:
  from_key: ${PRIVATE_KEY}

因为私钥比较重要,也规范一些,这里通过 ${PRIVATE_KEY} 导入项目根目录下.env 文件中的内容。

此外,我们还可以使用 brownie console,进入 brownie 提供的交互式命令环境,在该环境里,你可以使用 brownie 中的任何功能。

> brownie console

Brownie v1.17.0 - Python development framework for Ethereum

BrownieStorageProject is the active project.
c:\program files\python37\lib\site-packages\brownie\network\main.py:46: BrownieEnvironmentWarning: Development network has a block height of 6
  BrownieEnvironmentWarning,
Attached to local RPC client listening at '127.0.0.1:8545'...
Brownie environment is ready.
>>> from brownie import network
>>> network.show_active()
'development'
>>> from brownie import accounts
>>> account = accounts[0]
>>> from brownie import Storage
>>> storage = Storage.deploy({"from": account})
Transaction sent: 0xd7269730fb3ee3a642391c338234f9cb63993b7bd991316971c89ca6406cebe7
  Gas price: 0.0 gwei   Gas limit: 6721975   Nonce: 6
  Storage.constructor confirmed   Block: 7   Gas used: 243848 (3.63%)
  Storage deployed at: 0x500F5EDceE38597164c26606E93e92D059853a46

>>> transaction = storage.addPerson('二两', 28, {"from": account})
Transaction sent: 0x2aa19410ddc316413f54a6e1c25f6a5878b7a0877fa65a5bec80f380ba3c64aa
  Gas price: 0.0 gwei   Gas limit: 6721975   Nonce: 7
  Storage.addPerson confirmed   Block: 8   Gas used: 84259 (1.25%)

>>> transaction.wait(1)
  Storage.addPerson confirmed   Block: 8   Gas used: 84259 (1.25%)

进行单元测试

智能合约通常与钱相关,做好测试是非常有必要的。brownie 使用 pytest 来实现单元测试,至于 pytest,用过的都说好),在 tests 目录创建名为 test_storage.py 的文件,代码如下:

from brownie import Storage, accounts

def test_deploy():
    account = accounts[0]
    storage = Storage.deploy({"from": account})
    transaction = storage.addPerson('二两', 28, {"from": account})
    transaction.wait(1)
    # call people function to get data from people array
    result = storage.people(0)
    assert result == ('二两', 28)

很常规的单元测试代码,可以将智能合约部署的过程与 CI/CD 流程结合,每次部署都过一遍所有的单元测试,从而让合约更加健硕。

使用 Brownie 将合约部署到测试网络

阅读文档发现,在 Brownie 中通过 Infura 服务进行合约的部署,只需要配置一下则可,文档内容:https://eth-brownie.readthedocs.io/en/latest/network-management.html#using-infura

除了可以通过 export 的方式添加 WEB3_INFURA_PROJECT_ID 环境变量,我们还可以将 WEB3_INFURA_PROJECT_ID 直接添加到.env 中(文档里没写)。

WEB3_INFURA_PROJECT_ID 就是 Infura 为你提供的 Project ID,此外,因为要连接测试网络,所以部署时需要连接测试网络中的账号,你需要将你账号的私钥也放到.env 中。

PRIVATE_KEY= <你账号的私钥>
WEB3_INFURA_PROJECT_ID=<Infura中的Project ID>

然后通过 brownie run .\scripts\deploy.py --network rinkeby 运行则可完成部署。

0ba1214760a98c0878e05b6010c94011.png

27e82fc3c93b31427f761bf63496bd05.png

brownie 提供了多种网络,所以我们部署时不需要做额外操作,直接指定对应的网络则可。

a3c103461d3ac75ef79f08556e80a499.png

当然,后续开发时,我们还可以 brownie networks add 命令添加新的网络。

项目代码:https://github.com/ayuLiao/brownie_storage

结尾

这篇文章只是简单的介绍了 Brownie 的一些操作,Brownie 还具有很多高级功能,比如 Mock、Fork 一个区块链到本地进行开发、又比如 Brownie 提供了 Debug Tools 供你进行调试开发,后续的文章会分享这些内容。

最后提一嘴,Brownie 的文档是很好的学习资料。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Brownie 开发智能合约(入门使用) 的相关文章

  • 如何在生产中安全地更改会话 cookie 域或名称?

    我们最近意识到我们的会话 cookie 正在被写入我们网站的完全限定域名 www myapp com 例如 MYAPPCOOKIE 79D5DB83 domain www myapp com 我们希望将其切换为可以跨子域共享的cookie
  • 如何自定义 JFrame 上的标题栏?

    我想在我的 Java Swing 桌面应用程序中拥有一个自定义的标题栏 最好的方法是什么 我可以通过在 JFrame 的构造函数中使用以下代码来使用 Swing 标题栏 this setUndecorated true this getRo
  • 在Python中将用户昵称转换为正式名字

    我正在尝试根据 Python 中的用户名字和姓氏映射来自不同系统的用户 一个问题是 名字在很多情况下都是 昵称 例如 对于用户来说 他的名字在一个系统中是 Dave 而在另一个系统中是 David python 中有没有简单的方法可以将这些
  • 如何在Python中获取套接字的外部IP?

    当我打电话时socket getsockname 在套接字对象上 它返回我的机器的内部 IP 和端口的元组 但是 我想找回我的外部IP 最便宜 最有效的方式是什么 如果没有外部服务器的配合 这是不可能的 因为您和另一台计算机之间可能存在任意
  • 如何在 Google 地图中创建自定义地图?

    我正在尝试创建一个包含我家地图的 Google 地图应用程序 卧室 浴室 厨房等 使用 GPS 我会找到我现在在家里的位置 并尝试获取到我卧室的方向 步行距离 您可以使用Google的API来获取方向 我需要知道的是 如何添加我家的自定义地
  • Spring Oauth2. DaoAuthenticationProvider 中未设置密码编码器

    我对 Spring Oauth 和 Spring Security 很陌生 我正在尝试在我的项目中使用 client credentials 流程 现在 我设法使用自己的 CustomDetailsS ervice 来从系统中已存在的数据库
  • Google App Engine self.redirect() POST 方法

    在 GAE Python 中 使用 webApp 框架 调用 self redirect some url 通过 GET 方法将用户重定向到该 URL 是否也可以通过带有一些参数的 POST 方法进行 重定向 如果可以的话 怎样做 Than
  • 为什么我们在同一台服务器上使用多个应用程序服务器实例

    我想这是有充分理由的 但我不明白为什么有时我们会在同一物理服务器上放置例如 5 个具有相同 Web 应用程序的实例 这与多处理器架构的优化有关吗 JVM 或其他允许的最大内存限制 嗯 过了很长一段时间我又看到这个问题了 一台机器上的多个 J
  • Guava MultiSet 与 Map?

    我对Multiset的理解是一个带有频率的集合 但是我总是可以使用Map来表示频率 还有其他原因使用Multiset吗 优点Multiset
  • Java并发锁和条件的使用

    我可以用object wait object notify and synchronized blocks解决生产者消费者类型的问题 同时我可以使用locks and conditions from java util concurrent
  • 如何将模型从 ML Pipeline 保存到 S3 或 HDFS?

    我正在尝试保存 ML Pipeline 生成的数千个模型 正如答案中所示here https stackoverflow com questions 32121046 run 3000 random forest models by gro
  • Python:如何对数组 X 进行排序,但对 Y 进行相同的相对排序?

    例如 X 5 6 2 3 1 Y 7 2 3 4 6 我对X进行排序 X 1 2 3 5 6 但我希望对 Y 应用相同的相对排序 以便数字保持与以前相同的相对位置 Y 6 3 4 7 2 我希望这是有道理的 通常 你会做一个zip sort
  • 使用枚举名称而不是值对 Pydantic 字段进行编码

    我有一个枚举类 class Group enum Enum user 0 manager 1 admin 2 我有一个 pydantic 模型 class User BaseModel id int username str group G
  • 有没有比 Python 内置 == 运算符更快的方法来测试两个列表是否具有完全相同的元素?

    如果我有两个列表 每个列表有 800 个元素长并填充整数 有没有比使用内置元件更快的方法来比较它们具有完全相同的元件 如果没有 则短路 操作员 a 6 2 3 88 54 486 b 6 2 3 88 54 486 a b gt gt gt
  • 注释处理工具<-检查有效注释

    I have ColumnMetadata index 1 ColumnMetadata index 2 ColumnMetadata index 3 我必须使用 APT 检查索引号是否唯一 我不知道该怎么做 我看不懂教程 一般我在网上找资
  • 在Python中通过sys.stdout写入unicode字符串

    暂时假设一个人无法使用print 从而享受自动编码检测的好处 所以这给我们留下了sys stdout 然而 sys stdout太蠢了不做任何合理的编码 http bugs python org issue4947 现在人们阅读 Pytho
  • 使用基于Optional内容的流

    我从不受我控制的服务获取可能为空的地图 并且想要处理它 比方说 过滤 映射并减少到我需要的单个元素 问题 是否有从Optional到Stream的 链接 我尝试过 除其他外 return Optional ofNullable getMap
  • Spark (Python) 中的 Kolmogorov Smirnov 测试不起作用?

    我正在 Python Spark ml 中进行正态性测试 看到了我的结果think是一个错误 这是设置 我有一个标准化的数据集 范围 1 到 1 当我做直方图时 我可以清楚地看到数据不正常 gt gt gt prices norm hist
  • POJO 支持使用omnifaces 自动完成primefaces

    我正在尝试在我的项目中使用 primefaces 自动完成组件 以避免将特定转换器写入我尝试使用的每个列表对象全能面孔 http showcase omnifaces org converters ListConverter如建议的here
  • 如何测试send_file烧瓶

    我有一个小型烧瓶应用程序 它需要上传一些图像并将它们转换为多页 tiff 没什么特别的 但是如何测试多个文件的上传和文件下载呢 我的测试客户端 class RestTestCase unittest TestCase def setUp s

随机推荐

  • Linux 中的 chsh 命令及示例

    Linux中的chsh命令用于更改用户的登录shell 当前为登录shell Shell是与操作系统交互的用户界面 可以被认为是操作系统的外层 bash shell 是 Linux 中使用最广泛的登录 shell 之一 该命令允许用户从当前
  • selenium处理12306出发地value值修改不成功

    不知道你们在使用ui框架编写12306时 有没有遇到过这样的问题 在使用selenium去编写场景时发现出发地这个input标签 每次都没办法按照你的预期去修改值 例如 首先在浏览器里使用document发现完全可以修改掉输入框的值 然后兴
  • 排序算法总结(Python版本)

    看了很多排序算法 每种算法都有多个版本 现总结一版自己觉得容易理解的 供以后翻阅 1 插入排序 直接插入排序 直接插入排序是将一个数插入到已经排序好的序列中 做法是先将第一个数作为已经排序好的 依此将后面的数取出插入到前面已排序好的序列中
  • 数据科学猫:机器学习建模流程

    进击的橘子猫正式改名上线啦 我的CSDN主页 https blog csdn net Orange Spotty Cat 也欢迎大家搜索微信公众号 进击的橘子猫 我也会定期分享数据科学 Python 大数据 项目管理与PPT的相关知识 让我
  • 【机器学习】—各类梯度下降算法 简要介绍

    阅读之前看这里 博主是一名正在学习数据类知识的学生 在每个领域我们都应当是学生的心态 也不应该拥有身份标签来限制自己学习的范围 所以博客记录的是在学习过程中一些总结 也希望和大家一起进步 在记录之时 未免存在很多疏漏和不全 如有问题 还请私
  • 考研教训分享

    考研教训分享 大家好 今天分享一篇考研教训 这不是经验帖 这只是一篇避坑指南 记录了我考研期间所走的所有弯路 希望可以帮助到在这条路上奋斗的你 我是某双非的工科考生 所以这篇文章更偏向于考数学的研友 2月中旬备考 复习前感觉自己之前大学学过
  • 你的朋友可能还在为毕设烦恼,而你已经有了这113个Java计算机毕业设计项目

    站在计算机专业毕业设计的门槛上 你是否为选题和项目规划而烦恼 今天 我们将为即将毕业的同学们展示多个毕业设计项目 期待能为正在为毕业设计抓狂的同学们提供一些帮助 一 成品列表 以下所有springboot框架项目的源码博主已经打包好上传到百
  • Mac用户入门的13个基础终端命令

    终端是用户在Mac电脑中经常使用的应用程序 也是非常好用的Mac应用 终端应用之所以受欢迎是因为 它可以直接执行用户给出的命令 从而完成其他程序无法完成的操作 更改目录 cd 示例用法 cd folder 使用cd命令更改目录 例如 cd
  • vue进阶——整合富文本编辑器wangEditor

    vue进阶 整合富文本编辑器wangEditor 前言 一 什么是wangEditor 二 安装wangEditor 1 React 2 Vue2 3 Vue3 4 CDN 三 基本使用 1 vue2 2 vue3 四 文件上传问题 1 前
  • kdj指标主要看哪个值_KDJ指标的最全面攻略文章,散户学会后看指标不再迷茫,超赞...

    本文由公众号越声投研 yslcwh 整理 仅供参考 不构成操作建议 如自行操作 注意仓位控制和风险自负 我们步入了信息时代 知识已经是我们人类生存和生产当中最为重要的因素之一 而如今 在求知欲的趋势下 我们消费者也甘为知识而花费解囊 智慧经
  • 关于set_output_delay与set_input_delay概念与用法

    一 这两条约束语句都是针对板级延时而言的 语句中必须的有是 时钟与port 二 set input delay 用于数据输入端口 调节数据输入与时钟输入到来的相位关系 当FPGA外部送入FPGA内部寄存器数据时 会有两个时钟launch c
  • 被迫学习一波Linux命令

    事情起因 部署一个服务 人家说了最低配置是3G 我没当回事 拿着个2G的服务器直接就上了 结果 哈哈 都能猜到结果 服务器内存爆了 而且最可气的是服务器还登不进去 重启之后内马上又被拉满了 根本连接进不去 算是一次小小的事故 记录是为了不再
  • msvcp110.dll丢失原因——msvcp110.dll丢失怎么修复(最新可修复)

    昨天卸载了一个垃圾软件以后 我的其他软件就无法打开运行 提示msvcp110 dll丢失 无法继续执行此代码 今天早上找了很多方法 终于把msvcp110 dll丢失的原因以及修复的方法都弄明白了 msvcp110 dll是一个非常重要的文
  • 26功能之VS2013静态库的制作与使用

    26功能之VS2013静态库的制作与使用 一 制作静态库 1 新建项目 选择Win32项目 2 先选择空项目 静态库 若先选择静态库 空项目按钮无法被选中 这点需要注意 3 可以看到此时是没有其它 h和 cpp文件 有时VS13或者15即使
  • 天拓分享

    1 组态王和多台西门子 S7 300 400PLC 通过 dp 协议通讯时 设备地址应如何定义 1 硬件连接 计算机中插入一块 CP5611 或 CP5613 可实现将多个 S7 300 400PLC连接在一条 DP 总线上 2 DP 协议
  • 读取问题:UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd0 in position 0: invalid continuation

    关于读取CSV文件错误问题 1 新建CSV的文件 问题描述 新建xls工作表之后直接修改文件扩展名为csv 然后读取csv文件时出现如题错误 data pd read csv E csv 尝试了以下改进方法仍然错误 达咩达咩 不过大家有类似
  • 基础巩固

    写在前面 本文主要是自己用来巩固C 基础用的 内容源自我的一位同学rzj的八股总结 内容很多 后面会加上目录之类的 方便查找 如果有错误 欢迎及时在评论区或私信指出 1 1 1 简述下C 语言的特点 C 在C语言基础上引入了面对对象的机制
  • linux中vim试题,测试开发面试的Linux面试题总结之一:vim使用方法

    原标题 测试开发面试的Linux面试题总结之一 vim使用方法 编写测试脚本 查看日志 查看配置文件 少不了要和vim打交道 vim是unix linux下的文本编辑器 它一般有两个模式 命令模式和编辑模式 通过ESC来切换到命令模式 其常
  • bitcoinj开发环境搭建

    bitcoinj开发包是一个Java版本的比特币协议实现 使用bitcoinj就可以实现钱包管理和交易的发送与接收 而无须本地安装bitcoin core软件 本文将介绍bitcoinj开发环境的搭建方法 虽然bitcoinj有完备的文档
  • Brownie 开发智能合约(入门使用)

    简介 上篇文章 使用了 Remix 在线 IDE 个人感觉 Remix 在入门智能合约开发时 是很好的上手工具 因为 Remix 帮我们处理好了编译 部署的过程 并且还通过 JavaScript VM 准备好了本地区块链方便我们测试 可谓开