Merge branch 'master' of https://github.com/datawhalechina/team-learning-program
|
@ -0,0 +1,372 @@
|
||||||
|
# 新手建议
|
||||||
|
|
||||||
|
## 学习Linux的注意事项
|
||||||
|
|
||||||
|
* Linux严格区分大小写(命令全都是小写)—— 命令、文件名、选项等均区分大小写
|
||||||
|
|
||||||
|
* Linux中**所有内容**以文件形式保存,包括硬件
|
||||||
|
|
||||||
|
* 硬盘文件是/dev/sd[a-p]
|
||||||
|
* 光盘文件是/dev/sr0等
|
||||||
|
|
||||||
|
* Windows通过扩展名区分文件类型,还有图标可以区分;Linux不靠扩展名区分文件类型,靠文件权限区分,但也有一些约定俗成的扩展名:
|
||||||
|
|
||||||
|
* 压缩包:"*.gz", "*.bz2", "*.tar.bz2", "*.tgz"等
|
||||||
|
* 二进制软件包:".rpm"
|
||||||
|
* 网页文件:"*.sh"
|
||||||
|
* 配置文件:"*.conf"
|
||||||
|
|
||||||
|
注意:这些扩展名不是必要的,即时不加扩展名也没有影响,只是便于管理而已
|
||||||
|
|
||||||
|
* Linux所有存储设备都必须挂在之后用户才能使用,包括硬盘、U盘、光盘(将设备与挂载点连接的过程就是挂载)
|
||||||
|
|
||||||
|
* Windows下的程序不能直接在Linux中安装和运行
|
||||||
|
|
||||||
|
## 服务器管理和维护建议
|
||||||
|
|
||||||
|
### 服务器管理
|
||||||
|
|
||||||
|
| 目录名 | 目录作用 |
|
||||||
|
| :----------: | :----------------------------------------------------------: |
|
||||||
|
| /bin/ | 存放系统命令的目录,普通用户和超级用户都可以执行,不过放在/bin下的命令在单用户模式下也可以执行 |
|
||||||
|
| /sbin/ | 保存和系统环境设置相关的命令,只有超级用户可以使用这些命令进行系统环境设置,但是有些命令可以允许普通用户查看 |
|
||||||
|
| /usr/bin/ | 存放系统命令的目录,普通用户和超级用户都可以执行,这些命令和系统启动无关,在单用户模式下不能执行 |
|
||||||
|
| /usr/sbin/ | 存放根文件系统不必要的系统管理命令,例如多数服务程序。只有超级用户可以使用 |
|
||||||
|
| /boot/ | 系统启动目录,保存系统启动相关的文件,如内核文件和启动引导程序(grub)文件等 |
|
||||||
|
| /dev/ | 设备文件保存位置,我们已经说过Linux中所有内容以文件形式保存,包括硬件,这个目录就是用来 保存所有硬件设备的 |
|
||||||
|
| /etc/ | 配置文件保存位置,系统内所有采用默认安装方式(npm安装)的服务的配置文件全部保存在这个目录中,如用户账户和密码,服务的启动脚本,常用服务的配置文件等 |
|
||||||
|
| /home/ | 每个用户的默认登陆位置,普通用户的home目录就是在/home下建立一个和用户名相同的目录 |
|
||||||
|
| /lib/ | 系统调用的函数库保存位置 |
|
||||||
|
| /lost+found/ | 当系统意外崩溃或机器意外关机时,产生的一些文件碎片放在这里,当系统启动的过程中fsck工具会对其进行检查,并修复已经损坏的文件系统。这个目录只在每个分区中出现,例如/lost+found就是根分区的备份恢复目录,/boot/lost+found就是/boot分区的备份恢复目录 |
|
||||||
|
| /media/ | 挂载目录,系统建议是用来挂载媒体设备的,例如软盘和光盘 |
|
||||||
|
| /mnt/ | 挂载目录,建议挂载额外设备,如U盘,移动硬盘和其他操作系统的分区 |
|
||||||
|
| /misc/ | 挂载目录,系统建议用来挂载NFS服务的共享目录 |
|
||||||
|
| /opt/ | 第三方安装的软件保存位置,但现在更多的是保存在/usr/local中 |
|
||||||
|
| /proc/ | 虚拟文件系统,该目录的数据不保存到硬盘中,而是保存到内存中。主要保存系统的内核、进程、外部设备状态和网络状态灯,如/proc/cpuinfo是保存CPU信息的,/proc/devices是保存设备驱动的列表的,/proc/filesystems是保存 文件系统列表的,/proc/net/是保存网络协议信息的 |
|
||||||
|
| /sys/ | 虚拟文件系统,主要保存内核相关信息 |
|
||||||
|
| /root/ | 超级用户的家目录 |
|
||||||
|
| /srv/ | 服务数据目录, 一些系统服务启动后可以在这个目录保存需要的数据 |
|
||||||
|
| /tmp/ | 临时目录,系统存放临时文件的目录,该目录下所有用户都可以访问和写入,我们建议此目录不能保存重要数据,最好每次开机都把该目录清空 |
|
||||||
|
| /usr/ | 系统软件资源目录,注意usr不是user的缩写,而是"Unix Software Resource"的缩写,所以不是存放用户数据,而是存放系统软件资源的目录。系统中安装的软件大多数都在这里 |
|
||||||
|
| /var/ | 动态数据保存位置,主要保存缓存、日志以及软件运行所产生的文件 |
|
||||||
|
|
||||||
|
### 服务器注意事项
|
||||||
|
|
||||||
|
1. 远程服务器不允许关机,只能重启
|
||||||
|
2. 重启时应该关闭服务
|
||||||
|
3. 不要在服务器的访问高峰运行高负载命令
|
||||||
|
4. 远程配置防火墙时不要把自己踢出服务器(可以设置每五分钟将防火墙规则重置一次,配置完之后再取消该设置)
|
||||||
|
5. 指定合理的密码规范并定期更新
|
||||||
|
6. 合理分配权限
|
||||||
|
7. 定期备份重要数据和日志
|
||||||
|
|
||||||
|
磁盘分区是用分区编辑器在磁盘上划分几个逻辑部分,碟片一旦划分成数个分区,不同类的目录和文件 可以存储进不同的分区。
|
||||||
|
|
||||||
|
# 系统分区
|
||||||
|
|
||||||
|
## 分区类型
|
||||||
|
|
||||||
|
* 主分区:最多只能有4个
|
||||||
|
* 扩展分区:
|
||||||
|
* 最多只能有1个
|
||||||
|
* 主分区加扩展分区最多有4个
|
||||||
|
* 不能写入数据,只能包含逻辑分区(这种限制是硬盘的限制)
|
||||||
|
* 逻辑分区
|
||||||
|
|
||||||
|
## 格式化
|
||||||
|
|
||||||
|
硬盘经过正确分区后仍不能写入数据,我们的硬盘还必须经过格式化之后才能写入数据。格式化又称逻辑格式化,它是根据用户选定的文件系统(如FAT16、FAT32、NTFS、EXT 2、EXT3、EXT4等),在磁盘的特定区域写入特定数据,在分区中划分出一片用于存放文件分配表、目录表等用于文件管理的磁盘空间。格式化就是按照文件系统的规则将硬盘分成等大小的数据块,我们把数据块称为block。
|
||||||
|
|
||||||
|
> 注:Windows可以识别的系统有FAT16、FAT32、NTFS;Linux可以识别的系统有EXT2、EXT3、EXT4
|
||||||
|
|
||||||
|
## 设备文件名
|
||||||
|
|
||||||
|
#### 硬盘设备文件名
|
||||||
|
|
||||||
|
Windows是直接分区——>格式化——>分配盘符即可使用,Linux需要分区——>格式化——>给分区建立设备文件名——>分配盘符才能使用。
|
||||||
|
|
||||||
|
| 硬件 | 设备文件名 |
|
||||||
|
| ----------------- | ------------------- |
|
||||||
|
| IDE硬盘 | /dev/hd[a-d] |
|
||||||
|
| SCSI/SATA/USB硬盘 | /dev/sd[a-p] |
|
||||||
|
| 光驱 | /dev/cdrom或dev/sr0 |
|
||||||
|
| 软盘 | /dev/fd[0-1] |
|
||||||
|
| 打印机(25针) | /dev/lp[0-2] |
|
||||||
|
| 打印机(USB) | /dev/usb/lp[0-15] |
|
||||||
|
| 鼠标 | /dev/mouse |
|
||||||
|
|
||||||
|
#### 分区设备文件名
|
||||||
|
|
||||||
|
分区设备文件名直接**在硬盘设备文件名后面加分区号**即可,如
|
||||||
|
|
||||||
|
* IDE硬盘接口第一个分区:/dev/hda1(如今几乎看不到)
|
||||||
|
* SCSI硬盘接口、SATA硬盘接口的第一个分区:/dev/sda1
|
||||||
|
|
||||||
|
> IDE硬盘是最古老的硬盘,理论最高传输速度是133M/s
|
||||||
|
>
|
||||||
|
> SCSI硬盘接口与IDE硬盘同时代,更加昂贵但速度更快,理论最高传输速度可达200M/s,但这种硬盘主要用在服务器上
|
||||||
|
>
|
||||||
|
> 但上两种硬盘接口如今已经基本淘汰,如今使用更多的是小口的SATA串口硬盘,SATA已发展到3代,其理论传输速度最高可达500M/s,目前不管是服务器还是个人机基本使用的都是SATA硬盘接口。
|
||||||
|
|
||||||
|
需要留意的是,逻辑分区永远都是从5开始的
|
||||||
|
|
||||||
|
## 挂载
|
||||||
|
|
||||||
|
挂载实际上就是Windows中分配盘符的过程,盘符则被相应地称为挂载点,必须分区的分区有以下两种:
|
||||||
|
|
||||||
|
1. 根分区:/
|
||||||
|
2. swap分区(交换分区):可以理解为虚拟内存,当真正内存不够用时,可以使用这部分交换分区的硬盘空间来当内存,理论上来说交换分区应该是内存的两倍,但最大不超过2GB
|
||||||
|
|
||||||
|
若无这两个分区,Linux不能正常使用,但我们还推荐把/boot单独分区,这是为了防止Linux系统启动不起来,一般200MB即可。
|
||||||
|
|
||||||
|
# 远程登陆管理工具
|
||||||
|
|
||||||
|
## 网络连接
|
||||||
|
|
||||||
|
网络连接从虚拟机设置中可以看到,一共有三种:桥接、NAT和Host-only,下面讲解其区别:
|
||||||
|
|
||||||
|
* 桥接:桥接意味着虚拟机如同一个单独的主机一样访问Wifi等,也可以和其他机器通信
|
||||||
|
* NAT:虚拟机仅能和主机通信,但若主机可以访问互联网,虚拟机也可以访问互联网
|
||||||
|
* Host-only:虚拟机仅能和主机本机通信,不能访问互联网
|
||||||
|
|
||||||
|
## 网络配置
|
||||||
|
|
||||||
|
1. 首先调成Host-only模式,使得虚拟机仅与主机连接
|
||||||
|
2. 在主机上找到VMware Network Adapter VMnet1的IP地址,我本地地址为192.168.19.1
|
||||||
|
3. 在虚拟机上使用`ishw -c netwowrk`命令找到logical name,此即为虚拟机的网卡名称,我的虚拟网卡名称为ens33
|
||||||
|
4. 使用命令ifconfig [不等于IP地址] logical name,例如我使用的是`ifconfig ens33 192.168.19.2`
|
||||||
|
5. 此时再ifconfig即可看到我们设置的已生效
|
||||||
|
6. 我们可以在主机ping这个IP地址看到生效
|
||||||
|
7. 使用secureCRT连接即可
|
||||||
|
|
||||||
|
需要注意的是,以上方法配置IP地址时不是永久生效的,也就是重新启动电脑时就失效了,若想永久生效需要改变配置文件
|
||||||
|
|
||||||
|
若使用NAT模式,则步骤简单很多,只需要ifconfig获得IP地址之后直接用secureCRT连接即可
|
||||||
|
|
||||||
|
## WinSCP
|
||||||
|
|
||||||
|
另外推荐一个Windows主机与Linux虚拟机进行文件传输的工具——WinSCP,操作方法与上面类似,只需输入对应的IP地址即可连接。
|
||||||
|
|
||||||
|
# Linux常用命令
|
||||||
|
|
||||||
|
## 一、最常用命令
|
||||||
|
|
||||||
|
这是我们**使用得最多**的命令了,**Linux最基础的命令**!
|
||||||
|
|
||||||
|
- 可用 `pwd`命令查看用户的当前目录
|
||||||
|
- 可用 `cd` 命令来切换目录
|
||||||
|
- `.`表示当前目录
|
||||||
|
- `..` 表示当前目录的上一级目录(父目录)
|
||||||
|
- `-`表示用 cd 命令切换目录**前**所在的目录
|
||||||
|
- `~` 表示**用户主目录**的绝对路径名
|
||||||
|
|
||||||
|
**绝对路径:**
|
||||||
|
|
||||||
|
- 以斜线(/)开头 ,描述到文件位置的**完整说明** ,任何时候你想指定文件名的时候都可以使用
|
||||||
|
|
||||||
|
**相对路径 :**
|
||||||
|
|
||||||
|
- 不以斜线(/)开头 ,指定**相对于你的当前工作目录而言的位置** ,可以被用作指定文件名的简捷方式
|
||||||
|
|
||||||
|
## 二、文件处理命令
|
||||||
|
|
||||||
|
### 1. 命令格式与目录处理命令`ls`
|
||||||
|
|
||||||
|
**命令格式**:`命令[-选项][-参数]`,例:`ls -la /etc`
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
|
||||||
|
1. 个别命令使用不遵循此格式
|
||||||
|
2. 当有多个选项时,可以写在一起
|
||||||
|
3. 简化选项与完整选项:`-a` 等于 `--all`
|
||||||
|
|
||||||
|
`ls`命令的语法:
|
||||||
|
|
||||||
|
1. `ls -a`可以显示所有文件,包括隐藏文件(以点.开头的文件是隐藏文件)
|
||||||
|
|
||||||
|
2. 若希望查询的不是当前目录,可以使用`ls+其他目录`进行查询
|
||||||
|
|
||||||
|
3. `ls -l`可以显示更多属性(long),属性阐述如下:
|
||||||
|
1. 第一列分为三个部分,第一部分(如d告诉我们文件的类型是一个目录,-为二进制文件,1为软链接文件),drwx表示该文件支持读写和执行操作,r,w,x分别对应读、写、执行三个权限,三列分别对应所有者,所属组,其他人的权限
|
||||||
|
|
||||||
|
2. 第二列的2、2、3等表示调用次数
|
||||||
|
3. 第三列表示所有者,也就是这个文件的总负责人(拥有文件的所有权,可转让)
|
||||||
|
4. 第四列表示所属组,也就是可以操作这个文件的人
|
||||||
|
5. 第五列表示文件大小,默认单位是字节(很反Windows)
|
||||||
|
6. 最后一个是文件的最后一次修改时间(Linux没有创建时间这个概念)
|
||||||
|
|
||||||
|
4. `ls -lh`比原先的更人性化(humanitarian),它将对应的单位也显示了出来,`-h`实际上是一个通用选项,很多命令都可以加
|
||||||
|
|
||||||
|
5. `-d`显示当前目录本身而不显示目录下的数据,一般与`-l`结合使用,如`ls -ld /etc`
|
||||||
|
|
||||||
|
6. `ls -id`可以查看当前目录对应的文件ID
|
||||||
|
|
||||||
|
### 2. 目录处理命令
|
||||||
|
|
||||||
|
##### `mkdir`
|
||||||
|
|
||||||
|
**语法**:`mkdir -p [目录名]`
|
||||||
|
|
||||||
|
**功能描述**:创建新目录,`-p`递归创建(若一个目录本身不存在,可以在创建这个目录的同时创建子目录),也可以同时创建多个目录
|
||||||
|
|
||||||
|
##### `cd`
|
||||||
|
|
||||||
|
**语法**:`cd directory`
|
||||||
|
|
||||||
|
**功能描述**:改变当前目录
|
||||||
|
|
||||||
|
##### `pwd`
|
||||||
|
|
||||||
|
**语法**:`pwd`
|
||||||
|
|
||||||
|
**功能描述**:显示当前目录(print working directory)
|
||||||
|
|
||||||
|
##### `rmdir`
|
||||||
|
|
||||||
|
**语法**:`rmdir [目录名]`
|
||||||
|
|
||||||
|
**功能描述**:删除空目录(若目录非空则不能删除)
|
||||||
|
|
||||||
|
##### `cp`
|
||||||
|
|
||||||
|
**语法**:`cp -rf [源文件或目录] [目标目录] -r 复制目录 -p 保留文件属性(文件创建时间等不发生变化)`
|
||||||
|
|
||||||
|
**功能描述**:复制文件或目录
|
||||||
|
|
||||||
|
##### `mv`
|
||||||
|
|
||||||
|
**语法**:`mv [源文件或目录] [目标目录]`
|
||||||
|
|
||||||
|
**功能描述**:剪切文件、改名
|
||||||
|
|
||||||
|
##### `rm`
|
||||||
|
|
||||||
|
**语法**:`rm -rf [文件或目录] -r 删除目录 -f 强制执行`
|
||||||
|
|
||||||
|
**功能描述**:删除文件
|
||||||
|
|
||||||
|
### 3. 文件处理命令
|
||||||
|
|
||||||
|
##### `touch`
|
||||||
|
|
||||||
|
**语法**:`touch [文件名]`
|
||||||
|
|
||||||
|
**功能描述**:创建空文件
|
||||||
|
|
||||||
|
##### `cat`
|
||||||
|
|
||||||
|
**语法**:`cat [文件名]`
|
||||||
|
|
||||||
|
**功能描述**:显示文件内容 `-n`可显示行号
|
||||||
|
|
||||||
|
##### `tac`
|
||||||
|
|
||||||
|
与`cat`相反,可以倒着显示
|
||||||
|
|
||||||
|
##### `more`
|
||||||
|
|
||||||
|
`cat`命令显示的往往过多,若希望分页显示可以使用`more`,用法与`cat`相同,使用时按空格可以一页页往后翻,使用q或Q退出
|
||||||
|
|
||||||
|
##### `less`
|
||||||
|
|
||||||
|
由于`more`无法向上翻,我们可以使用`less`命令,可以使用page up一页页往上翻,也可以使用上箭头一行行往上翻,其他操作与`more`相同。另外`less`还可以进行搜索,比如想要搜索关键词service,可以输入/service进行检索,页面会对这些关键词进行高亮,可以使用`n`找到其他关键词位置
|
||||||
|
|
||||||
|
##### `head`
|
||||||
|
|
||||||
|
若只想要看文件的前几行,可以使用`head -n`加指定行数,若不加则默认显示前10行
|
||||||
|
|
||||||
|
##### `tail`
|
||||||
|
|
||||||
|
与`head`类似 ,但是显示后面几行。
|
||||||
|
|
||||||
|
常用搭配为:`tail -f`,该命令会动态显示文件末尾内容
|
||||||
|
|
||||||
|
## 三、链接命令`ln`
|
||||||
|
|
||||||
|
**语法**:`ln -s [原文件] [目标文件] -s 创建软链接`
|
||||||
|
|
||||||
|
**功能描述**:生成链接文件
|
||||||
|
|
||||||
|
**示例**:
|
||||||
|
|
||||||
|
* `ln -s /etc/issue issue.soft`:生成软链接
|
||||||
|
* `ln /etc/issue issue.hard`:生成硬链接
|
||||||
|
|
||||||
|
**软链接和硬链接的区别**
|
||||||
|
|
||||||
|
我们使用`ls -l`查看这两个文件的信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
-rw-r--r-- 2 root root 26 Jul 15 2020 issue.hard
|
||||||
|
lrwxrwxrwx 1 root root 10 Jan 31 04:55 issue.soft -> /etc/issue
|
||||||
|
```
|
||||||
|
|
||||||
|
我们会发现这两个文件的信息相差的非常多,软链接文件开头的文件类型是`l(link)`,三个权限都是`rwx`,即可读可写可执行,软链接文件就类似于Windows的快捷方式,用处是便于做管理,我们可以看到最后有一个箭头指向`/etc/issue`。另外我们看到这个文件只有31个字节,因为它只是一个符号链接。我们可以总结得出软链接的三个特点:
|
||||||
|
|
||||||
|
1. 权限是`rwx`
|
||||||
|
2. 文件很小,只是符号链接
|
||||||
|
3. 箭头指向源文件
|
||||||
|
|
||||||
|
下面我们看硬链接的特点,我们首先分别查看 这两个文件的信息:
|
||||||
|
|
||||||
|
```
|
||||||
|
ls -l issue.hard
|
||||||
|
ls -l /etc/issue
|
||||||
|
```
|
||||||
|
|
||||||
|
我们可以看到这两个文件的所有信息一模一样,包括文件的大小,这类似于拷贝,似乎相当于`cp -p`,而硬链接和`cp -p`的最大不同就是硬链接可以实现同步更新,我们可以做一个简单的实验,我们先查看硬链接文件,然后往源文件中写入文件,可以发现硬链接文件也被同时修改了,当然软链接也会同步修改。
|
||||||
|
|
||||||
|
但当我们将源文件复制到另一个位置并删除原位置文件之后,再试图打开软链接会提示“没有那个文件或目录”,而且再显示这个目录软链接会标红并一直闪,而硬链接可以正常访问,没有影响,这就是硬链接和软连接的不同之处。
|
||||||
|
|
||||||
|
实际上我们可以通过命令`ls -i`来识别其`i`节点以辨别出是硬链接还是软链接,硬链接和源文件的`i`节点相同,软链接则不同。
|
||||||
|
|
||||||
|
硬链接相当于一个同步文件,但可以做实时备份(一个文件删了不会影响另一个文件),硬链接有两个限制,这也是硬链接和软链接的区别:
|
||||||
|
|
||||||
|
1. 不能跨分区
|
||||||
|
2. 不能针对目录使用
|
||||||
|
|
||||||
|
## 四、权限管理命令
|
||||||
|
|
||||||
|
Linux用户一共分成三类,分别是所有者(U),所属组(G)和其他人(O),权限也分成三类,分别是`r`,`w`,`x`,对应读、写、执行,我们首先学习如何更改权限。
|
||||||
|
|
||||||
|
#### `chmod`
|
||||||
|
|
||||||
|
更改文件的人只能是文件所有者或者管理员root用户,更改文件权限有两种方式,第一种方式如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod [{ugoa}{+-=}{rwx}][文件或目录]
|
||||||
|
```
|
||||||
|
|
||||||
|
其中第一个花括号里`u`,`g`,`o`,`a`分别表示所有者,所属组,其他人和所有人,第二个花括号`+`和`-`分别表示增加和减少权限,`=`表示成为后面的权限。第二种方式如下:
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod [mod=421][文件或目录] -R 递归修改
|
||||||
|
```
|
||||||
|
|
||||||
|
数字的意思只是将三个权限位分别用数字来表示,比如`r`用4表示,`w`用2表示,`x`用1表示,则若要表示`rwxrw-r--`则记为`764`
|
||||||
|
|
||||||
|
#### `chown`
|
||||||
|
|
||||||
|
命令英文原意是`change file ownership`,作用是改变文件或目录的所有者,改变文件file的所有者为user的具体用法为:
|
||||||
|
|
||||||
|
```
|
||||||
|
chown user file
|
||||||
|
```
|
||||||
|
|
||||||
|
要注意只有root和文件的所有者可以改变文件的权限
|
||||||
|
|
||||||
|
#### `chgrp`
|
||||||
|
|
||||||
|
命令英文原意是`change file group ownership`,作用是改变文件或目录的所属组,若具体用法和前面`chown`相同。我们可以使用`groupadd`命令添加组(使用`useradd`命令添加用户)
|
||||||
|
|
||||||
|
#### `umask`
|
||||||
|
|
||||||
|
命令英文原意是`the user file-creation mask`,作用是显示、设置文件的缺省权限,语法是:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
umask [-S]
|
||||||
|
```
|
||||||
|
|
||||||
|
其中`-S`的作用是显示新建文件的缺省权限,但需要注意的是缺省创建文件时不可以有可执行权限的,所以当`touch`创建文件时会发现所有权限都少了`x`。
|
||||||
|
|
||||||
|
当我们直接使用`umask`时,比如显示0022,第一个0是特殊权限,我们暂时不涉及,第二只第四位分别是所有者、所属组和其他人,我们的最终权限实际上是`777-022=755`,也就是`rwx r-x r-x`,当然这指的是目录,如果是文件由于没有可执行权限,文件权限应当是`rw- r-- r--`,当然缺省创建的权限可以更改,直接使用`umask 077`即可将文件缺省权限更改为`rwx --- ---`,但不推荐做这种更改
|
|
@ -0,0 +1,509 @@
|
||||||
|
<font color='red'> 注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 </font>
|
||||||
|
|
||||||
|
本次组队学习重点在于以太坊基础知识、以太坊客户端以及以太坊solidity编程,因此本节教程重点在于以太坊核心知识点的掌握,区块链部分的基础知识可以作为补充,请学习者量力而行。另外若学习者觉得本节内容难度太高,可以先对基本知识点有一个概览,在第二节以及第三节实战内容学习完成之后再深入学习本节内容。
|
||||||
|
|
||||||
|
# 一、区块链简介 #
|
||||||
|
|
||||||
|
## 1.1、区块链与区块链技术 ##
|
||||||
|
|
||||||
|
在阅读本教程之前,[大家对比特币原理不太了解同学可以先阅读下此博客~](http://blog.codinglabs.org/articles/bitcoin-mechanism-make-easy.html),大家对比特币有简单了解后对于区块链会有更好的认识。
|
||||||
|
|
||||||
|
**区块链**是将记录(区块)通过密码学串联并加密的链式数据结构。而**区块链技术**,是通过P2P网络和区块链来实现数据存储的**去中心化**、**不可逆**和**不可篡改**。比特币正是构建在区块链技术上的典型应用。通过区块链技术,我们可以将信息(数据、程序)保存在区块上并接入到区块链中,这样就实现了信息的去中心化存储、不可逆和不可篡改。**区块链应用**是指利用区块链技术开发的应用。
|
||||||
|
|
||||||
|
## 1.2、区块链历史 ##
|
||||||
|
|
||||||
|
2008年,一个网名叫中本聪(Satoshi Nakamoto)的人发表了一篇名为《比特币:一种点对点电子货币系统》的论文,论文中首次提到了“区块链”这一概念。2009年,中本聪创立了以区块链为底层技术的比特币网络,开发出了第一个区块,被称为“创世区块”。该阶段被称为“区块链1.0”。
|
||||||
|
|
||||||
|
由于比特币是一个电子货币系统,所以主要功能就是记账。但随后人们发现,区块链技术作为比特币的底层技术,功能可以远远不止于记账,许多关于“未知的信任”的问题,都可以通过区块链来解决,例如电子存证、信息记录等。于是在比特币的基础上,诞生了带有智能合约的区块链系统,即允许开发者通过编写智能合约来实现特定的逻辑,这一阶段被称为“区块链2.0”。这一阶段的主要代表是以太坊。
|
||||||
|
|
||||||
|
随后,人们想要提升区块链应用的性能,于是出现了EOS、ArcBlock等系统,其特点是高性能、大吞吐量,但由于引入了超级节点、云节点等特性,弱化了“去中心化”这一特点,因此受到较大的争议。这一阶段被称为“区块链3.0”。
|
||||||
|
|
||||||
|
由于比特币是一款电子货币,可扩展性较低,而所谓的“区块链3.0”目前受到较大争议,且部分项目的底层算法完全不同于典型的区块链,因此学习区块链2.0中的以太坊是目前学习区块链的最佳方式。
|
||||||
|
|
||||||
|
## 1.3、区块链基础技术与算法 ##
|
||||||
|
|
||||||
|
区块链技术不是单独的一项技术,而是一系列技术组成的技术栈,其具有以下的特点:
|
||||||
|
|
||||||
|
* 数据分布式存储
|
||||||
|
* 存储的数据不可逆、不可篡改、可回溯
|
||||||
|
* 数据的创建和维护由所有参与方共同参与
|
||||||
|
|
||||||
|
为了实现这些特点、维护区块链应用的稳定运行,区块链技术中包含了分布式存储技术、密码学技术、共识机制以及区块链2.0提出的智能合约。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 1.3.1、区块
|
||||||
|
|
||||||
|
区块链由一个个区块(block)组成。区块很像数据库的记录,每次写入数据,就是创建一个区块。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\bg2017122703.png" width="300"/>
|
||||||
|
</center>
|
||||||
|
<center>中心化存储</center>
|
||||||
|
每个区块包含两个部分。
|
||||||
|
|
||||||
|
> - 区块头(Head):记录当前区块的特征值
|
||||||
|
> - 区块体(Body):实际数据
|
||||||
|
|
||||||
|
区块头包含了当前区块的多项特征值。
|
||||||
|
|
||||||
|
> - 生成时间
|
||||||
|
> - 实际数据(即区块体)的哈希
|
||||||
|
> - 上一个区块的哈希
|
||||||
|
> - ...
|
||||||
|
|
||||||
|
### 1.3.2、分布式存储技术 ###
|
||||||
|
|
||||||
|
与传统的数据存储技术不同,在区块链技术中,数据并不是集中存放在某个数据中心上,也不是由某个权威机构或是大多数节点来存储,而是分散存储在区块链网络中的每一个节点上。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image2.png" width="300"/>
|
||||||
|
</center>
|
||||||
|
<center>中心化存储</center>
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image3.png" width="300"/>
|
||||||
|
</center>
|
||||||
|
<center>分布式存储</center>
|
||||||
|
**节点和区块的关系是什么?**
|
||||||
|
|
||||||
|
可以用共享文档来简单描述:所有可以访问共享文档的账号就叫做节点,当然全节点需要同步共享文档,也就是拥有全部的区块数据区块就是共享文档。每个人更新了,所有人都可以查看最新的文档
|
||||||
|
|
||||||
|
### 1.3.3、密码学技术 ###
|
||||||
|
|
||||||
|
为了实现数据的不可逆、不可篡改和可回溯,区块链技术采用了一系列密码学算法和技术,包括哈希算法、Merkle 树、非对称加密算法。
|
||||||
|
|
||||||
|
##### 哈希算法 #####
|
||||||
|
|
||||||
|
哈希算法是一个单向函数,可以将任意长度的输入数据转化为固定长度的输出数据(哈希值),哈希值就是这段输入数据唯一的数值表现。由于在计算上不可能找到哈希值相同而输入值不同的字符串,因此两段数据的哈希值相同,就可以认为这两段数据也是相同的,所以哈希算法常被用于对数据进行验证。
|
||||||
|
|
||||||
|
在区块链中,数据存储在区块里。每个区块都有一个区块头,区块头中存储了一个将该区块所有数据经过哈希算法得到的哈希值,同时,每个区块中还存储了前一个区块的哈希值,这样就形成了区块链。如果想要篡改某一个区块A中的数据,就会导致A的哈希值发生变化,后一个区块B就无法通过哈希值正确地指向A,这样篡改者又必须篡改B中的数据......也就是说,篡改者需要篡改被篡改的区块以及后面的所有区块,才能让所有的节点都接受篡改。
|
||||||
|
|
||||||
|
##### Merkle树 #####
|
||||||
|
|
||||||
|
Merkle树是一种树形结构,在区块链中,Merkle树的叶子节点是区块中数据的哈希值,非叶子节点是其子结点组合后的哈希值,这样由叶子节点开始逐层往上计算,最终形成一个Merkle根,记录在区块的头部,这样就可以保证每一笔交易都无法篡改。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image4.png" width="500"/>
|
||||||
|
</center>
|
||||||
|
<center>Merkle 树</center>
|
||||||
|
##### 非对称加密技术 #####
|
||||||
|
|
||||||
|
非对称加密技术使用两个非对称密钥:公钥和私钥。公钥和私钥具有两个特点:
|
||||||
|
|
||||||
|
1. 通过其中一个密钥加密信息后,使用另一个密钥才能解开
|
||||||
|
2. 公钥一般可以公开,私钥则保密
|
||||||
|
|
||||||
|
在区块链中,非对称加密技术主要用于信息加密、数字签名和登录认证。在信息加密场景中,信息发送者A使用接收者B提供的公钥对信息进行加密,B收到加密的信息后再通过自己的私钥进行解密。再数字签名场景中,发送者A通过自己的私钥对信息进行加密,其他人通过A提供的公钥来对信息进行验证,证明信息确实是由A发出。在登录认证场景中,客户端使用私钥加密登录信息后进行发送,其他人通过客户端公钥来认证登录信息。
|
||||||
|
|
||||||
|
- RSA 算法
|
||||||
|
|
||||||
|
RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它。但是有不少新来的同事对它不太了解,恰好看到一本书中作者用实例对它进行了简化而生动的描述,使得高深的数学理论能够被容易地理解。
|
||||||
|
RSA是第一个比较完善的公开密钥算法,它既能用于加密,也能用于数字签名。RSA以它的三个发明者Ron Rivest, Adi Shamir, Leonard Adleman的名字首字母命名,这个算法经受住了多年深入的密码分析,虽然密码分析者既不能证明也不能否定RSA的安全性,但这恰恰说明该算法有一定的可信性,目前它已经成为最流行的公开密钥算法。
|
||||||
|
RSA的安全基于大数分解的难度。其公钥和私钥是一对大素数(100到200位十进制数或更大)的函数。从一个公钥和密文恢复出明文的难度,等价于分解两个大素数之积(这是公认的数学难题)。
|
||||||
|
|
||||||
|
- ECC 椭圆曲线算法
|
||||||
|
|
||||||
|
具体可以参见此文章:[ECC椭圆曲线加密算法:介绍](https://zhuanlan.zhihu.com/p/36326221)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 1.3.4、共识机制 ###
|
||||||
|
|
||||||
|
区块链系统是一个分布式系统,分布式系统要解决都首要问题就是一致性问题,也就是如何使多个孤立的节点达成共识。在中心化系统中,由于有一个中心服务器这样的“领导”来统一各个节点,因此达成一致性几乎没有问题。但在去中心化场景下,由于各个节点是相互独立的,就可能会出现许多不一致的问题,例如由于网络状况等因素部分节点可能会有延迟、故障甚至宕机,造成节点之间通信的不可靠,因此一致性问题是分布式系统中一个很令人头疼的问题。
|
||||||
|
|
||||||
|
由 Eirc Brewer 提出,Lynch 等人证明的 CAP 定理为解决分布式系统中的一致性问题提供了思路。CAP 定理的描述如下:在分布式系统中,**一致性**、**可用性**和**分区容错性**三者不可兼得。这三个术语的解释如下:
|
||||||
|
|
||||||
|
* 一致性(**C**onsistency):所有节点在同一时刻拥有同样的值(等同于所有节点访问同一份最新的数据副本
|
||||||
|
* 可用性(**A**vailability):每个请求都可以在有限时间内收到确定其是否成功的响应
|
||||||
|
* 分区容错性(**P**artition tolerance):分区是指部分节点因为网络原因无法与其他节点达成一致。分区容错性是指由网络原因导致的系统分区不影响系统的正常运行。例如,由于网络原因系统被分为 A, B, C, D 四个区,A, B 中的节点无法正常工作,但 C, D 组成的分区仍能提供正常服务。
|
||||||
|
|
||||||
|
在某些场景下,对一致性、可用性和分区容错性中的某一个特性要求不高时,就可以考虑弱化该特性,来保证整个系统的容错能力。区块链中常见的共识机制的基本思路正是来自 CAP 定理,部分区块链应用中用到的共识机制如下表:
|
||||||
|
|
||||||
|
| 共识机制 | 应用 |
|
||||||
|
| -------- | ---------------------------------- |
|
||||||
|
| PoW | 比特币、莱特币、以太坊的前三个阶段 |
|
||||||
|
| PoS | PeerCoin、NXT、以太坊的第四个阶段 |
|
||||||
|
| PBFT | Hyperledger Fabric |
|
||||||
|
|
||||||
|
##### PoW(Proof of Work,工作量证明) #####
|
||||||
|
|
||||||
|
PoW 机制的大致流程如下:
|
||||||
|
|
||||||
|
1. 向所有节点广播新交易和一个数学问题
|
||||||
|
2. 最先解决了数学问题的节点将交易打包成区块,对全网广播
|
||||||
|
3. 其他节点验证广播区块的节点是否解决了数学问题(完成了一定的工作量),验证通过则接受该区块,并将该区块的哈希值放入下一个区块中,表示承认该区块
|
||||||
|
|
||||||
|
由于在 PoW 机制中,区块的产生需要解决一个数学问题,也就是所谓的**挖矿**,这往往要消耗较大的算力和电力,因此节点们倾向于在**最长的链**的基础上添加区块,因为如果节点想在自己的链上添加新的区块,那么就需要重新计算 1 个或 $n$ 个这样的数学问题(每添加一个区块就需要计算一个)。因此在比特币中最长的链被认为是合法的链,这样节点间就形成了一套“共识”。
|
||||||
|
|
||||||
|
PoW 机制的优点是完全去中心化,缺点是需要依赖数学运算,资源的消耗会比其他的共识机制高,可监管性弱,同时每次达成共识需要全网共同参与运算,性能较低。
|
||||||
|
|
||||||
|
##### PoS(Proof of Stack,股权证明) #####
|
||||||
|
|
||||||
|
PoS 针对 PoW 的缺点做出了改进。PoS 要求参与者预先放置一些货币在区块链上用于换取“股权”,从而成为**验证者(Validator)**,验证者具有产生区块的权利。PoS 机制会按照存放货币的量和时间给验证者分配相应的利息,同时还引入了奖惩机制,打包错误区块的验证者将失去他的股权——即投入的货币以及产生区块的权利。PoS 机制的大致流程如下:
|
||||||
|
|
||||||
|
1. 加入 PoS 机制的都是持币人,称为验证者
|
||||||
|
2. PoS 算法根据验证者持币的多少在验证者中挑选出一个给予产生区块的权利
|
||||||
|
3. 如果一定时间内没有产生区块,PoS 就挑选下一个验证者,给予产生区块的权利
|
||||||
|
4. 如果某个验证者打包了一份欺诈性交易,PoS 将剥夺他的股权
|
||||||
|
|
||||||
|
PoS 的优点在于:
|
||||||
|
|
||||||
|
1. 引入了利息,使得像比特币这样发币总数有限的通货紧缩系统在一定时间后不会“无币可发”
|
||||||
|
2. 引入了奖惩机制使节点的运行更加可控,同时更好地防止攻击
|
||||||
|
3. 与 PoW 相比,不需要为了生成新区块而消耗大量电力和算力
|
||||||
|
4. 与 PoW 相比,缩短了达成共识所需的时间
|
||||||
|
|
||||||
|
由于 PoS 机制需要用户已经持有一定数量的货币,没有提供在区块链应用创立初始阶段处理数字货币的方法,因此使用 PoS 机制的区块链应用会在发布时预先出售货币,或在初期采用 PoW,让矿工获得货币后再转换成 PoS,例如以太坊现阶段采用的是 PoW 机制,在第四阶段“宁静”(Serenity)中将过渡到 PoS。
|
||||||
|
|
||||||
|
##### 拜占庭将军问题(Byzantine Generals Problem) #####
|
||||||
|
|
||||||
|
拜占庭将军问题是分布式网络中的通信容错问题,可以描述为:
|
||||||
|
|
||||||
|
> 一组拜占庭将军各领一支队伍共同围困一座城市。各支军队的行动策略限定为进攻或撤离两种。因为部分军队进攻而部分军队撤离可能会造成灾难性的后果,因此各将军决定通过投标来达成一致策略,即“共进退”。因为各将军位于城市不同方向,他们只能通过信使互相联系。在投票过程中每位将军都将自己的选择(进攻或撤退)通过信使分别通知其他所有将军,这样一来每位将军根据自己的投票和其他所有将军送来的信息就可以知道共同投票的结果,进而做出行动。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image1.png" width="500"/>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
拜占庭将军的问题在于,将军中可能出现叛徒。假设3名将军中有1名叛徒,2名忠诚将军一人投进攻票,一人投撤退票,这时叛徒可能会故意给投进攻的将军投进攻票,而给投撤退的将军投撤退票。这就导致一名将军带队发起进攻,而另外一名将军带队撤退。
|
||||||
|
|
||||||
|
另外,由于将军之间通过信使进行通讯,即使所有将军都忠诚,也不能排除信使被敌人截杀,甚至信使叛变等情况。
|
||||||
|
|
||||||
|
假设存在叛变将军或信使出问题等情况,如果忠诚将军仍然能够通过投票来决定他们的战略,便称系统达到了**拜占庭容错(Byzantine Fault Tolerance)**。
|
||||||
|
|
||||||
|
拜占庭问题对应到区块链中,将军就是节点,信使就是网络等通信系统,要解决的是存在恶意节点、网络错误等情况下系统的一致性问题。
|
||||||
|
|
||||||
|
**PBFT(Practical Byzantine Fault Tolerance)** 是第一个得到广泛应用且比较高效的拜占庭容错算法,能够在节点数量不小于 $n=3f+1$ 的情况下容忍 $f$ 个拜占庭节点(恶意节点)。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# 二、以太坊介绍 #
|
||||||
|
|
||||||
|
首先我们要知道我们为什么要学习以太坊,主要有以下四个原因:
|
||||||
|
|
||||||
|
* 以太坊是区块链2.0的代表,学习以太坊能了解到区块链技术的所有知识
|
||||||
|
* 引入了智能合约,拓宽了区块链的应用场景
|
||||||
|
* 对开发者友好、对用户友好,容易编写出简单的区块链应用,学习趣味性高
|
||||||
|
* Solidity 语法与 Javascript、Go 等语言接近,易上手
|
||||||
|
|
||||||
|
## 2.1、以太坊简介 ##
|
||||||
|
|
||||||
|
区块链技术常常被认为是自互联网诞生以来最具颠覆性的技术,然而,自比特币诞生后一直没有很好的区块链应用开发平台。想要在比特币基础上开发区块链应用是非常复杂繁琐的,因为比特币仅仅是一个加密数字货币系统,无法用来实现更广阔的业务需求。以太坊是目前使用最广泛的支持完备应用开发的共有区块链系统。
|
||||||
|
|
||||||
|
和比特币不同,比特币只适合加密数字货币场景,不具备图灵完备性,也缺乏保存实时状态的账户概念,以及存在 PoW 机制带来的效率和资源浪费的问题,而以太坊作为区块链2.0的代表,目标是扩展智能合约和建立一个去中心化应用平台,具有图灵完备的特性、更高效的共识机制、支持智能合约等多种应用场景,使得开发者能够很方便地在以太坊上开发出基于区块链的应用。
|
||||||
|
|
||||||
|
### 2.1.1、以太坊的发展 ###
|
||||||
|
|
||||||
|
2014年, Vitalik Buterin 发表了文章《以太坊:一个下一代智能合约和去中心化应用平台》。同年,Buterin 在迈阿密比特币会议中宣布启动以太坊项目,并提出了多项创新性的区块链技术。2015年,以太坊CCO Stephan Tual 在官方博客上宣布以太坊系统诞生,主网上线。
|
||||||
|
|
||||||
|
以太坊发展至今经历了“前沿”(Frontier)、“家园”(Homestead)以及现在所处的“大都会”(Metropolis)三个阶段。第四阶段“宁静”(Serenity)将作为以太坊的最后一个阶段,目前尚未有计划发布日期。
|
||||||
|
|
||||||
|
### 2.1.2、以太坊的特点 ###
|
||||||
|
|
||||||
|
以太坊团队和外界对以太坊的描述都是“世界计算机”,这代表它是一个开源的、全球的去中心化计算架构。它执行称为智能合约的程序,并使用区块链来同步和存储系统状态,以及使用名为以太币的加密数字货币来计量和约束执行操作的资源成本。同时,以太坊提供了一系列的接口,使得开发者能够通过以太坊来开发去中心化 Web 应用DApps。
|
||||||
|
|
||||||
|
### 2.1.3、智能合约 ###
|
||||||
|
|
||||||
|
相比比特币,以太坊最大的特点就是引入了**智能合约**。智能合约本质上就是一段编写好的程序,可以在特定的条件下被触发并执行特定的操作。由于区块链具有不可逆和不可篡改的特点,因此智能合约与区块链结合后,就成了一份“强制执行”的合约。
|
||||||
|
|
||||||
|
以太坊能够作为一个去中心化应用平台和”世界计算机”,其核心就是智能合约。智能合约的引入,使得开发者能够实现许多(理论上是任何)业务逻辑。如果说比特币是通过区块链技术开发的特定计算器,那么引入了智能合约的以太坊就是基于区块链技术的通用计算机。可以简单的理解成:比特币的交易系统就是一份写死的智能合约,而以太坊则将智能合约的开发权限交给开发者。
|
||||||
|
|
||||||
|
以太坊提供了对智能合约的全面支持,包括编写智能合约编程语言 **Solidity** 和运行智能合约的**以太坊虚拟机(Ethereum Virtual Machine,EVM)**。
|
||||||
|
|
||||||
|
### 2.1.4、幽灵协议 ###
|
||||||
|
|
||||||
|
幽灵合约的英文是“Greedy Heaviest Observed Subtree" (GHOST) protocol,在介绍幽灵协议之前,先介绍以太坊中的叔区块、叔块奖励和叔块引用奖励这三个概念。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image5.png" width="400"/>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
假设目前以太坊区块链中的区块高度(区块链上的区块个数)为6,现在产生了一笔新的交易,矿工A先将该笔交易打包成了区块 Block 7,在矿工A将 Block 7 广播到其他节点的这段时间里,矿工B和矿工C又分别产生了 Block 8 和 Block 9。Block 7、Block 8、Block 9 都指向 Block 6,即 Block 6 是他们的父区块。由于 Block 7 是最先产生的,因此 Block 7 被认为是有效区块,Block 8 和 Block 9 就是**叔区块**(作废区块)。
|
||||||
|
|
||||||
|
<center class="half">
|
||||||
|
<img src=".\pic\image6.png" width="300"/>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
现在链上的区块高度为7,在这基础上又产生了新的交易,并被打包成了 Block 10。在以太坊中,Block 10 除了可以引用它的父区块 Block 7 外,还可以引用叔区块 Block 8 和 Block 9。并且,Block 8 和 Block 9 的矿工会因此获得一笔奖励,称为**叔块奖励**,Block 10 的矿工除了基础奖励之外,由于引用了叔区块,还会获得一笔额外的**叔块引用奖励**。
|
||||||
|
|
||||||
|
**幽灵协议**是以太坊的一大创新。由于在比特币中的出块时间被设计为10分钟,而以太坊为了提高出块速度,将出块时间设计为12秒(实际14~15秒左右),这样的高速出块意味着高速确认,高速确认会带来区块的**高作废率**和**低安全性**。因为区块需要花一定的时间才能广播至全网,如果矿工 A 挖出了一个区块,而矿工 B 碰巧在 A 的区块扩散至 B 之前挖出了另一个区块,矿工 B 的区块就会作废并且没有对区块链的网络安全做出贡献。此外,这样的高速确认还会带来**中心化**的问题:如果 A 拥有全网 30% 的算力而 B 拥有 10% 的算力,那么 A 将会在 70% 的时间内都在产生作废区块,而 B 在 90% 的时间内都在产生作废区块,这样,B 永远追不上 A,后果是 A 通过其算力份额拥有对挖矿过程实际上的控制权,出现了算力垄断,弱化了去中心化。
|
||||||
|
|
||||||
|
幽灵协议正是为了解决上述问题而引入的,协议的主要内容如下:
|
||||||
|
|
||||||
|
- 计算最长链时,不仅包括当前区块的父区块和祖区块,还包括祖先块的作废的后代区块(叔区块),将它们综合考虑来计算哪一个区块拥有支持其的最大工作量证明。这解决了网络安全性的问题
|
||||||
|
- 以太坊付给以“叔区块”身份为新块确认作出贡献的废区块87.5%的奖励(叔块奖励),把它们纳入计算的“侄子区块”将获得奖励的12.5%(叔块引用奖励)。这就使得即使产生作废区块的矿工也能够参与区块链网络贡献并获得奖励,解决了中心化倾向的问题
|
||||||
|
- 叔区块最深可以被其父母的第二代至第七代后辈区块引用。这样做是为了:
|
||||||
|
- 降低引用叔区块的计算复杂性
|
||||||
|
- 过多的叔块引用奖励会剥夺矿工在主链上挖矿的激励,使得矿工有转向公开攻击者链上挖矿的倾向(即公开攻击者可能会恶意产生大量作废区块,无限引用将会诱使矿工转移到攻击者的链上,从而抛弃合法的主链)
|
||||||
|
- 计算表明带有激励的五层幽灵协议即使在出块时间为15s的情况下也实现了了95%以上的效率,而拥有25%算力的矿工从中心化得到的益处小于3%
|
||||||
|
|
||||||
|
### 2.1.5、以太坊的组成部分 ###
|
||||||
|
|
||||||
|
在以太坊中,包括了 P2P 网络、共识机制、交易、状态机、客户端这几个组成部分。
|
||||||
|
|
||||||
|
* P2P 网络:在以太坊主网上运行,可通过TCP端口30303访问,并运行称为 ÐΞVp2p 的协议。
|
||||||
|
* 共识机制:以太坊目前使用名为 Ethash 的 POW 算法,计划在将来会过渡到称为 Casper 的 POS 算法。
|
||||||
|
* 交易:以太坊中的交易本质上是网络消息,包括发送者、接收者、值和数据载荷(payload)。
|
||||||
|
* 状态机:以太坊的状态转移由以太坊虚拟机(Ethereum Virtual Machine,EVM)处理,EVM 能够将智能合约编译成机器码并执行。
|
||||||
|
* 客户端:用于用户和以太坊进行交互操作的软件实现,最突出的是 Go-Ethereum(Geth) 和 Parity。
|
||||||
|
|
||||||
|
### 2.1.6、以太坊中的概念 ###
|
||||||
|
|
||||||
|
* 账户:以太坊中的账户类似于银行账户、应用账户,每个账户有一个20字节的地址。账户又分为**普通账户**(又叫外部账户,External Owned Account, EOA)和**合约账户**(Contract)。普通账户是由以太坊使用者创建的账户,包含地址、余额和随机数;合约账户是创建智能合约时建立的账户,包含存储空间和合约代码
|
||||||
|
* 状态:状态是由账户和两个账户之间价值的转移以及信息的状态转换构成的
|
||||||
|
* 地址:地址是一个账户 ECDSA 公钥的 Keccak 散列最右边的160位,通过地址可以在以太坊上接收或发送交易。在 Etherscan 上,可以通过地址来查询一个账户的信息
|
||||||
|
* 交易:以太坊中的交易不仅包括发送和接收以太币,还包括向合约账户发送交易来调用合约代码、向空用户发送交易来生成以交易信息为代码块的合约账户
|
||||||
|
* Gas:Gas 是以太坊中的一种机制,用于执行智能合约或交易操作的虚拟燃料。由于以太坊是图灵完备的,为了避免开发者无意或恶意编写出死循环等浪费资源或滥用资源的情况,以太坊中的每一笔交易都需支付一定的 Gas (燃料费),即需支付一定的以太币作为 Gas。Gas 的金额通常是由交易的发起者指定并支付的
|
||||||
|
* 挖矿:和比特币类似,以太坊同样通过挖矿来产生区块。在以太坊目前的 PoW 机制下,每当一笔交易发出并广播,就会吸引矿工来将该交易打包成区块。每产生一个区块都会有一笔**固定奖励**给矿工,目前的固定奖励是3个以太。同时,区块中所有操作所需的 Gas 也会作为奖励给矿工。与比特币不同的是,以太坊中产生叔块的矿工可能会获得叔块奖励,引用叔块的矿工会获得叔块引用奖励
|
||||||
|
* DApp(去中心化应用):通过智能合约,开发者能够设计想要的逻辑,相当于是网站的后端。而 DApp 则相当于是一个完整的网站(前端+后端),因此 DApp = 智能合约 + Web 前端。以太坊提供了一个名为 web3.js 的 Javascript 库,通过 web3.js 可以实现 Web 与以太坊区块链的交互和与智能合约的交互,方便开发者创建 DApp
|
||||||
|
|
||||||
|
## 2.2、以太坊基础 ##
|
||||||
|
|
||||||
|
### 2.2.1、以太坊中的货币 ###
|
||||||
|
|
||||||
|
以太坊中的货币称为 **以太币**,单位为**以太(Ether)**,也称 ETH 或符号 Ξ。以太可以被分割为更小的单位,最小的单位是 wei,1 以太 = $10^18$ wei。以太币各单位的名称及之间的关系如下表:
|
||||||
|
|
||||||
|
7
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 2.2.2、以太坊钱包 ###
|
||||||
|
|
||||||
|
以太坊钱包是用于创建和广播交易的应用程序,常用的钱包有
|
||||||
|
|
||||||
|
* MetaMask,一款基于浏览器扩展的钱包,可以很方便地添加到 Chrome, FireFox 等支持扩展的浏览器中
|
||||||
|
* Jaxx,一款跨平台、多币种的钱包
|
||||||
|
* MyEtherWallet(MEW),一款基于 Web 的钱包,可以在任何浏览器中运行
|
||||||
|
* Emerald Wallet,一款被设计来用于以太坊经典区块链的钱包,但也与其他以太坊区块链兼容
|
||||||
|
|
||||||
|
#### MetaMask 基础 ####
|
||||||
|
|
||||||
|
以 Chrome 为例,访问 [Google 网上应用商店](https://chrome.google.com/webstore/category/extensions),搜索 MetaMask 并添加至 Chrome
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
添加完成后 Chrome 会自动打开初始化页面
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
初次使用创建钱包
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
为钱包设置密码
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
创建密码后,MetaMask 会生成一串密语,密语是12个随机的英文单词,用于防止密码忘记。密语可以直接当成密码使用,因此需要妥善保管
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
注册完毕后就可以在 Chrome 地址栏右边的扩展程序栏点击 🦊 图标使用 MetaMask 了
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#### 获取测试以太 ####
|
||||||
|
|
||||||
|
除了以太坊主网以外,以太坊还提供了 Ropsten, Kovan, Rinkeby, Goerli 这几个公共测试网络,另外还支持局域网测试网络和自建测试网络。在这里我们切换到 Ropsten 测试网络
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
随后点击 **Buy** 按钮,点击**测试水管**下方的获取以太
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
在打开的页面中点击 request 1 ether from faucet 就可以得到1个测试以太,当然,可以多次点击。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
测试以太仅供测试使用,除此之外没有任何价值,测试完毕后剩下的以太可以发送到水龙头账户捐赠给水龙头,以供他人测试使用。
|
||||||
|
|
||||||
|
## 2.3、以太坊交易的数据结构
|
||||||
|
|
||||||
|
在以太坊网络中,交易执行属于一个事务。具有原子性、一致性、隔离性、持久性特点。
|
||||||
|
|
||||||
|
- 原子性: 是不可分割的最小执行单位,要么做,要么不做。
|
||||||
|
- 一致性: 同一笔交易执行,必然是将以太坊账本从一个一致性状态变到另一个一致性状态。
|
||||||
|
- 隔离性: 交易执行途中不会受其他交易干扰。
|
||||||
|
- 持久性: 一旦交易提交,则对以太坊账本的改变是永久性的。后续的操作不会对其有任何影响。
|
||||||
|
|
||||||
|
以太坊交易的本质是由外部拥有的账户发起的签名消息,由以太坊网络传输,并被序列化后记录在以太坊区块链上,**交易是唯一可以触发状态更改或导致合约在EVM中执行的事物**
|
||||||
|
|
||||||
|
### 2.3.1、交易的数据结构
|
||||||
|
|
||||||
|
以太坊的数据结构主要可以分为四部分:`nonce`、`gas`、交易目标和消息(主要部分)、交易签名
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
开头是一个 uint64 类型的数字,称之为随机数。用于撤销交易、防止双花和修改以太坊账户的 Nonce 值。
|
||||||
|
|
||||||
|
第二部分是关于交易执行限制的设置,gas 为愿意供以太坊虚拟机运行的燃料上限。 `gasPrice` 是愿意支付的燃料单价。`gasPrcie * gas` 则为愿意为这笔交易支付的最高手续费。
|
||||||
|
|
||||||
|
第三部分是交易发送者输入以太坊虚拟机执行此交易的初始信息: 虚拟机操作对象(接收方 To)、从交易发送方转移到操作对象的资产(Value),以及虚拟机运行时入参(input)。其中 To 为空时,意味着虚拟机无可操作对象,**此时虚拟机将利用 input 内容部署一个新合约**。
|
||||||
|
|
||||||
|
第四部分是交易发送方对交易的签名结果,可以利用交易内容和签名结果反向推导出签名者,即交易发送方地址。以上总结如下:
|
||||||
|
|
||||||
|
* `nonce`:由发起人EOA发出的序列号,用于防止交易消息重播。
|
||||||
|
* `gas price`:交易发起人愿意支付的gas单价(wei)。
|
||||||
|
* `start gas`:交易发起人愿意支付的最大gas量。
|
||||||
|
* `to`:目的以太坊地址。
|
||||||
|
* `value`:要发送到目的地的以太数量。
|
||||||
|
* `data`:可变长度二进制数据负载(payload)。
|
||||||
|
* `v,r,s`:发起人EOA的ECDSA签名的三个组成部分。
|
||||||
|
* 交易消息的结构使用递归长度前缀(RLP)编码方案进行序列化,该方案专为在以太坊中准确和字节完美的数据序列化而创建。
|
||||||
|
|
||||||
|
### 2.3.2、交易中的`nonce`
|
||||||
|
|
||||||
|
按以太坊黄皮书的定义, `nonce`是一个标量值,它等于从这个地址发送的交易数,或者对于关联code的帐户来说,是这个帐户创建合约的数量。因此`nonce`便有以下特征:
|
||||||
|
|
||||||
|
* `nonce`不会明确存储为区块链中帐户状态的一部分。相反,它是通过计算发送地址的已确认交易的数量来动态计算的。
|
||||||
|
* `nonce`值还用于防止错误计算账户余额。`nonce`强制来自任何地址的交易按顺序处理,没有间隔,无论节点接收它们的顺序如何。
|
||||||
|
* 使用`nonce`确保所有节点计算相同的余额和正确的序列交易,等同于用于防止比特币“双重支付”(“重放攻击”)的机制。但是,由于以太坊跟踪账户余额并且不单独跟踪 `UTXO` ,因此只有在错误地计算账户余额时才会发生“双重支付”。`nonce`机制可以防止这种情况发生。
|
||||||
|
|
||||||
|
### 2.3.3、并发和`nonce`
|
||||||
|
|
||||||
|
以太坊是一个允许操作(节点,客户端,DApps)并发的系统,但强制执行单例状态。例如,出块的时候只有一个系统状态。假如我们有多个独立的钱包应用或客户端,比如 MetaMask 和 Geth,它们可以使用相同的地址生成交易。如果我们希望它们都够同时发送交易,该怎么设置交易的`nonce`呢?一般有以下两种做法:
|
||||||
|
|
||||||
|
* 用一台服务器为各个应用分配`nonce`,先来先服务——可能出现单点故障,并且失败的交易会将后续交易阻塞。
|
||||||
|
* 生成交易后不分配`nonce`,也不签名,而是把它放入一个队列等待。另起一个节点跟踪`nonce`并签名交易。同样会有单点故障的可能,而且跟踪`nonce`和签名的节点是无法实现真正并发的。
|
||||||
|
|
||||||
|
### 2.3.4、交易中的`gas`
|
||||||
|
|
||||||
|
Gas 中译是:瓦斯、汽油,代表一种可燃气体。 这形象地比喻以太坊的交易手续费计算模式,不同于比特币中**直接**支付比特币作为转账手续费, 以太坊视为一个去中心化的计算网络,当你发送Token、执行合约、转移以太币或者在此区块上干其他的时候,计算机在处理这笔交易时需要进行计算消耗网络资源,这样你必须支付燃油费购买燃料才能让计算机为你工作。最终燃料费作为手续费支付给矿工。
|
||||||
|
|
||||||
|
> 注:可以在Etherscan上查询gas price与confirmation time的关系,如下图
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
因为手续费等于`gasPrice * gasUsed`,用户在转账,特别是执行智能合约时 gasUsed 无法提前预知。 这样存在一个风险,当用户的交易涉及一个恶意的智能合约,该合约执行将消耗无限的燃料, 这样会导致交易方的余额全部消耗(恶意的智能合约有可能是程序Bug,如合约执行陷入一个死循环)。
|
||||||
|
|
||||||
|
为了避免合约中的错误引起不可预计的燃料消耗,用户需要在发送交易时设定允许消耗的燃料上限,即 gasLimit。 这样不管合约是否良好,最坏情况也只是消耗 gasLimit 量的燃料。
|
||||||
|
|
||||||
|
然而,一笔交易所必须支付的燃料已经在区块中通过该交易已执行的计算量记录。 如果你不想支出太多燃料,而故意设置过低的 gasLimit 是没太多帮助的。 你必须支付足够燃料来支付本交易所必要的计算资源。如果交易尚未执行完成,而燃料已用完, 将出现一个 `Out of Gas` 的错误。特别注意的是,即使交易失败,你也必须为已占用的计算资源所支付手续费。 比如,你通过合约给 TFBOYS 投票,设置 gasPrice=2 gwei,gasLimit=40000(实现投票需要40001的燃料开销), 最终你投票失败且仍然需要支付 40000*2 gwei= 80000 gwei= 0.00008 ETH。
|
||||||
|
|
||||||
|
另外,如果最终 gasUsed 低于 gasLimit,即燃料未用完。则剩余燃料(gasLimit - gasUsed )将在交易后退还给你。 比如你发送 1 Ether 到另一个账户B,设置 gas limit 为 400000,将有 400000 - 21000 返回给你。
|
||||||
|
|
||||||
|
> 注意:21000 是标准转账交易的gasUsed。因此一笔标准的转账交易你可以设置 gasLimit 为21000
|
||||||
|
|
||||||
|
## 2.4、以太坊账户
|
||||||
|
|
||||||
|
对比比特币的UTXO余额模型,以太坊使用“账户”余额模型。 以太坊丰富了账户内容,除余额外还能自定义存放任意多数据。 并利用账户数据的可维护性,构建智能合约账户。下面我们首先将比特币的UTXO余额模型与以太坊账户进行比较,说明其各自的优缺点以及适用性。
|
||||||
|
|
||||||
|
### 2.4.1、比特币UTXO和以太坊账户结构比较
|
||||||
|
|
||||||
|
在当前的区块链项目中,主要有两种记录保存方式,**一种是账户/余额模型,一种是UTXO模型**。比特币采用就是UTXO模型,以太坊、EOS等则采用的是账户/余额模型。
|
||||||
|
|
||||||
|
<img src="pic/utxo_com.jpg" style="zoom:67%;" />
|
||||||
|
|
||||||
|
### 2.4.2、比特币UTXO
|
||||||
|
|
||||||
|
UTXO是 Unspent Transaction Output的缩写,意思是**未花费的输出,**可以简单理解为还没有用掉的收款。比如韩梅梅收到一笔比特币,她没有用掉,这笔比特币对她来说就是一个UTXO。关于UTXO的具体介绍大家可以查看[这篇文章](https://zhuanlan.zhihu.com/p/74050135)。
|
||||||
|
|
||||||
|
**UTXO 核心设计思路是:它记录交易事件,而不记录最终状态。**要计算某个用户有多少比特币,就要对其钱包里所有的UTXO求和,得到结果就是他的持币数量。UTXO模型在转账交易时,是以UTXO为单位的,也就是说在支付时,调用的是整数倍UTXO,比如1个UTXO,3个UTXO,没有0.5个UTXO的说法。
|
||||||
|
|
||||||
|
* 比特币在基于UTXO的结构中存储有关用户余额的数据,系统的整个状态就是一组UTXO的集合,每个UTXO都有一个所有者和一个面值(就像不同的硬币),而交易会花费若干个输入的UTXO,并根据规则创建若干个新的UTXO
|
||||||
|
* 每个引用的输入必须有效并且尚未花费,对于一个交易,必须包含有每个输入的所有者匹配的签名,总输入必须大于等于总输出值。所以系统中用户的余额是用户具有私钥的UTXO的总值
|
||||||
|
|
||||||
|
### 2.4.3、以太坊账户
|
||||||
|
|
||||||
|
为什么以太坊不用UTXO呢?显然是因为麻烦,以太坊的做法更符合直觉,以太坊中的状态就是系统中所有账户的列表,每个账户都包含了一个余额和以太坊**特殊定义的数据**(代码和内部存储)。如果发送账户有足够多的余额来进行支付,则交易有效,在这种情况下发送账户先扣款,而收款账户将记入这笔收入。**如果接受账户有相关代码,则代码会自动运行,并且它的内部存储也可能被更改,或者代码还可能向其他账户发送额外的消息,这就会导致进一步的借贷资金关系。**
|
||||||
|
|
||||||
|
### 2.4.4、优缺点比较
|
||||||
|
|
||||||
|
**比特币UTXO的优点**:
|
||||||
|
|
||||||
|
* 更高程度的隐私:如果用户为他们收到的每笔交易使用新地址,那么通常很难将账户互相链接。这很大程度上适用于货币,但不太适用于任何dapps,因为dapps通常涉及跟踪和用户绑定的复杂状态,可能不存在像货币那样简单的用户状态划分方案
|
||||||
|
* 潜在的可扩展性:UTXO在理论上更符合可扩展性要求,因为我们只需要依赖拥有UTXO的那些人去维护基于Merkle树的所有权证明就够了,即使包括所有者在内的每个人都决定忘记该数据,那么也只有所有者受到对应的UTXO的损失,不影响接下来的交易。而在账户模式中,如果每个人都丢失了与账户相对应的Merkle树的部分,那将会使得和该账户有关的消息完全无法处理,包括发币给它。
|
||||||
|
|
||||||
|
**以太坊账户模式的优点**:
|
||||||
|
|
||||||
|
* 可以节省大量空间:不将UTXOs分开存储,而是合成一个账户;每个交易只需要一个输入、一个签名并产生一个输出
|
||||||
|
* 更好的可替代性:货币本质上都是同质化、可替代的;UTXO的设计使得货币从来源分成了“可花费”和“不可花费”两类,这在实际应用中很难有对应模型
|
||||||
|
* 更加简单:更容易编码和理解,特别是设计复杂脚本的时候,UTXO的脚本逻辑复杂时更令人费解
|
||||||
|
* 便于维护持久轻节点:只要沿着特定方向扫描状态树,轻节点 可以很容易地随时访问账户相关的所有数据。而UTXO地每个交易都会使得状态引用发生改变,这对应节点来说长时间运行Dapp会有很大压力
|
||||||
|
|
||||||
|
### 2.4.5、总结
|
||||||
|
|
||||||
|
| | BitCoin | Ethereum |
|
||||||
|
| ------------ | ---------------- | ---------------- |
|
||||||
|
| **设计定位** | 现金系统 | 去中心化应用平台 |
|
||||||
|
| **数据组成** | 交易列表(账本) | 交易和账户状态 |
|
||||||
|
| **交易对象** | UTXO | Accounts |
|
||||||
|
| **代码控制** | 脚本 | 智能合约 |
|
||||||
|
|
||||||
|
## 2.5、以太坊账户类型
|
||||||
|
|
||||||
|
以太坊作为智能合约操作平台,将账户划分为两类:外部账户(EOAs)和合约账户(contract account),下面分别做简要介绍:
|
||||||
|
|
||||||
|
<img src="pic/EOA_CA.png" style="zoom:67%;" />
|
||||||
|
|
||||||
|
### 2.5.1、外部账户(EOA)
|
||||||
|
|
||||||
|
外部账户是由人来控制的,也就是常规理解的普通账户,外部账户包含以太币余额,主要作用就是发送交易(是广义的交易,包括转币和触发合约代码),是由用户私钥控制的,没有关联代码,所有在以太坊上交易的发起者都是外部账户。
|
||||||
|
|
||||||
|
外部账户特点总结:
|
||||||
|
|
||||||
|
1. 拥有以太余额。
|
||||||
|
2. 能发送交易,包括转账和执行合约代码。
|
||||||
|
3. 被私钥控制。
|
||||||
|
4. 没有相关的可执行代码。
|
||||||
|
|
||||||
|
### 2.5.2、合约账户(CA)
|
||||||
|
|
||||||
|
合约账户有时也叫内部账户,有对应的以太币余额和关联代码,它是由代码控制的,可以通过交易或来自其他合约的调用消息来触发代码执行,执行代码时可以操作自己的存储空间,也可以调用其他合约
|
||||||
|
|
||||||
|
合约账户特点总结:
|
||||||
|
|
||||||
|
1. 拥有以太余额。
|
||||||
|
2. 有相关的可执行代码(合约代码)。
|
||||||
|
3. 合约代码能够被交易或者其他合约消息调用。
|
||||||
|
4. 合约代码被执行时可再调用其他合约代码。
|
||||||
|
5. 合约代码被执行时可执行复杂运算,可永久地改变合约内部的数据存储。
|
||||||
|
|
||||||
|
|
||||||
|
如果大家对概念还理解不深可以先尝试学习后面部分,本教程内容有限,推荐大家有精力阅读以下读物:
|
||||||
|
- [区块链学习的书籍](https://www.zhihu.com/question/61156867)
|
||||||
|
- [区块链入门教程](https://www.ruanyifeng.com/blog/2017/12/blockchain-tutorial.html)
|
||||||
|
- [IBM教程](https://developer.ibm.com/zh/technologies/blockchain/tutorials/)
|
||||||
|
|
||||||
|
**参考自:**
|
||||||
|
1. [比特币白皮书]https://www.8btc.com/wiki/bitcoin-a-peer-to-peer-electronic-cash-system)
|
||||||
|
2. [以太坊白皮书](https://ethfans.org/posts/ethereum-whitepaper)
|
||||||
|
3. [超级账本白皮书](https://www.chainnode.com/doc/399)
|
||||||
|
4. [闪电网络白皮书](https://www.chainnode.com/doc/399)
|
|
@ -0,0 +1,406 @@
|
||||||
|
<font color='red'> 注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 </font>
|
||||||
|
|
||||||
|
# 合约编写实战实例
|
||||||
|
|
||||||
|
## 一、简单代币合约
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
pragma solidity > 0.4.22;
|
||||||
|
|
||||||
|
contract Coin{
|
||||||
|
//这里我们定义了一个address 作为key, uint做为value的hashTable balances; 我们还定义了一个address的变量minter;
|
||||||
|
address public minter;
|
||||||
|
mapping(address=>uint) balances;
|
||||||
|
event Sent(address from, address to, uint amount);
|
||||||
|
constructor(){
|
||||||
|
//代表创建这个合约的账户地址,被赋值给变量minter.
|
||||||
|
minter = msg.sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
//添加一个挖矿合约
|
||||||
|
function mint(address receiver, uint amount) public{
|
||||||
|
require(msg.sender == minter);
|
||||||
|
balances[receiver] += amount;
|
||||||
|
|
||||||
|
}
|
||||||
|
function send(address receiver, uint amount) public{
|
||||||
|
require(balances[msg.sender] >= amount);
|
||||||
|
balances[msg.sender] -= amount;
|
||||||
|
balances[receiver] += amount;
|
||||||
|
emit Sent(msg.sender,receiver,amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
解析:
|
||||||
|
上面实现一个简单的加密货币,币在这里可以无中生有,但只有创建合约的人才能做到,且任何人都可以给他人转币,无需注册名和密码。
|
||||||
|
|
||||||
|
`address`类型是一个160位的值,不允许任何算数操作,这种类型适合存储合约地址或外部人员。
|
||||||
|
|
||||||
|
`mappings`可看作是一个哈希表,它会执行虚拟初始化,以使得所有可能存在的键都映射到一个字节表示为全零的值。
|
||||||
|
|
||||||
|
`event Sent(address from, address to, uint amount)`;声明了一个所谓的事件,它在send函数最后一行被发出。用户界面可以监听区块链上正在发送的事件,且不会花费太多成本,一旦它被发出,监听该事件的listener都将收到通知,而所有的事件都包含了`from`,`t`o和`amoun`t三个参数,可方便追踪事务。
|
||||||
|
|
||||||
|
`msg.sender`始终是当前函数或者外部函数调用的来源地址。
|
||||||
|
|
||||||
|
最后真正被用户和其他合约所调用的,用于完成本合约功能的方法是`mint`和`send`。若`mint`被合约创建者外的其他调用则说明都不会发生。
|
||||||
|
|
||||||
|
`send`函数可被任何人用于向其他人发送代币,前提是发送者拥有这些代币,若使用合约发送代币给一个地址,当在区块链浏览器上查到该地址时时看不到任何相关信息的,因为,实际上发送币和更改余额的信息仅仅存在特定合约的数据存储器中。通过使用事件,可非常简单地为新币创建一个区块链浏览器来追踪交易和余额。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-15.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 二、水龙头合约
|
||||||
|
|
||||||
|
在前面我们通过 Ropsten 测试网络的水龙头(Faucet)获取了一些以太币,并提到可以向水龙头账户发送以太币来捐赠以太币。实际上,水龙头账户是一个合约账户,水龙头就是一份合约,而整个网站就是合约+前端组成的DApp。下面我们通过 Remix 来编写一个简单的水龙头合约,借此了解如何创建、部署合约以及一些 Solidity 的基本语法。
|
||||||
|
|
||||||
|
首先打开 Remix,并新建一个名为 faucet.sol 的文件,该文件就是 Solidity 的源文件
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-16.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
打开 faucet.sol,并写入如下代码
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
contract faucet {
|
||||||
|
function withdraw (uint amount) public {
|
||||||
|
require (amount <= 1e18);
|
||||||
|
msg.sender.transfer (amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
receive () external payable {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
通过这几行代码我们就实现了一个非常简单的水龙头合约。首行代码 `pragma solidity ^0.7.0 `是一个**杂注**,指定了我们的源文件使用的编译器版本不能低于 0.7.0,也不能高于 0.8.0。
|
||||||
|
|
||||||
|
`contract faucet{...}` 声明了一个合约对象,合约对象类似面向对象语言中的类,对象名必须跟文件名相同。
|
||||||
|
|
||||||
|
接下来通过 `function withdraw (uint amount) public {...}` 创建了一个名为 withdraw 的函数,该函数接收一个无符号整数(uint)作为参数,并且被声明为 public 函数,意为可以被其他合约调用。
|
||||||
|
|
||||||
|
withdraw 函数体中的 `require` 是 Solidity 的内置函数,用来检测括号中的条件是否满足。条件满足则继续执行合约,条件不满足则合约停止执行,回撤所有执行过的操作,并抛出异常。在这里我们通过 `require (amount <= 1e18)` 来检测输入的以太币值是否小于等于1个以太。
|
||||||
|
|
||||||
|
接下来的这一行 `msg.sender.transfer (amount)` 就是实际的提款操作了。`msg` 是 Solidity 中内置的对象,所有合约都可以访问,它代表触发此合约的交易。也就是说当我们调用 `withdraw` 函数的时候实际上触发了一笔交易,并用 `msg` 来表示它。`sender` 是交易 `msg` 的属性,表示了交易的发件人地址。函数 `transfer` 是一个内置函数,它接收一个参数作为以太币的数量,并将该数量的以太币从合约账户发送到调用合约的用户的地址中。
|
||||||
|
|
||||||
|
最后一行是一个特殊的函数 `receive` ,这是所谓的 `fallback` 或 `default` 函数。当合约中的其他函数无法处理发送到合约中的交易信息时,就会执行该函数。在这里,我们将该函数声明为 `external` 和 `payable` ,`external` 意味着该函数可以接收来自外部账户的调用,`payable` 意味着该函数可以接收来自外部账户发送的以太币。
|
||||||
|
|
||||||
|
这样,当我们调用合约中的 `withdraw` 并提供一个参数时,我们可以从这份合约中提出以太币;当我们向合约发送以太币时,就会调用 `receive` 函数往合约中捐赠以太币。
|
||||||
|
|
||||||
|
代码编写完毕后,在 Remix 左侧的功能栏中选择第二项,并点击 *Compile faucet.sol* 来编译我们的 sol 文件。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-17.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
编译完成后会出现一个 Warning,提示我们添加 SPDX license,可以忽略。
|
||||||
|
|
||||||
|
随后选择 Remix 左侧工具栏的第三项,进入合约部署界面
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-18.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
首先将 ENVIRONMENT 选择为 Injected Web3,这样才能通过 MetaMask 钱包来发送交易。
|
||||||
|
|
||||||
|
随后点击 Deploy 部署合约,MetaMask 会弹出部署合约的交易界面
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-19.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
因为该笔交易是合约创建交易,因此我们支付的以太币为0,但仍需支付一定的 Gas 费用,可以自己设定 Gas 的价格。
|
||||||
|
|
||||||
|
合约部署成功后会收到 Chrome 的消息提示,并在 Remix 的 Deployed Contracts 中也会有显示
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-20.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
这样我们就完成了这个水龙头合约的部署。
|
||||||
|
|
||||||
|
#### 水龙头测试 ####
|
||||||
|
|
||||||
|
我们刚刚创建的水龙头中还没有以太坊,因此我们可以通过 MetaMask 向水龙头合约的地址中发送一些以太坊。水龙头合约的地址会显示在 Remix 中的,见上图 FAUCET AT 0X7A4...34219,可以直接复制。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-21.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
交易被确认后,我们的水龙头中就有了0.999726个以太币,现在我们可以通过 Remix 中合约一栏的 withdraw 按钮来提取以太币了。需要注意,这里输入的以太币个数是以 wei 为单位的。
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-22.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
点击 withdraw 后,会弹出警告框
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-23.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
这是因为目前我们还没有设置这笔交易的 Gas,不用担心,点击 Send Transaction 后,在弹出的 MetaMask 中设置即可。
|
||||||
|
|
||||||
|
交易被确认后,我们得到了刚刚提取的0.999726个以太币
|
||||||
|
|
||||||
|
<center>
|
||||||
|
<img style="border-radius: 0.3125em;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(34,36,38,.12),0 2px 10px 0 rgba(34,36,38,.08);"
|
||||||
|
src="pic/part2-15.png">
|
||||||
|
<br>
|
||||||
|
<div style="color:orange; border-bottom: 1px solid #d9d9d9;
|
||||||
|
display: inline-block;
|
||||||
|
color: #999;
|
||||||
|
padding: 2px;"></div>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
|
||||||
|
若大家没有执行成功可以重新做一次、查找其他资料或者[观看此视频](https://www.bilibili.com/video/BV1sJ411D72u?p=465)
|
||||||
|
|
||||||
|
## 三、投票合约的实现
|
||||||
|
|
||||||
|
最后是一个较复杂的投票合约作为结束,我们希望实现的功能是有一个主席,主席将赋予每个成员投票权,而成员的投票权可以委托给其他人,结束时将返回投票最多的提案。听起来很简单一个功能实现起来却较为复杂,下面我们拆分开进行讲解
|
||||||
|
|
||||||
|
<img src="pic/rating.png" style="zoom:67%;" />
|
||||||
|
|
||||||
|
# 3.1、投票合约的实现
|
||||||
|
|
||||||
|
本次教程将以一个较复杂的投票合约作为结束,我们希望实现的功能是为每个(投票)建议建立一份合约,然后作为合约的创造者-主席,主席将赋予每个成员(地址)投票权,而成员的投票权可以选择委托给其他人也可以自己投票,结束时将返回投票最多的提案。听起来很简单一个功能实现起来却较为复杂,下面我们拆分开进行讲解
|
||||||
|
|
||||||
|
注:
|
||||||
|
|
||||||
|
1. 代码可直接在Remix编辑器的已有solidity文件中找到,在contract/_Ballot.sol文件里
|
||||||
|
2. 若学习者前面部分掌握较牢固,不妨尝试直接自行阅读代码,无需阅读本节内容
|
||||||
|
|
||||||
|
|
||||||
|
首先我们定义成员类型,我们为每个投票者定义权重、是否已投票、
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
struct Voter {
|
||||||
|
uint weight; // weight is accumulated by delegation
|
||||||
|
bool voted; // if true, that person already voted
|
||||||
|
address delegate; // person delegated to
|
||||||
|
uint vote; // index of the voted proposal
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
然后我们定义提案类型,包含提案名和投票总数:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
struct Proposal {
|
||||||
|
bytes32 name; // short name (up to 32 bytes)
|
||||||
|
uint voteCount; // number of accumulated votes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
定义三个变量,主席是一个公开的地址,建立投票者与地址的映射,然后定义提案动态数组:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
address public chairperson;
|
||||||
|
mapping(address => Voter) public voters;
|
||||||
|
Proposal[] public proposals;
|
||||||
|
```
|
||||||
|
|
||||||
|
- `address public chairperson`:投票发起人,类型为 address。
|
||||||
|
- `mapping(address => Voter) public voters`:所有投票人,类型为 `address` 到 `Voter` 的映射。
|
||||||
|
- `Proposal[] public proposals`:所有提案,类型为动态大小的 `Proposal` 数组。
|
||||||
|
|
||||||
|
3 个状态变量都使用了 `public` 关键字,使得变量可以被外部访问(即通过消息调用)。事实上,编译器会自动为 `public `的变量创建同名的 `getter` 函数,供外部直接读取。
|
||||||
|
|
||||||
|
我们还需要为每个投票赋予初始权值,并将主席的权重设置为1。我们一般使用`constructor`赋初值,这与C++等语言类似:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
constructor(bytes32[] memory proposalNames) {
|
||||||
|
chairperson = msg.sender;
|
||||||
|
voters[chairperson].weight = 1;
|
||||||
|
|
||||||
|
for (uint i = 0; i < proposalNames.length; i++) {
|
||||||
|
proposals.push(Proposal({
|
||||||
|
name: proposalNames[i],
|
||||||
|
voteCount: 0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
所有提案的名称通过参数 `bytes32[] proposalNames` 传入,逐个记录到状态变量 `proposals` 中。同时用 `msg.sender` 获取当前调用消息的发送者的地址,记录为投票发起人 `chairperson`,该发起人投票权重设为 1。
|
||||||
|
|
||||||
|
接下来我们需要给每个投票者赋予权重:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function giveRightToVote(address voter) public {
|
||||||
|
require(
|
||||||
|
msg.sender == chairperson,
|
||||||
|
"Only chairperson can give right to vote."
|
||||||
|
);
|
||||||
|
require(
|
||||||
|
!voters[voter].voted,
|
||||||
|
"The voter already voted."
|
||||||
|
);
|
||||||
|
require(voters[voter].weight == 0);
|
||||||
|
voters[voter].weight = 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
该函数给 `address voter` 赋予投票权,即将 `voter` 的投票权重设为 1,存入 `voters` 状态变量。
|
||||||
|
|
||||||
|
上面这个函数只有投票发起人 `chairperson` 可以调用。这里用到了 `require((msg.sender == chairperson) && !voters[voter].voted)` 函数。如果` require` 中表达式结果为 `false`,这次调用会中止,且回滚所有状态和以太币余额的改变到调用前。但已消耗的 `Gas` 不会返还。
|
||||||
|
|
||||||
|
下面一段是整段代码的重点,其作用是委托其他人代理投票,基本思路是:
|
||||||
|
|
||||||
|
1. 使用`require`判断委托人是否已投票(若投过票再委托则重复投票),并判断被委托对象是否是自己
|
||||||
|
2. 当判断被委托人不是0地址(主席)时,被委托人代理委托人的票,【绕口警告】由于被委托人也可能委托了别人,因此这里需要一直循环直到找到最后没有委托别人的被委托人为止!
|
||||||
|
3. 委托人找到对应的被委托人,委托人已投票(避免重复投票)
|
||||||
|
4. 判断被委托人是否已投票,若投了票则将被委托人投的提案票数加上委托人的权重,若未投票则令被委托人的权重加上委托人的权重(以后投票自然相当于投两票)
|
||||||
|
|
||||||
|
注:该函数使用了 `while` 循环,这里合约编写者需要十分谨慎,防止调用者消耗过多 `Gas`,甚至出现死循环。
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function delegate(address to) public {
|
||||||
|
Voter storage sender = voters[msg.sender];
|
||||||
|
require(!sender.voted, "You already voted.");
|
||||||
|
require(to != msg.sender, "Self-delegation is disallowed.");
|
||||||
|
|
||||||
|
while (voters[to].delegate != address(0)) {
|
||||||
|
to = voters[to].delegate;
|
||||||
|
require(to != msg.sender, "Found loop in delegation.");
|
||||||
|
}
|
||||||
|
sender.voted = true;
|
||||||
|
sender.delegate = to;
|
||||||
|
Voter storage delegate_ = voters[to];
|
||||||
|
if (delegate_.voted) {
|
||||||
|
proposals[delegate_.vote].voteCount += sender.weight;
|
||||||
|
} else {
|
||||||
|
delegate_.weight += sender.weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
投票部分仅是几个简单的条件判断:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function vote(uint proposal) public {
|
||||||
|
Voter storage sender = voters[msg.sender];
|
||||||
|
require(sender.weight != 0, "Has no right to vote");
|
||||||
|
require(!sender.voted, "Already voted.");
|
||||||
|
sender.voted = true;
|
||||||
|
sender.vote = proposal;
|
||||||
|
proposals[proposal].voteCount += sender.weight;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
用 `voters[msg.sender]` 获取投票人,即此次调用的发起人。接下来检查是否是重复投票,如果不是,进行投票后相关状态变量的更新。
|
||||||
|
|
||||||
|
接下来是计算获胜提案:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function winningProposal() public view
|
||||||
|
returns (uint winningProposal_)
|
||||||
|
{
|
||||||
|
uint winningVoteCount = 0;
|
||||||
|
for (uint p = 0; p < proposals.length; p++) {
|
||||||
|
if (proposals[p].voteCount > winningVoteCount) {
|
||||||
|
winningVoteCount = proposals[p].voteCount;
|
||||||
|
winningProposal_ = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`returns (uint winningProposal)` 指定了函数的返回值类型,`constant` 表示该函数不会改变合约状态变量的值。
|
||||||
|
|
||||||
|
最后是查询获胜者名称:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
function winnerName() public view
|
||||||
|
returns (bytes32 winnerName_)
|
||||||
|
{
|
||||||
|
winnerName_ = proposals[winningProposal()].name;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这里采用内部调用 `winningProposal()` 函数的方式获得获胜提案。如果需要采用外部调用,则需要写为 `this.winningProposal()`。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**参考自:**
|
||||||
|
|
||||||
|
[尚硅谷区块链全套Go语言→GoWeb→以太坊→项目实战](https://www.bilibili.com/video/BV1sJ411D72u)
|
||||||
|
[web3.js 1.0中文手册](http://cw.hubwiz.com/card/c/web3.js-1.0/)
|
||||||
|
|
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 9.9 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 9.8 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 153 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 989 B |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 132 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 7.9 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 51 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 185 KiB |
|
@ -0,0 +1 @@
|
||||||
|
This folder contains some necessary picture
|
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 85 KiB |
|
@ -21,17 +21,24 @@
|
||||||
### Task00:熟悉规则(1天)
|
### Task00:熟悉规则(1天)
|
||||||
- 组队、修改群昵称
|
- 组队、修改群昵称
|
||||||
- 熟悉打卡规则
|
- 熟悉打卡规则
|
||||||
|
|
||||||
### Task01:热身练习(2天)
|
### Task01:热身练习(2天)
|
||||||
- 完成热身练习文件夹中的7道题目
|
- 完成热身练习文件夹中的7道题目
|
||||||
- 熟悉基本输入输出及蓝桥杯的练习系统的使用方法。
|
- 熟悉基本输入输出及蓝桥杯的练习系统的使用方法。
|
||||||
### Task02 基础练习(3天)
|
|
||||||
|
### Task02:基础练习(3天)
|
||||||
- 完成基础练习文件夹中的7道题目
|
- 完成基础练习文件夹中的7道题目
|
||||||
### Task03 基础练习2(3天)
|
|
||||||
|
### Task03:基础练习2(3天)
|
||||||
- 完成基础练习2文件夹中的8道题目
|
- 完成基础练习2文件夹中的8道题目
|
||||||
### Task4 真题练习(3天)
|
|
||||||
|
### Task04:真题练习(3天)
|
||||||
- 完成真题练习文件夹中的10道题目
|
- 完成真题练习文件夹中的10道题目
|
||||||
### Task5 真题练习2(3天)
|
|
||||||
|
### Task05:真题练习2(3天)
|
||||||
- 完成真题练习文件夹中的8道题目
|
- 完成真题练习文件夹中的8道题目
|
||||||
|
|
||||||
|
|
||||||
## 开源贡献者
|
## 开源贡献者
|
||||||
|
|
||||||
韩绘锦:华北电力大学
|
韩绘锦:华北电力大学
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
|
||||||
|
|
||||||
|
# 青少年编程(Scratch)电子学会一级
|
||||||
|
|
||||||
|
开源学习内容:https://github.com/datawhalechina/team-learning-program/tree/master/Scratch
|
||||||
|
|
||||||
|
## 基本信息
|
||||||
|
|
||||||
|
- 贡献人员:马燕鹏、王思齐
|
||||||
|
- 学习周期:12天,每天平均花费时间1小时,根据个人学习接受能力强弱有所浮动。
|
||||||
|
- 学习形式:模拟测试题
|
||||||
|
- 人群定位:Scratch初学者
|
||||||
|
- 先修内容:无
|
||||||
|
- 难度系数:低
|
||||||
|
|
||||||
|
## 学习目标
|
||||||
|
|
||||||
|
由于测试题目为往年电子学会Scratch一级考试的真题,所以通过做题来掌握Scratch一级考试的知识点,为通过考试做准备。
|
||||||
|
|
||||||
|
**一级考试大纲与说明**
|
||||||
|
|
||||||
|
(一)考核目标
|
||||||
|
|
||||||
|
学生对编程软件的界面认识和基本操作,初步能够导入角色和设置背景,并通过对角色的不同操作以及加入声音,形成一个具有简单顺序结构代码的作品,同时针对参加1级考试的学生将进行简单的逻辑推理能力的考查。
|
||||||
|
|
||||||
|
(二)能力目标
|
||||||
|
|
||||||
|
通过本级考试的学生,对软件认识良好,会进行软件的基本操作,能完成基本作品。
|
||||||
|
|
||||||
|
(三)考试标准
|
||||||
|
|
||||||
|
1、初步学会使用编程工具,理解编程工具中的核心概念。
|
||||||
|
|
||||||
|
- 1)理解编程环境界面中功能区的分布与作用;
|
||||||
|
- 2)能够完成拖拽程序模块到程序区的操作并进行正确的连接;
|
||||||
|
- 3)能够通过舞台区按钮完成运行与停止程序的操作;
|
||||||
|
- 4)会使用角色的移动、旋转指令模块;
|
||||||
|
- 5)能为作品添加背景音乐,并设置声音的播放代码;
|
||||||
|
- 6)能够绘制背景并对背景进行切换;
|
||||||
|
- 7)能够打开计算机上已保存的项目和保存新制作的项目。
|
||||||
|
|
||||||
|
2、按照规定的功能编写出完整的顺序结构程序。
|
||||||
|
|
||||||
|
- 1)掌握顺序结构流程图的画法;
|
||||||
|
- 2)理解参数的概念,能够调整指令模块中的参数;
|
||||||
|
- 3)能够完成一个顺序结构的程序;
|
||||||
|
- 4)程序中包含播放一段音频和切换背景;
|
||||||
|
- 5)程序中包含切换角色的造型,角色移动和旋转;
|
||||||
|
- 6)按指定的要求保存作品。
|
||||||
|
|
||||||
|
(四)知识块
|
||||||
|
|
||||||
|
- [x] 1. 熟悉编程软件:舞台区,角色区,模块区,脚本区,造型标签,声音标签,背景标签,新建和保存作品,语言的选择,从本地打开软件,程序的运行和停止
|
||||||
|
- [x] 2. 角色的导入:库导入,绘制,本地导入等方式,大小设置,顺序结构流程图
|
||||||
|
- [x] 3. 背景的认识:选取合适的背景,背景和角色的区别,背景的切换
|
||||||
|
- [x] 4. 角色的操作:移动,旋转,造型切换
|
||||||
|
- [x] 5. 声音的导入:导入声音并设置为背景音乐,设定声音音效,设定声音音量,设定声音的播放和停止
|
||||||
|
- [x] 6. 逻辑推理,编程数学:逻辑推理,形象思维(图形推理)
|
||||||
|
|
||||||
|
|
||||||
|
(五)题型配比及分值
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 任务安排
|
||||||
|
|
||||||
|
### Task00:熟悉规则【1天】
|
||||||
|
|
||||||
|
- 组队、修改群昵称。
|
||||||
|
- 熟悉打卡规则。
|
||||||
|
|
||||||
|
### Task01:熟悉Scratch【2天】
|
||||||
|
- 熟悉Scratch考级(一级)的考试标准
|
||||||
|
- 熟悉Scratch考级(一级)的知识点
|
||||||
|
|
||||||
|
|
||||||
|
### Task02:模拟题(一)【2天】
|
||||||
|
- 课堂派:判断题15个,单选题20个
|
||||||
|
- 写博客:编程题(01 森林的一天、02 舞者凯希)
|
||||||
|
|
||||||
|
|
||||||
|
### Task03:模拟题(二)【2天】
|
||||||
|
- 课堂派:判断题15个,单选题20个
|
||||||
|
- 写博客:编程题(03 小狗散步、04 猫捉老鼠)
|
||||||
|
|
||||||
|
|
||||||
|
### Task04:模拟题(三)【2天】
|
||||||
|
- 课堂派:判断题15个,单选题20个
|
||||||
|
- 写博客:编程题(05 城堡漫步、06 火箭发射)
|
||||||
|
|
||||||
|
### Task05:模拟题(四)【2天】
|
||||||
|
- 课堂派:判断题15个,单选题20个
|
||||||
|
- 写博客:编程题(07 飞向太空、08 小狗长大记)
|
||||||
|
|
||||||
|
|
||||||
|
### Task06:模拟题(五)【2天】
|
||||||
|
- 课堂派:判断题15个,单选题20个
|
||||||
|
- 写博客:编程题(09 运动起来、10 动物园之旅)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
# 参考答案
|
||||||
|
|
||||||
|
- [01 森林的一天](https://mp.weixin.qq.com/s/k-_r7xHGVW335e1FyJKIhg)
|
||||||
|
- [02 舞者凯希](https://mp.weixin.qq.com/s/km6rJbI9Ih0heThkWZF27g)
|
||||||
|
- [03 小狗散步](https://mp.weixin.qq.com/s/LelNrrmRTIUocde-x0kVzQ)
|
||||||
|
- [04 猫捉老鼠](https://mp.weixin.qq.com/s/jPMGhincv_nnk4VOJ1XyzQ)
|
||||||
|
- [05 城堡漫步](https://mp.weixin.qq.com/s/S--pZGgJvv5LHU1mX3p15w)
|
||||||
|
- [06 火箭发射](https://mp.weixin.qq.com/s/M_agy8qJCmBIZ6ui6sK3Ww)
|
||||||
|
- [07 飞向太空](https://mp.weixin.qq.com/s/9eN4MRVCQcV4z7otV7Omfg)
|
||||||
|
- [08 小狗长大记](https://mp.weixin.qq.com/s/Gh5jCN2fu6-clPcYevuWlw)
|
||||||
|
- [09 运动起来](https://mp.weixin.qq.com/s/amK1Rxi_uqoyJFy-nDB9ug)
|
||||||
|
- [10 动物园之旅](https://mp.weixin.qq.com/s/Ff_GB4AnBtrEHwVTCB45qw)
|
|
@ -0,0 +1,76 @@
|
||||||
|
通过前面的学习,相信你已经能够参考【图图】的说明来进行绘图创造了,那么从现在开始,我们将正式开始各种趣味图形绘画。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
🔑 在【冲鸭】之前,请记住一个【锦囊妙计】,后面的画图过程中会涉及到各种各样的功能,有时我们可能会忘记指令怎么写,所以在每次训练的开头会为大家附上一个【图图】功能查询表,忘了就去看看吧。
|
||||||
|
|
||||||
|
|向前走<br>|t.forward(100)<br>|
|
||||||
|
|:----|:----|:----|:----|
|
||||||
|
|向后走<br>|t.back(100)<br>|
|
||||||
|
|向左转<br>|t.left(90)<br>|
|
||||||
|
|向右转<br>|t.right(90)<br>|
|
||||||
|
|
||||||
|
|
||||||
|
万事俱备,只欠冲锋,好啦,正式开始我们图形挑战之旅吧!!!
|
||||||
|
|
||||||
|
# 🚀挑战1:
|
||||||
|
|
||||||
|
这是一道送分题,别说你不会,赶紧的~
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,从左向右画出一条长度为100的直线吧。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战2:
|
||||||
|
|
||||||
|
送分题又来了!
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,从左向右画出一个度数为90的角吧。
|
||||||
|
|
||||||
|
(边的长度自己决定就好啦,可以也设置成100)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 📺示例1:
|
||||||
|
|
||||||
|
下面,我们将一起来画几个简单的图形,第一个图形是一个三条边长度都为100的三角形,想一想该怎么画出来,如果想不出来,可以试试在草稿纸上画一画,要注意每次【图图】画完一条线后旋转的角度哦。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
下面是三角形的答案,一定要自己尝试后再来看哦:
|
||||||
|
|
||||||
|
**讲解**:三角形一共有三条边,【图图】向前100后,完成第一条边的绘制,然后需要向右旋转120度,接着又向前100,完成第二条边后再向右旋转120度,向前100,完成最后一条边的绘制
|
||||||
|
|
||||||
|
```plain
|
||||||
|
import turtle as t
|
||||||
|
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
#连续画三条边
|
||||||
|
t.forward(100)
|
||||||
|
t.right(120)
|
||||||
|
t.forward(100)
|
||||||
|
t.right(120)
|
||||||
|
t.forward(100)
|
||||||
|
t.right(120)
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
# 🚀挑战3:
|
||||||
|
|
||||||
|
通过学习后你已经能画出三角形了,那么,再用同样的方式来试试正方形吧
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战4:
|
||||||
|
|
||||||
|
看来你已经能绘制简单图形了,那么现在来尝试一下复杂一点的“十字”图吧,方法和前面的图形差不多哦,但是注意每转动一次后移动的距离:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
真棒,这么快就完成了第一天的打卡内容了,是不是觉得还挺简单,哈哈,别着急,后面会逐渐提高难度,小心哦~
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
# 🔑技能包:
|
||||||
|
|
||||||
|
别忘了昨天学习的技能哦:
|
||||||
|
|
||||||
|
|向前走<br>|t.forward(100)<br>|
|
||||||
|
|:----|:----|:----|:----|
|
||||||
|
|向后走<br>|t.back(100)<br>|
|
||||||
|
|向左转<br>|t.left(90)<br>|
|
||||||
|
|向右转<br>|t.right(90)<br>|
|
||||||
|
|
||||||
|
|
||||||
|
——————————————————————————————————————————————
|
||||||
|
|
||||||
|
今天是第二天,主题是【你妈妈叫你回家啦】
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
老师,你在开玩笑?
|
||||||
|
|
||||||
|
哈哈,今天的指令确实和回家有关系,叫做【回家指令】,【回家指令】这是个啥?名字起得这么有创意(无聊)吗?难道要让图图回家?
|
||||||
|
|
||||||
|
咳咳咳,回答正确,就是一个让图图回家的指令。
|
||||||
|
|
||||||
|
# 1、回家指令介绍
|
||||||
|
|
||||||
|
## (1)简介
|
||||||
|
|
||||||
|
为什么叫做回家指令呢?这是因为他是通过英文名home()翻译过来的,通过这一条指令可以帮助图图回到起点,也就是说,即使你通过指令让图图跑到了千里之外,我也能用这个指令让他回来,是不是一个很6的指令:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## (2)格式
|
||||||
|
|
||||||
|
那么,这么6的指令可以用来做什么呢?我来举个例子🌰吧:
|
||||||
|
|
||||||
|
### 📺示例1:
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,画出一个直角三角形吧:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
只知道两个直角边分别是80和60,但是另外一条斜边长度不知道,这个时候怎么办呢?
|
||||||
|
|
||||||
|
**路人甲:**不知道哎
|
||||||
|
|
||||||
|
**路人乙:**我是初中生,我学过【勾股定理】可以算
|
||||||
|
|
||||||
|
**隔壁小明:**难道是用home指令
|
||||||
|
|
||||||
|
是的,隔壁小明真聪明,通过home指令就能解决这个问题,最后一条边其实就是图图返回起点走的路:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
程序如下:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
import turtle as t
|
||||||
|
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(80)
|
||||||
|
t.right(90)
|
||||||
|
t.forward(60)
|
||||||
|
t.home() # 返回起点
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
这样,通过home指令,让图图自动返回到起点,于是就画出了最后一条边,是不是非常简单,来试试吧!
|
||||||
|
|
||||||
|
# 🚀挑战1:
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,画出一个钝角三角形吧。
|
||||||
|
|
||||||
|
(备注:钝角三角形就是有一个角度数大于90的的三角形)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战2:
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,画出对应的图形吧。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战3:
|
||||||
|
|
||||||
|
有点厉害啊,已经解决了两个挑战了,那么咱们增加下难度吧!
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,画出有一条对角线的正方形吧。
|
||||||
|
|
||||||
|
备注1:对角线就是两个相对的角顶点的连接线
|
||||||
|
|
||||||
|
备注2:图图回到起点后会自动旋转到水平向右
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战4:
|
||||||
|
|
||||||
|
能做到这里看来你已经掌握home指令的诀窍了,既然这样,那就来完成最后的挑战吧。
|
||||||
|
|
||||||
|
题目描述:请参考下面图片中的内容,画出一个直角梯形。
|
||||||
|
|
||||||
|
备注:上底就是较短的一条边,下底就是较长的一条边
|
||||||
|
|
||||||
|
提示:上底、下底、高的长度需要你自己设定,只要能画出类似的直角梯形就可以啦
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
今天,正儿八经给大家普及下新知识。
|
||||||
|
|
||||||
|
⛽️知识加油站——循环
|
||||||
|
|
||||||
|
循环在我们生活中非常普遍,比如我们乘坐旋转木马,旋转木马会一直循环,又比如说月球会一直不停绕着太阳旋转:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
而在编程中循环也是一个非常重要知识,它可以让程序一直重复执行,比如下面这个程序,虽然只输入了一句话,但是通过设置循环次数为5,就能打印5次“我喜欢看海贼王”:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
for i in range(5):
|
||||||
|
print('我喜欢看海贼王')
|
||||||
|
```
|
||||||
|
## 循环的格式
|
||||||
|
|
||||||
|
想使用循环非常简单,只需要按照下面的格式设置就可以了:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
# 括号里填写你想循环的次数,写上冒号后,换行,空4个空格后就可以写循环的内容
|
||||||
|
for i in range(次数):
|
||||||
|
循环的内容
|
||||||
|
```
|
||||||
|
📺练习1:
|
||||||
|
|
||||||
|
使用循环画一个正方形。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
一起来分析下,要画一个正方形,需要下面这些步骤:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
向前100
|
||||||
|
旋转90度
|
||||||
|
向前100
|
||||||
|
旋转90度
|
||||||
|
向前100
|
||||||
|
旋转90度
|
||||||
|
向前100
|
||||||
|
旋转90度
|
||||||
|
```
|
||||||
|
可以看成,我们要让计算机重复执行4次下面的内容:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
下面的内容要重复执行4次:
|
||||||
|
向前100
|
||||||
|
向右旋转90度
|
||||||
|
```
|
||||||
|
这样就就可以写成循环了:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(100)
|
||||||
|
t.right(90)
|
||||||
|
```
|
||||||
|
完整代码:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
import turtle as t
|
||||||
|
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
#连续画四条边
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(100)
|
||||||
|
t.right(90)
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
🚀挑战1:
|
||||||
|
|
||||||
|
题目描述:通过循环画出一个边长为100的等边三角形
|
||||||
|
|
||||||
|
备注:等边三角形就是三条边的长度相等、三个角角度都为60度的三角形
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
🚀挑战2:
|
||||||
|
|
||||||
|
题目描述:通过循环画出一个正六边形
|
||||||
|
|
||||||
|
备注:正六边形每个角的度数(大小)都一样,6个角总共720度
|
||||||
|
|
||||||
|
提示:需要先计算正六边形的每个角大小,再得出图图每次需要转多少度,这是小学三年级除法的难度,你敢说不会?ㄟ(▔︵▔ㄟ)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
🚀挑战3:
|
||||||
|
|
||||||
|
能完成上面两道题,说明你还是很厉害的,那么咱们就提高一点难度吧,这道题你还能做对,那可就非常厉害了。
|
||||||
|
|
||||||
|
题目描述:使用循环,画出一个五角星:(可以像上面那样,先写出文字步骤来分析一下哦~)
|
||||||
|
|
||||||
|
👽拓展:如果画好了,还可以给五角星加上颜色
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
🚀挑战4:
|
||||||
|
|
||||||
|
光阴似箭,日月如梭,一转眼,你,竟然,做到了最后一道题,你,这么,厉害,爸妈知道吗?如果不知道就赶紧告诉他们吧。
|
||||||
|
|
||||||
|
下面是第三天的终极挑战:画一个圆
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
提示:画圆其实很简单,虽然你看着是一个圆,但其实,他可以看成是很多短短的边组成的,那么怎么来画圆呢?你可以看一看下面这个视频:
|
||||||
|
|
||||||
|
[https://baike.baidu.com/item/%E5%89%B2%E5%9C%86%E6%9C%AF/595781?secondId=25702197](https://baike.baidu.com/item/%E5%89%B2%E5%9C%86%E6%9C%AF/595781?secondId=25702197)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
看完视频是不是发现啦,古代人通过在圆内画多边形来近似测量圆一周的长度,多边形边越多,形状上就越接近圆,那么基于这个方法,我们可以用图图来帮我们画一个圆,怎么来画呢,我们知道圆有360°,也就是说,如果要画圆,【图图】需要围绕中心走360次,每一次走一步,每走一步向右转1度,画出一个有360边的多边形就可以了。
|
||||||
|
|
||||||
|
下面就是画圆的关键,如果你不能理解,那就记住这个有趣的画圆公式吧:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
import turtle as t
|
||||||
|
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
#请删除这一行,然后写上循环360次的程序吧
|
||||||
|
t.forward(1) #向前走一步
|
||||||
|
t.right(1) #向右转一度
|
||||||
|
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
如果你已经画好了圆,再试一试修改forward里面的数字,看看会发生什么变化~
|
||||||
|
|
||||||
|
#
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
A:老师,你是在欺负我英语不好吗?整个英文标题
|
||||||
|
|
||||||
|
老师:对啊,你怎么知道我在欺负你
|
||||||
|
|
||||||
|
A:... ...
|
||||||
|
|
||||||
|
# 1、抬笔与落笔
|
||||||
|
|
||||||
|
## (1)简介
|
||||||
|
|
||||||
|
今天我们一起来学习一个新技能【抬笔】与【落笔】。
|
||||||
|
|
||||||
|
前面我们画了这么多图形,但是大家有没有发现,我们从来没有抬过笔,这是一件非常不科学的事,就好比,你笔尖从没离开过画纸,但却写下了下面这四个字(不能写连笔):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
所以,在常规的绘画和写字中,我们其实是需要【抬笔】和【落笔】这两个动作的,同样的,图图也有这两个指令:
|
||||||
|
|
||||||
|
## (2)格式
|
||||||
|
|
||||||
|
```plain
|
||||||
|
t.up() # 抬笔
|
||||||
|
t.down() # 落笔
|
||||||
|
```
|
||||||
|
把这两句放到完整的程序中,先前进100步,然后抬笔,前进50步,然后落笔,然后再前进100步,
|
||||||
|
|
||||||
|
想一想可能画出的图形是什么样子,然后再运行一下这段代码看一看吧:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(100)
|
||||||
|
t.up() #抬笔
|
||||||
|
t.forward(50)
|
||||||
|
t.down() #落笔
|
||||||
|
t.forward(100)
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
# 🚀挑战1:
|
||||||
|
|
||||||
|
题目描述:画出一条虚线
|
||||||
|
|
||||||
|
(1)实线每一段长为10
|
||||||
|
|
||||||
|
(2)间断距离为5
|
||||||
|
|
||||||
|
提示:使用循环会更方便哦
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战2:
|
||||||
|
|
||||||
|
题目描述:绘制8条平行线
|
||||||
|
|
||||||
|
(1)每条线的长度为100
|
||||||
|
|
||||||
|
(2)相邻两条线同一端之间的距离为20
|
||||||
|
|
||||||
|
(备注:平行线倾斜程度自己设定就好,可以用文字写出走的步骤,看看循环的部分在哪里)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战3:
|
||||||
|
|
||||||
|
不知不觉,又来到了最后一题,哦不对,是倒数第二题,这一题和上面一题难度差不多,耐心一点就能画出来。
|
||||||
|
|
||||||
|
题目描述:请画出四个平行的小旗子
|
||||||
|
|
||||||
|
(1)旗子高40
|
||||||
|
|
||||||
|
(2)旗面宽
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战4:
|
||||||
|
|
||||||
|
不知不觉,终于来到了最后一题,你还记得之前画过的的圆吗?这次有了抬笔功能的配合,我们就可以画出一个很常见的汽车标志啦:
|
||||||
|
|
||||||
|
(1)两个圆中心之间的距离是85
|
||||||
|
|
||||||
|
(2)想一想每画完一个圆后【图图】要向前移动多少呢?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
画一个圆的关键步骤参考:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
for i in range(360):
|
||||||
|
t.forward(1)
|
||||||
|
t.right(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
**老师**:通过两天的训练,相信大家对于循环已经掌握的非常好了,今天我们要学习的是【循环中的循环】
|
||||||
|
|
||||||
|
**A**:【循环中的循环】?老师,你是在传授我武林秘籍吗?
|
||||||
|
|
||||||
|
**老师**:你是猴子派来的吗?总想着武林秘籍
|
||||||
|
|
||||||
|
**A**:那什么是【循环中的循环】啊?
|
||||||
|
|
||||||
|
**老师**:听我细细道来
|
||||||
|
|
||||||
|
# 1、循环中的循环
|
||||||
|
|
||||||
|
循环中的循环是编程中常用的一种程序的结构,他也被称为【循环嵌套】,也就是在一个大循环里套着小循环,听起来有点复杂,但其实非常的简单,一起来学习一下吧:
|
||||||
|
|
||||||
|
在学习【循环嵌套】前我们先来练习一道题,这样有助于我们理解【循环嵌套】的含义。
|
||||||
|
|
||||||
|
# 🚀挑战1:
|
||||||
|
|
||||||
|
题目描述:参考下面图示,借助循环,画出四个并排在一起的正方形
|
||||||
|
|
||||||
|
(1)正方形的边长是40
|
||||||
|
|
||||||
|
(2)两个正方形之间的距离是20
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
⬇️⬇️⬇️ 做完后再看,实在做不出来也可以看,不到万不得已还是别看 ⬇️⬇️⬇️
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**解析**:大家说如果要画四个正方形,需要重复画几个一样的正方形?
|
||||||
|
|
||||||
|
4个对吧,那如果用图图来画一个正方形需要重复几次【向前走100向右转90度】呢
|
||||||
|
|
||||||
|
也是4次,所以我们来看看,如果要通过图图来画出它需要怎么做:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
#画第一个正方形
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
#画完后要向前移动到到第二个正方形的顶点,需要向前移动60步,移动的时候是抬笔的状态
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
```
|
||||||
|

|
||||||
|
|
||||||
|
如果还要画三次应该怎么办呢?哈哈,对的,继续用同样的语句三遍就行了
|
||||||
|
|
||||||
|
```plain
|
||||||
|
#画第一个正方形
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
#画第二个正方形
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
#画第三个正方形
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
#画第四个正方形
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
```
|
||||||
|
这个时候你发现了什么,是不是下面段落连续出现了4次,我们学习循环时说过,当重复不断出现时可以使用循环,这里仍然可以的,将这个循环的逻辑写成中文就是:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
下面的内容要循环4次:
|
||||||
|
for i in range(4):
|
||||||
|
t.forward(40)
|
||||||
|
t.right(90)
|
||||||
|
t.up()
|
||||||
|
t.forward(60)
|
||||||
|
t.down()
|
||||||
|
```
|
||||||
|
把中文也变成程序,和之前循环是一样的方式,加上for i in range(),但是一定要记得,后面这个整体前面有四个空格,还记得之前讲循环的时候说过吧,加四个空格是为了告诉程序,下面的内容归我管,要循环4次,可不要忘了哦:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
是不是觉得好像听懂了,又好像有一点疑惑,怎么办呢?很简单,再来做两道题自然就懂了:
|
||||||
|
|
||||||
|
# 🚀挑战2:
|
||||||
|
|
||||||
|
题目描述:参考下面的图示,借助循环,画出四个并排在一起的三角形
|
||||||
|
|
||||||
|
(提示:➡️可以不用循环嵌套,但是呢,使用循环嵌套又会更简单,算了,用不用看你吧
|
||||||
|
|
||||||
|
➡️如果使用嵌套,你可以参考上面画4个正方形的方法哦,先用中文分析循环次数,在写成程序)
|
||||||
|
|
||||||
|
(1)等边三角形的边长是40
|
||||||
|
|
||||||
|
(2)两个三角形之间的距离是20
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战3:
|
||||||
|
|
||||||
|
不得了不得了,已经做到第3题了,少年我看你天赋异禀啊!
|
||||||
|
|
||||||
|
题目描述:参考下面的图示,借助循环,画出一个风车
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
👽拓展:在风车的基础上进行创作吧,看看你的风车能比下面的好看吗?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 🚀挑战4(选做):
|
||||||
|
|
||||||
|
这道题选做哦,如果你觉得前面的已经难不倒你了,那就再来试试这一道题吧!
|
||||||
|
|
||||||
|
题目描述:参考下面的图示,借助循环,画出一个风车
|
||||||
|
|
||||||
|
(1)菱形的两个角分别是60°和90°
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
# 1、坐标
|
||||||
|
|
||||||
|
地瓜:土豆土豆,我是地瓜,我现在已经被敌人包围了,请求支援
|
||||||
|
|
||||||
|
土豆:地瓜地瓜,我是土豆,请立刻告诉我你的坐标
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
什么是坐标呢?读完上面这个对话你肯定有点感觉了吧,坐标就是一个物体在空间中的位置,就比如上面飞机在天空中的位置,你需要告诉友军他的位置,友军才能去营救。
|
||||||
|
|
||||||
|
又比如说下面这个图,就是一个平面空间,我们在上面画了两根轴来帮助大家记录坐标,这两根轴就叫做坐标轴,两根轴交叉的就是坐标为(0,0)的地方,那么怎么来读取坐标呢?其实非常简单,看【图图】对应坐标轴上的数字就可以了。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
就比如这样,图图的位置分别对应横轴的1,对应纵轴的3,所以他的坐标是(1,3):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 🚀挑战1:
|
||||||
|
|
||||||
|
题目描述:请你写出图图在在下图中的位置:
|
||||||
|
|
||||||
|
(对应横轴和纵轴来看哦~)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 2、goto功能
|
||||||
|
|
||||||
|
那么,怎么才能帮图图到指定的坐标呢?这就要使用到一个有趣的功能goto,它可以帮助我们去到指定的坐标位置,但是在使用它之前需要记住两个重要秘诀:
|
||||||
|
|
||||||
|
### 秘诀1:如果使用goto功能时没有抬笔,那么图图会向着指定的位置画过去
|
||||||
|
|
||||||
|
这个秘诀说的什么意思呢?我们一起来看看:
|
||||||
|
|
||||||
|
(1)goto语句的用法
|
||||||
|
|
||||||
|
```plain
|
||||||
|
t.goto(x, y) # x,y就是对应的坐标
|
||||||
|
```
|
||||||
|
如果我使用下面这一段语句,你猜猜会发生什么:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
t.goto(80,80)
|
||||||
|
```
|
||||||
|
出现了下面的图像,原因是因为我们让图图去坐标是(80,80)的位置,所以图图就直接移动过去了,不过使用goto语句,图图的朝向是不会改变的哦:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
那么,如果我想让图图**瞬间移动**过去要怎么办呢,这时候秘诀2了
|
||||||
|
|
||||||
|
### 秘诀2:要让图图瞬间移动到某个坐标,而不留下痕迹,就要使用抬笔和落笔功能
|
||||||
|
|
||||||
|
```plain
|
||||||
|
t.up()
|
||||||
|
t.goto(x,y) # 比如t.goto(80,80)
|
||||||
|
t.down
|
||||||
|
```
|
||||||
|
## 🚀挑战2:
|
||||||
|
|
||||||
|
请你将图图**瞬间移动**到坐标为(100,100)的地方,如下图:
|
||||||
|
|
||||||
|
(提示:下图中的红色圆圈只是示意图图的一开始的位置,忽略就可以)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# 3、乾坤大挪移
|
||||||
|
|
||||||
|
有小伙伴儿会问:“乾坤大挪移是什么?”,很简单,就是让图图进行各种移动,各种来考你的题目,hiahiahiahia~
|
||||||
|
|
||||||
|
## 🚀挑战3:
|
||||||
|
|
||||||
|
还记得小时候用过的钉板吗?我们通过钉板制作各种各样的图形,比如下面这种。
|
||||||
|
|
||||||
|
那么现在请使用goto语句,参考下面的坐标画出对应的图形:
|
||||||
|
|
||||||
|
(备注:如果画出来的图形很小,可以把坐标都放大10倍哦,比如把(1,1)改成(10,10),把(5,1)改成(50,10))
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 🚀挑战4:
|
||||||
|
|
||||||
|
看来你已经简单了解了坐标和使用坐标的方式,那么,再来考考你吧。
|
||||||
|
|
||||||
|
题目描述:请先将图图移动到坐标为(100,100)的位置,然后画出一个边长为50的五角星
|
||||||
|
|
||||||
|
(备注:五角星的每个角都是36°,其他角的参数参考下图2)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
恭喜你已经完成了前六天的任务,在前六天中你学习到了编程中的循环,画图模块中的方向功能、抬笔功能、颜色填充功能、回家功能,并且熟悉了常见的图形、多边形,能够坚持到第七天,你一定非常厉害,既然这样,那么就请准备好,迎接最后的挑战吧!
|
||||||
|
|
||||||
|
最后的挑战
|
||||||
|
|
||||||
|
世界上有非常多的国家,咱们中国只是其中一个,除了中国,还有很多国家分布在世界各地,他们有的领土大,有的领土小,有的很有名气,有的可能你还没听说过,但是无论哪个国家,他们都有一个象征自己国家的物品,你知道是什么吗?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
对啦,这个物品就是国旗,即使是新成立的国家,也需要设计属于自己的国旗,而每一种国旗都有自己不同的含义,比如咱们中国的国旗,这可不是简单在上面涂个颜色、画几个星星,星星的角度和数量都是有讲究的。在中国国旗上,红色象征革命,旗上的五颗五角星及其相互关系象征共产党领导下的革命人民大团结。五角星用黄色是为了在红地上显出光明,四颗小五角星各有一尖正对着大星的中心点,表示围绕着一个中心而团结。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
当然啦其他国家的国旗也有属于自己国家、自己历史的意义。比如冰岛,这可不是一个岛,他也是一个国家,他的国旗是这样的:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
采用了红色、蓝色和白色,象征冰岛是屹立于汪洋大海的一个炽热而又被冰雪覆盖的美丽岛国,十字图案源自丹麦国旗图案,表示冰岛历史上与挪威、丹麦的关系。
|
||||||
|
|
||||||
|
国旗虽上面的图案虽然看起来简单,但是要画出来可不容易,今天我们就为大家准备了下面2种国旗,选择1种你觉得对自己有一定挑战的国旗,并通过图图将他画出来吧。
|
||||||
|
|
||||||
|
## ⛽️加油站—图层
|
||||||
|
|
||||||
|
在开始画图前我们需要再学习一个重要的知识——图层,认识了图层,你使用图图就能起到事半功倍的作用。
|
||||||
|
|
||||||
|
什么是图层呢?其实非常简单,你可以理解成一张图是由多层图叠在一起的,比如下面的【瑞士国旗】:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
它可以由两层组合在一起,最下面一层是红色,上面一层是白色的十字:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
所以,如果要使用图图,就可以先画最下面一层红色背景,再画白色的十字。
|
||||||
|
|
||||||
|
### 📺练习1:
|
||||||
|
|
||||||
|
说说下面的【加拿大国旗】可以由几层组成,分别是那几层呢?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
答案:可以有三层组成:最下面一层是红色长方形,中间层是白色长方形,最上面一层是红色枫叶
|
||||||
|
|
||||||
|
## 🚀挑战开始!
|
||||||
|
|
||||||
|
### (1)冰岛国旗 难度系数🌟🌟🌟
|
||||||
|
|
||||||
|
参考:
|
||||||
|
|
||||||
|
设置成蓝色:t.color('#0048E0')
|
||||||
|
|
||||||
|
设置成红色:t.color('#FF0F00')
|
||||||
|
|
||||||
|
设置成白色:t.color('white')
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考坐标图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### (2)朝鲜国旗 难度系数🌟🌟🌟🌟
|
||||||
|
|
||||||
|
参考:
|
||||||
|
|
||||||
|
(1)
|
||||||
|
|
||||||
|
设置成蓝色:t.color('#0048E0')
|
||||||
|
|
||||||
|
设置成红色:t.color('#FF0F00')
|
||||||
|
|
||||||
|
设置成白色:t.color('white')
|
||||||
|
|
||||||
|
(2)
|
||||||
|
|
||||||
|
五角星的小边长度为23
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
参考坐标图:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
#
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
本篇主要介绍 Python 的安装与环境配置方法。Python作为一门通用型的编程语言,可以通过很多方法完成安装,同时,也可根据实际需求搭建不同类型的开发环境。由于 Jupyter 是最通用的开发环境,同时,Jupyter 本身也是 Notebook 形式的开发环境,非常适合初学者上手使用。因此,本次内容将主要采用 Jupyter Notebook/Jupyter Lab 来进行演示,本篇也将详细介绍如何通过通用科学计算平台 Anaconda 来进行 Python和Jupyter 的安装。
|
||||||
|
|
||||||
|
|
||||||
|
# 1 Anaconda下载与安装
|
||||||
|
|
||||||
|
Anaconda 是一个工具包,将 Python 和许多与科学计算相关的库捆绑在一起,形成了一个方便的科学计算环境,你安装了 Ananconda 就相当于安装了 Python 外加这些库,省去了自己下载和安装各种库的麻烦,方便初学者专注于学习 Python。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
此处,我们采用 Anaconda 进行 Python 安装和开发环境搭建。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.1 下载Anaconda
|
||||||
|
|
||||||
|
访问Anaconda官网(https://www.anaconda.com),在下拉菜单中的 Products 里选择 Individual Edition,个人版,同时也是免费版。此版本中不涉及付费内容,可供个人用户使用。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
进入页面后,点击Download,会自动跳转到操作系统选择的界面。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
此时,可根据自身操作系统进行选择和下载。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
当然,windows 64的用户也可直接通过下述网盘连接进行下载,该版本为 Anaconda3-2020.11-Windows-x86_64。
|
||||||
|
|
||||||
|
- 链接:https://pan.baidu.com/s/1IEasB0epWpPRhgYdgSCHaA
|
||||||
|
- 提取码:i6zj
|
||||||
|
|
||||||
|
## 1.2 安装Anaconda
|
||||||
|
|
||||||
|
下载完成后,即可开始安装。双击安装文件,进入欢迎界面,点击 Next。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
点击同意,进入到下一步。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
选择软件使用权限,是指针对当前登录用户还是所有用户,二者都行,无特殊要求。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
选择安装位置,完成安装。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如果出现此页面,需要勾选配置环境变量选项。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
无需安装VS Code,直接跳过即可。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
安装完成后,在开始菜单栏,或者软件安装位置,找到 Anaconda Navigator 并打开。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
进入到如下界面。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
我们能看到 Anaconda 中集成了非常多数据科学计算相关的功能,并且,在安装过程中,也完成了 Python 的安装和环境变量的设置,以及 Jupyter和PyCharm 的安装。其中 Jupyter 是本次学习将用到的代码编辑工具,而 PyCharm 则是一款集成开发环境(IDE),本次学习并不涉及。
|
||||||
|
|
||||||
|
|
||||||
|
## 1.3 启动Jupyter
|
||||||
|
|
||||||
|
我们能够看到,在 Anaconda 中有两个 Jupyter 组件,一个是**Notebook**,一个是**JupyterLab**。其中,Lab 是 Notebook 的升级版,用户交互界面更加友好,并且拥有许多额外辅助功能,例如代码框分屏、文件管理系统等,但相比 Notebook,Lab 并不支持第三方插件,因此如果是想使用 Jupyter 丰富的插件,则只能选择 Notebook。不过二者在实际编程的功能使用上没有区别,本次学习推荐使用 JupyterLab。
|
||||||
|
|
||||||
|
点击 JupyterLab,启动相关服务,系统会自动打开浏览器并进入到 JupyterLab 界面。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
能够成功弹出浏览器窗口,则说明安装成功。如果浏览器关闭,再次点击 Anaconda 中 Jupyter Lab 组件中的 Launch 即可再次打开 Jupyter 界面。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
或者在浏览器里直接输入 http://localhost:8890/lab。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
# 2 Jupyter基本操作
|
||||||
|
|
||||||
|
接下来,简单介绍 Jupyter 的基本操作。
|
||||||
|
|
||||||
|
## 2.1 简单代码编写尝试
|
||||||
|
|
||||||
|
在 JupyterLab 主界面中,左边是文件目录,右边是编程界面,首次登陆时,点击 Python3 即可创建一个新的编程文件。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
如下所示:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
同时,在左侧文件目录,也会出现一个新的`ipynb`文件,也就是正在编辑的代码文件。
|
||||||
|
|
||||||
|
> ipynb 文件是 ipython Notebook 的简写,Jupyter 脱胎于 ipython 编辑器,因此 Jupyter 文件仍然保留了 ipynb 的文件类型命名方式。
|
||||||
|
|
||||||
|
接下来,简单尝试在右侧代码框中输入 Python 代码。点击右侧代码框(cell)中输入`a = 1`。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
也就是令`a = 1`,然后`shift+enter`执行该代码。执行完成后,会自动新生成一个 cell,接下来的代码就可以在新生成的 cell 中执行。在新生成的 cell 中,输入`a`能够看到,返回结果就是`a`的赋值。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
至此,我们就完成了一次简单的 Python 代码编写和运行。
|
||||||
|
|
||||||
|
## 2.2 Notebook式编辑环境
|
||||||
|
|
||||||
|
将代码写入一个个**cell**,代码文件由一个个cell组成,书写代码时就像一行一行在记笔记,就是所谓的 Notebook 式的代码编辑环境。Notebook 式代码编辑环境其实也是 REPL(Read Eval Print Loop)环境的一种,即交互式编译。简单来说,交互式编译就是指允许用户逐行输入代码、逐行查看结果,从而逐行进行调试。这无疑是大幅降低了代码编写的难度,这也是建议 Python 初学者使用 Jupyter 的原因。
|
||||||
|
|
||||||
|
## 2.3 Jupyter的基本操作
|
||||||
|
|
||||||
|
由于后续 Jupyter 将作为主力代码编辑器,因此我们有必要深入了解 Jupyter 的一些常用功能。当然,Jupyter 本身也是一个独立的软件,具体软件的功能介绍可以查看 [Jupyter](https://jupyter.org/) 官网(https://jupyter.org),里面有 Jupyter 所有功能的完整介绍。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
此处先介绍实际学习过程中常用的功能。
|
||||||
|
|
||||||
|
### 2.3.1 cell类型选择
|
||||||
|
|
||||||
|
在 Jupyter 中,每个 cell 除了代码以外,还可以使用 Markdown 语法输入文本内容,以及尚未确定格式的草稿。
|
||||||
|
- 选定一个 cell 后,选择 code 则是代码内容;
|
||||||
|
- 选择 Markdown 则是使用 Markdown 语法输入文本内容;
|
||||||
|
- 选择 Raw 则是草稿内容,不会输出任何结果。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
例如,使用Markdown语法打印标题:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
同样,是`shift+enter`执行 Markdown 语法。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以看出,jupyter 还是个不错的笔记工具,同时,也非常适合编写数据分析报告。
|
||||||
|
|
||||||
|
### 2.3.2 cell不同模式及快捷键
|
||||||
|
|
||||||
|
cell 有两种不同模式,选中 cell 时是 **command(命令)** 模式,而单击 cell 内,出现光标闪烁时,则是进入了 cell 内容的 **edit(编辑)** 模式,在编辑模式下,可以进行内容输入,而在命令模式下,则可使用一些 cell 快捷键对其进行操作。
|
||||||
|
|
||||||
|
|
||||||
|
快捷键 | 操作| 快捷键 | 操作
|
||||||
|
:---:|---|:---:|---
|
||||||
|
a | 在上方插入一个cell | b | 在下方插入一个cell
|
||||||
|
x | 剪切该cell | c | 复制该cell
|
||||||
|
v | 在cell下方粘贴复制的cell | m | 转为markdown模式
|
||||||
|
y | 转为code模式 | r | 转为raw模式
|
||||||
|
z | 撤销操作 | 双击d | 删除该cell
|
||||||
|
|
||||||
|
|
||||||
|
### 2.3.3 JupyterLab 文件管理系统
|
||||||
|
|
||||||
|
相比 Notebook,JupyterLab 拥有非常便捷的文件管理系统,我们前面已经尝试,当创建一个新的`ipy`文件时,左侧文件栏将出现对应文件。JupyterLab 左侧就是其文件管理界面,在其中,我们可以进行文件创建、文件夹创建、文件上传等操作。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### 2.3.4 JupyterLab 文件系统主目录及修改方式
|
||||||
|
|
||||||
|
那么,我们创建的`ipy`文件存在哪呢?<br>
|
||||||
|
在 Anaconda 中,一般系统会默认 Jupyter 的主目录就是系统的文档目录。但文档目录在 C 盘下,如果是首次安装 Jupyter,并希望单独设置一个文件夹作为默认主目录,可以按照如下步骤进行操作:
|
||||||
|
|
||||||
|
|
||||||
|
**(1)在 Anaconda 中打开 CMD.exe Prompt,进入命令行界面**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
当然,此处也可以`win+r`,然后输入`cmd`进入命令行。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
**(2)生成 Jupyter 配置文件**
|
||||||
|
|
||||||
|
在命令行中,输入
|
||||||
|
|
||||||
|
```python
|
||||||
|
jupyter notebook --generate-config
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
注意上述配置文件的保存路径。若已有配置文件,再次输入命令将可选择是否覆盖原配置文件。当然,覆盖原配置文件将导致原配置失效。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**(3)修改主目录配置**
|
||||||
|
|
||||||
|
接下来,按照命令行中提示的配置文件路径,找到配置文件。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
可以用文本编辑器打开,能够看到所有的 Jupyter 可选配置。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
`ctrl+f`进入搜索栏,搜索`c.NotebookApp.notebook_dir`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
将对应位置的#号删除,使其配置生效,并在等号后面输入新的主目录文件夹位置(自行选择文件位置),保存退出,并在**重启Jupyter后生效**。
|
||||||
|
|
||||||
|
|
||||||
|
**(4)查看新的主目录**
|
||||||
|
|
||||||
|
进入对应文件夹位置,查看文件夹内文件和 JupyterLab 内显示文件是否一致。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
至此,新的主目录文件设置成功。当然,任何对主目录文件的操作都会同步至 JupyterLab 的文件栏页。
|
||||||
|
|
||||||
|
> 不难发现,Jupyter文件系统主目录就类似于其他编程语言的操作空间概念。
|
||||||
|
|
||||||
|
### 2.3.5 停止`ipy`进程
|
||||||
|
|
||||||
|
由于 Python 代码在运行过程中,对象都存储在内存中,因此,为了合理控制内存,在必要的情况下需要手动终止 Jupyter 进程。此时可以使用左侧栏的 KERNEL SESSIONS 功能,进行操作。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
点击 SHUT DOWN 即可关闭对话。
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
# 1、Turtle图图
|
||||||
|
|
||||||
|
在开始练习前,先来认识一下接下来会陪伴你好几天的程序小伙伴儿turtle【图图】吧,为什么叫做turtle【图图】呢,是因为它长这样:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
弄错了,是这个:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
虽然有点丑丑的,但确实是一直货真价实的乌龟,乌龟的英文是turtle,你也可以叫它【图图】,当然你要是叫它小乌龟,也......没啥问题......😓
|
||||||
|
|
||||||
|
# 2、图图的设置
|
||||||
|
|
||||||
|
天啊!乌龟竟然能画图,开玩笑吗?
|
||||||
|
|
||||||
|
不过这确实是一只会画图的乌龟,就像乌龟在海滩上爬行能留下痕迹一样,程序中的【图图】也能帮助我们画出各种各样的线条。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## (1)导入图图库
|
||||||
|
|
||||||
|
想要使用图图帮助你画图,需要提前做一些设置,设置很简单,一共就两步:
|
||||||
|
|
||||||
|
1、导入图图库
|
||||||
|
|
||||||
|
2、设定重要信息
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
**第一行** 就是导入【图图】库,然后给图图起了个新的名字t(后面会用到)
|
||||||
|
|
||||||
|
**第二行** 是固定的设置,咱们每次写上就行
|
||||||
|
|
||||||
|
**第三行** 是给图图设置一个形状,这里是turtle(乌龟形状),你也可以改成下面的英文:
|
||||||
|
|
||||||
|
```python
|
||||||
|
arrow circle square triangle classic
|
||||||
|
```
|
||||||
|
|
||||||
|
**第四行** 也是一定要有的语句,它的作用是告诉程序,“我画完啦!”
|
||||||
|
|
||||||
|
### 练习1:
|
||||||
|
|
||||||
|
将第四行的`?`改成其它几个英文单词,看看有什么不一样吧:
|
||||||
|
|
||||||
|
(单词:`arrow` `circle` `square` `triangle` `classic`)
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='?')
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
# 3、行动吧!图图
|
||||||
|
|
||||||
|
## (1)图图走起来
|
||||||
|
|
||||||
|
掌握了图图的基本设置,就可以让图图开始行动啦,我们先学习最简单的向前走:
|
||||||
|
|
||||||
|
```python
|
||||||
|
t.forward(100) # 向前走100步
|
||||||
|
```
|
||||||
|
想让图图向前走几步,数字就是多少,把这行代码写入到前面的程序中试试:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
既然有向前走,那么,当然还有向后退,赶紧试试吧:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
t.back(100) # 向后退100步
|
||||||
|
```
|
||||||
|
## (2)向左转
|
||||||
|
|
||||||
|
既然能走,当然可以转弯啊,图图可是“艾科不来梅西多亚瑟星球”最能转的乌龟
|
||||||
|
|
||||||
|
向左转也很简单,就是下面这个口令啦:
|
||||||
|
|
||||||
|
```python
|
||||||
|
t.left(90) #向左转90度
|
||||||
|
```
|
||||||
|
再加上向前走我们就能画出一个直角啦:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.left(90) #向左转90度
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 挑战1:
|
||||||
|
|
||||||
|
(1)修改`left`中的角度,看看图图能画出下面这些角吗?(钝角和锐角)
|
||||||
|
|
||||||
|
(2)除了向左转,也有向右转哦,它的指令是这样的`t.right(100)`,赶紧试试吧!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 练习2:
|
||||||
|
|
||||||
|
如果要画出下面这个角度为30度的角应该怎么办呢?
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.right(30) #向右转30度
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
这样写对吗?
|
||||||
|
|
||||||
|
哈哈,当然不对,来画一条辅助线看看,图图实际转动的度数是红色箭头指示的角度,我们知道平角等于180度,平角也就是下面这个平平的线啦:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
所以图图实际转动的角度等于180 - 30 = 150,之后我们会画很多这样的角,你也可以做一条辅助线,用180减去标出来的角,就是要转动的角度啦:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
正确的写法:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.right(150) #向右转150度
|
||||||
|
t.forward(100) #向前走100步
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
|
||||||
|
学完【预备课1】你是不是认为图图只是一个能简单移动的乌龟?
|
||||||
|
|
||||||
|
其实图图的能力相当强大,当你学的知识越来越多后甚至可以使用图图去创作这样一幅作品:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
当然,这是很久以后了!!!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
不过,千里之行,始于足下,我们再来学习一个基础技能吧:给图画涂色。
|
||||||
|
|
||||||
|
给图画涂色其实很简单,和我们用颜料笔画画的步骤是一样的
|
||||||
|
|
||||||
|
(1)先用笔在颜料盘中粘一个颜色
|
||||||
|
|
||||||
|
(2)然后开始涂色
|
||||||
|
|
||||||
|
(3)当涂完颜色后结束涂色
|
||||||
|
|
||||||
|
用程序写也是这三步:
|
||||||
|
|
||||||
|
```python
|
||||||
|
t.color('red')
|
||||||
|
#color是颜色的英文,括号里写上你想使用的颜色的英文单词
|
||||||
|
|
||||||
|
t.begin_fill()
|
||||||
|
#开始填充颜色,begin:开始,fill:填充
|
||||||
|
|
||||||
|
t.end_fill()
|
||||||
|
#结束填充颜色,end:结束
|
||||||
|
```
|
||||||
|
|
||||||
|
涂色的格式是这样的:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.color('red') #颜色设置成红色
|
||||||
|
t.begin_fill() #开始填充
|
||||||
|
|
||||||
|
# 这里写要画的图形的指令
|
||||||
|
|
||||||
|
t.end_fill() #结束填充
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
如果要填充一个直角三角形可以这样写:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import turtle as t
|
||||||
|
t.TurtleScreen._RUNNING = True
|
||||||
|
t.shape(name='turtle')
|
||||||
|
t.color('red') #颜色设置成红色
|
||||||
|
t.begin_fill() #开始填充
|
||||||
|
t.forward(60)
|
||||||
|
t.right(90)
|
||||||
|
t.forward(80)
|
||||||
|
t.right(143)
|
||||||
|
t.forward(100)
|
||||||
|
t.right(127)
|
||||||
|
t.end_fill() #结束填充
|
||||||
|
t.done()
|
||||||
|
```
|
||||||
|
将`red`改成其他的颜色试试吧:
|
||||||
|
|
||||||
|
- 黄色:yellow
|
||||||
|
- 蓝色:blue
|
||||||
|
- 绿色:green
|
||||||
|
- 紫色:purple
|
||||||
|
- 粉丝:pink
|