Redis MQ
2021-03-06
Redis 作者还分叉了项目,专门搞了一个实验项目 disque 来实现一个消息队列管理工具。
https://github.com/antirez/disque
这不是这篇文章的关注重点。这篇文章是想像一下使用 Redis 做 MQ 的情况(没在生产环境使用过)。
一、为什么使用 MQ
无非就是减少耦合,削峰填谷。
中间引入一个组件做代理,自然耦合解开了。
另外,一个稳定高效的 MQ 可以快速吸收消息,避免生产者的等待,同时消费者可以根据自己的处理速度来运行。就好像水库的作用,就是削峰填谷,损有余而补不足。
PS: MQ 好像还起到了一点 HA 的作用。
PS: 一种设计完善的消息推送方案,比如一个事件多方消费,如果没有消息队列则需要多次调用。
至于可能导致的问题嘛,当然也有,就是多了一个组件引入了新的复杂性。
上面提到的好处都是建立在 MQ 稳定可靠运行的基础之上,自然需要很多新的工作来维护消息队列中间件。
小规模系统就应该怎么简单怎么来,但如果系统足够复杂,多点工作总是难免的。
1.1 RabbitMQ
Erlang 所写(我听说过的唯一一个 Erlang 项目),AMQP 0-9-1 (高级消息队列协议) 的开源实现(Mozilla 公共许可证)。
1.2 Kafka
据说性能非常好,而且是倍经考验了,时不时看到有大型项目将其作为基础设施使用。
虽然最近两年 Kafka 很热门,但我没有在生产环境使用过。
二、Redis 实现
基础功能:
拓展功能:
- 重复消费(多个消费者)
- 确认机制
- 持久化
- 广播(消息直接写入多个队列)
2.1 List
采用 List 构建一个简单的 FIFO 队列:
LPUSH
RPOP/BRPOP 如果没有消息了,会返回 nil
RPOPLPUSH / BRPOPLPUSH 可以在处理完成之后再从备份队列删除,实现类似 ACK 的功能
PS: RPOPLPUSH: 弹出一个元素,并将该元素压入另一个列表。BRPOPLPUSH: 如果源列表没有元素会阻塞。
redicConn.lpush('queue', 'a')
while True:
msg = redisConn.rpop('queue')
if msg is None:
time.sleep(1)
continue
do_something(msg)
2.2 SortedSet
有序集合,不支持重复消息(有时可以当做功能)。
用时间戳(或加一定权重)当分数,据此排序。
2.3 Pub/Sub
实现了简单的 Pub/Sub 功能。
虽然没有 RabbitMQ 那些多 API,也不支持那些复杂的功能配置,但是在简单的场景下用用还是不错的。
- 消费者订阅指定主题
subscribe foo
- 生产者发布消息
publish foo ooxxooxx
有两个不可忽视的问题:
- Pub 上来的消息必须实时发往订阅者,如果订阅者不在线,那么消息就丢失了。
- 如果消息积压到一定程度,Redis 会强制断开消费者连接,清空消息队列。
2.4 Streams
Redis 5.0 引入的新数据类型。
这个名字可能是来自流式计算。
所有提交上来的消息按顺序串成一个消息链。
非常值得一提的是,Stream 实现的队列允许重复消费、支持 ACK。
三、总结
- Redis 不保证数据完整性,所以如果是对数据完整性有要求的话,不能使用这个方案。
- Redis 不支持消息确认机制(
Ack/Nack/Reject),需要自己在应用层面实现,但如果这么做的话,不如干脆用 MQ 算了。
- 除了
Pub/Sub 之外,List、SortedSet、Streams 都支持消息持久化,但是应用时需要注意避免消息堆积,否则会可能会对内存造成压力。
如何保证消息的完整性呢?
参考资料与拓展阅读
Linux 开发工具 jq JSON
2021-03-04

jq 是我在命令行中解析 JSON 的一个常用工具,用起来非常顺手。
- https://github.com/stedolan/jq
- https://stedolan.github.io/jq/
用法
curl https://24pullrequests.com/users.json | jq
# 取第一个元素
curl https://24pullrequests.com/users.json | jq '.[0]'
# 取第一个元素的指定字段
curl https://24pullrequests.com/users.json | jq '.[0].nickname'
# 切片
curl https://24pullrequests.com/users.json | jq '.[:2]'
# 遍历
curl https://24pullrequests.com/users.json | jq '.[] | .nickname'
# 取字段
curl https://24pullrequests.com/users/changeworld.json | jq .nickname
# 取多个字段
curl https://24pullrequests.com/users/changeworld.json | jq '.nickname,.contributions_count'
# 获取列表长度
curl https://24pullrequests.com/users.json | jq length
# 列出 Keys
curl https://24pullrequests.com/users/changeworld.json | jq keys
curl https://24pullrequests.com/users/changeworld.json | jq "keys[]"
curl https://24pullrequests.com/users.json | jq ".[0] | keys[]"
# 列出 Keys 和 值类型
cat a.json | jq ".[0] | keys,map(type)"
cat a.json | jq ".[0] | to_entries | map([.key, (.value | type)])"
cat a.json | jq '.[0] | to_entries | map("\(.key) : \(.value|type)")[]'
还有很多更强大的用法,可以参考文档,我就会上面几个,在命令行中简单搜索 JSON 也够用了。
刚在文档中学会一招,重新组合 JSON:
curl https://24pullrequests.com/users/changeworld.json | jq '[.nickname, .organisations[].login]'
curl https://24pullrequests.com/users/changeworld.json | jq '{name:.nickname, orgs:[.organisations[].login]}'
👍🏻 Nice!!!
Golang GoStdLib
2021-03-01
- archive: 打包 (tar, zip)
参考:《Golang archive 包》
- compress: 压缩 (bzip2, flate, gzip, lzw, zlib)
压缩 & 解压缩
import (
"bytes"
"compress/gzip"
"io"
)
func compressData(data string) ([]byte, error) {
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
_, err := gz.Write([]byte(data))
if err != nil {
return nil, err
}
if err := gz.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func decompressData(compressedData []byte) (string, error) {
r, err := gzip.NewReader(bytes.NewReader(compressedData))
if err != nil {
return "", err
}
defer r.Close()
decompressedData, err := io.ReadAll(r)
if err != nil {
return "", err
}
return string(decompressedData), nil
}
压缩 & 解压缩 & 列出压缩包中的内容
import (
"archive/zip"
)
// 压缩文件
func compressFile(zipFilename string, filenames ...string) error {
zipFile, err := os.Create(zipFilename)
if err != nil {
return err
}
defer zipFile.Close()
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
for _, filename := range filenames {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
fi, err := file.Stat()
if err != nil {
return err
}
fileHeader, err := zip.FileInfoHeader(fi)
if err != nil {
return err
}
// 设置文件名(压缩后的文件名)
fileHeader.Name = filename
// 写入文件头部
writer, err := zipWriter.CreateHeader(fileHeader)
if err != nil {
return err
}
// 复制文件内容到压缩包中
_, err = io.Copy(writer, file)
if err != nil {
return err
}
}
return nil
}
// 解压缩文件
func decompressFile(zipFilename, targetDir string) error {
zipReader, err := zip.OpenReader(zipFilename)
if err != nil {
return err
}
defer zipReader.Close()
for _, file := range zipReader.File {
// 打开压缩包中的文件
fileReader, err := file.Open()
if err != nil {
return err
}
defer fileReader.Close()
// 创建目标文件
extractedFilePath := fmt.Sprintf("%s/%s", targetDir, file.Name)
extractedFile, err := os.Create(extractedFilePath)
if err != nil {
return err
}
defer extractedFile.Close()
// 复制文件内容到目标文件
_, err = io.Copy(extractedFile, fileReader)
if err != nil {
return err
}
}
return nil
}
func listFilesInZip(zipFilename string) ([]string, error) {
files := []string{}
zipReader, err := zip.OpenReader(zipFilename)
if err != nil {
return files, err
}
defer zipReader.Close()
for _, file := range zipReader.File {
files = append(files, file.Name)
}
return files, nil
}
Golang GoStdLib
2021-03-01
标准库中的 archive 支持 tar,zip 两种打包格式。
https://en.wikipedia.org/wiki/Tar_(computing)
最常用的 tar 包:
func createTarArchive(sourceDir, targetFile string) error {
file, err := os.Create(targetFile)
if err != nil {
return err
}
defer file.Close()
tarWriter := tar.NewWriter(file)
defer tarWriter.Close()
return filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
header, err := tar.FileInfoHeader(info, info.Name())
if err != nil {
return err
}
if err := tarWriter.WriteHeader(header); err != nil {
return err
}
if !info.IsDir() {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(tarWriter, file)
if err != nil {
return err
}
}
return nil
})
}
func extractTarArchive(sourceFile, targetDir string) error {
file, err := os.Open(sourceFile)
if err != nil {
return err
}
defer file.Close()
tarReader := tar.NewReader(file)
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return err
}
targetPath := filepath.Join(targetDir, header.Name)
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
return err
}
if header.Typeflag == tar.TypeReg {
file, err := os.Create(targetPath)
if err != nil {
return err
}
defer file.Close()
_, err = io.Copy(file, tarReader)
if err != nil {
return err
}
}
}
return nil
}
Golang
2021-02-24
数组声明时可以使用 ... 当作长度,表示自动判断。
数组声明时,可以只初始化其中的部分值。
a := [...]int{2: 1, 4: 2} // [5]int{0, 0, 1, 0, 4}
b := [5]int{1, 2, 3} // [5]int{1, 2, 3, 0, 0}
基本操作
a := [...]int{1, 2, 3}
var b []int
// 新增
b = append(b, 1)
// 删除元素
s = append(s[:index], s[index+1:]...)
// 遍历
for i := range a {
fmt.Println(a[i])
}
for index, value := range a {
fmt.Printf("%v: %v", index, value)
}
for _, value := range a {
fmt.Printf("%v", value)
}
for i := 0; i < len(array); i++ {
fmt.Printf("value: %d\n", array[i])
}
// 判断元素是否存在/获取元素序号
func Index(target int, array []int) int {
for i, v := range array {
if target == v {
return i
}
}
return -1
}
示例
package main
import (
"fmt"
"reflect"
)
func main() {
a := [5]int{1, 3, 5, 7, 9}
fmt.Printf("%s\t%#v\n", reflect.TypeOf(a), a)
// [5]int [5]int{1, 3, 5, 7, 9}
// array => slice
b := a[:] // a[0:len(a)]
fmt.Printf("%s\t%#v\n", reflect.TypeOf(b), b)
// []int []int{1, 3, 5, 7, 9}
// slice => array
// c := ([5]int)(b) // cannot convert b (type []int) to type [5]int
c := (*[5]int)(b) // 切片只能转换成数组指针
fmt.Printf("%s\t%#v\n", reflect.TypeOf(c), c)
// *[5]int &[5]int{1, 3, 5, 7, 9}
// 用类型别名试试:
type NumArr [5]int
c2 := (*NumArr)(b)
fmt.Printf("%s\t%#v\n", reflect.TypeOf(c2), c2)
// *main.NumArr &main.NumArr{1, 3, 5, 7, 9}
// 只能遍历赋值
d := [5]int{}
for index, v := range b {
d[index] = v
}
fmt.Printf("%s\t%#v\n", reflect.TypeOf(d), d)
// 通过 copy 的方法实现 slice2array
e := [5]int{}
copy(e[:], b) // return length of b
fmt.Printf("%s\t%#v\n", reflect.TypeOf(e), e)
}
去重
func Unique(arr []int) []int {
arrLen := len(arr) - 1
for arrLen > 0 {
for i := arrLen - 1; i >= 0; i-- {
if arr[arrLen] == arr[i] {
arr = append(arr[:i], arr[i+1:]...)
break
}
}
arrLen--
}
return arr
}
func UniqueOptimized(arr []int) []int {
uniqueArr := make([]int, 0, len(arr))
uniqueMap := make(map[int]struct{})
for _, num := range arr {
if _, ok := uniqueMap[num]; !ok {
uniqueArr = append(uniqueArr, num)
uniqueMap[num] = struct{}{}
}
}
return uniqueArr
}
// BenchmarkUniqueOriginal-20 135 8658964 ns/op 0 B/op 0 allocs/op
// BenchmarkUniqueOptimized-20 3501 347027 ns/op 285402 B/op 208 allocs/op
可以看到,一个性能好一些(只用 4% 的时间),资源使用多一些。
使用反射
https://blog.csdn.net/youngwhz1/article/details/83026263
func SliceRemoveDuplicate(a interface{}) (ret []interface{}) {
if reflect.TypeOf(a).Kind() != reflect.Slice {
fmt.Printf("<SliceRemoveDuplicate> <a> is not slice but %T\n", a)
return ret
}
va := reflect.ValueOf(a)
for i := 0; i < va.Len(); i++ {
if i > 0 && reflect.DeepEqual(va.Index(i-1).Interface(), va.Index(i).Interface()) {
continue
}
ret = append(ret, va.Index(i).Interface())
}
return ret
}
func SliceInsert(s []interface{}, index int, value interface{}) []interface{} {
rear := append([]interface{}{}, s[index:]...)
return append(append(s[:index], value), rear...)
}
func SliceInsert2(s *[]interface{}, index int, value interface{}) {
rear := append([]interface{}{}, (*s)[index:]...)
*s = append(append((*s)[:index], value), rear...)
}
func SliceInsert3(s interface{}, index int, value interface{}) bool {
if ps, ok := s.(*[]string); ok {
if val, ok := value.(string); ok {
rear := append([]string{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
return true
}
} else if ps, ok := s.(*[]int); ok {
if val, ok := value.(int); ok {
rear := append([]int{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
}
} else if ps, ok := s.(*[]float64); ok {
if val, ok := value.(float64); ok {
rear := append([]float64{}, (*ps)[index:]...)
*ps = append(append((*ps)[:index], val), rear...)
}
} else {
fmt.Printf("<SliceInsert3> Unsupported type: %T\n", s)
}
return false
}
func SliceRemove(s []interface{}, index int) []interface{} {
return append(s[:index], s[index+1:]...)
}
func SliceRemove2(s *[]interface{}, index int) {
*s = append((*s)[:index], (*s)[index+1:]...)
}
func SliceRemove3(s interface{}, index int) bool {
if ps, ok := s.(*[]string); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else if ps, ok := s.(*[]int); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else if ps, ok := s.(*[]float64); ok {
*ps = append((*ps)[:index], (*ps)[index+1:]...)
} else {
fmt.Printf("<SliceRemove3> Unsupported type: %T\n", s)
return false
}
return true
}
func SliceClear(s *[]interface{}) {
*s = append([]interface{}{})
}
func SliceClear2(s *[]interface{}) {
*s = (*s)[0:0]
}
func SliceClear3(s interface{}) bool {
if ps, ok := s.(*[]string); ok {
*ps = (*ps)[0:0]
//*ps = append([]string{})
} else if ps, ok := s.(*[]int); ok {
*ps = (*ps)[0:0]
//*ps = append([]int{})
} else if ps, ok := s.(*[]float64); ok {
*ps = (*ps)[0:0]
//*ps = append([]float64{})
} else {
fmt.Printf("<SliceClear3> Unsupported type: %T\n", s)
return false
}
return true
}
泛型实现
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
tot := len(s) + len(v)
if tot <= cap(s) {
s2 := s[:tot]
copy(s2[i+len(v):], s[i:])
copy(s2[i:], v)
return s2
}
s2 := make(S, tot)
copy(s2, s[:i])
copy(s2[i:], v)
copy(s2[i+len(v):], s[i:])
return s2
}
内置方法 make
make(Type, Len, Cap) 可以用来为 slice, map, channel 三种类型初始化(类似:C 语言 (int *)malloc(5))。
这里就看 Slice 的情况。
s1 := make([]int, 5)
s2 := make([]int, 5, 10)
// s2[8] = 1 // panic: runtime error: index out of range [8] with length 5
s2 = append(s2, 1)
如果有预留空间,append 的时候可以不用重新分配内存并遍历赋值。
如果切片有扩容的需要,就最好采用 make 来初始化。
如果不指定容量,则切片的容量和长度将相等。
Golang
2021-02-22
- 创建临时文件
- 使用编辑器编辑
- 获取内容
- 删除临时文件
package main
import (
"bufio"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
)
func main() {
// create a tmp file
// ioutil.TempFile creates a temp file and opens the file for reading and writing
// and returns the resulting *os.File (file descriptor).
tmpFile, err := ioutil.TempFile(os.TempDir(), "todo-tmp-")
if err != nil {
log.Fatal("Cannot create temporary file", err)
}
// defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
fmt.Println("Created File: " + tmpFile.Name())
// choose editor by env var
editor := os.Getenv("EDITOR")
args := []string{}
if editor == "" {
editor = "vim"
args = append(args, "--clean")
fmt.Println("No EDITOR enviroment variable set, use " + editor)
} else {
fmt.Println("Using EDITOR enviroment variable: " + editor)
}
args = append(args, tmpFile.Name())
fmt.Println("Opening file with " + editor + " with args: " + fmt.Sprint(args))
// check the editor command is available
// _, err = exec.LookPath(editor)
// if err != nil {
// log.Fatal("Cannot find editor: " + editor)
// }
// call the editor
cmd := exec.Command(editor, args...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
log.Fatal("Cannot run editor: " + editor)
}
// read the file
// data, err := ioutil.ReadFile(tmpFile.Name())
// if err != nil {
// log.Fatal("Cannot read file: " + tmpFile.Name())
// }
tmpFile.Seek(0, 0)
s := bufio.NewScanner(tmpFile)
for s.Scan() {
fmt.Println("Content:\n\n" + s.Text())
}
if err = s.Err(); err != nil {
log.Fatal("error reading temp file", err)
}
}
Golang GoStdLib
2021-02-21
func Clone(s string) string //
func Compare(a, b string) int // 比较
func Contains(s, substr string) bool // 包含子字符串
func ContainsAny(s, chars string) bool // 包含任意Char
func ContainsRune(s string, r rune) bool // 包含Rune
func Count(s, substr string) int // 计数
func Cut(s, sep string) (before, after string, found bool)
func EqualFold(s, t string) bool //
func Fields(s string) []string // 切割
func FieldsFunc(s string, f func(rune) bool) []string
func HasPrefix(s, prefix string) bool // 前缀判断 startswith
func HasSuffix(s, suffix string) bool // 后缀判断 endswith
func Index(s, substr string) int // 查找
func IndexAny(s, chars string) int // 查找Any
func IndexByte(s string, c byte) int // 查找Byte
func IndexFunc(s string, f func(rune) bool) int // 查找Func
func IndexRune(s string, r rune) int // 查找Rune
func Join(elems []string, sep string) string // 连接
func LastIndex(s, substr string) int // 右查找
func LastIndexAny(s, chars string) int // 右查找Any
func LastIndexByte(s string, c byte) int // 右查找Byte
func LastIndexFunc(s string, f func(rune) bool) int // 右查找Func
func Map(mapping func(rune) rune, s string) string //
func Repeat(s string, count int) string // 重复
func Replace(s, old, new string, n int) string // 替换
func ReplaceAll(s, old, new string) string // 替换 = Replace -1
func Split(s, sep string) []string // 切割
func SplitAfter(s, sep string) []string //
func SplitAfterN(s, sep string, n int) []string //
func SplitN(s, sep string, n int) []string //
func Title(s string) string //
func ToLower(s string) string // 转小写
func ToLowerSpecial(c unicode.SpecialCase, s string) string
func ToTitle(s string) string // Title
func ToTitleSpecial(c unicode.SpecialCase, s string) string
func ToUpper(s string) string
func ToUpperSpecial(c unicode.SpecialCase, s string) string
func ToValidUTF8(s, replacement string) string
func Trim(s, cutset string) string
func TrimFunc(s string, f func(rune) bool) string
func TrimLeft(s, cutset string) string
func TrimLeftFunc(s string, f func(rune) bool) string
func TrimPrefix(s, prefix string) string
func TrimRight(s, cutset string) string
func TrimRightFunc(s string, f func(rune) bool) string
func TrimSpace(s string) string
func TrimSuffix(s, suffix string) string
type Builder
func (b *Builder) Cap() int
func (b *Builder) Grow(n int)
func (b *Builder) Len() int
func (b *Builder) Reset()
func (b *Builder) String() string
func (b *Builder) Write(p []byte) (int, error)
func (b *Builder) WriteByte(c byte) error
func (b *Builder) WriteRune(r rune) (int, error)
func (b *Builder) WriteString(s string) (int, error)
type Reader
func NewReader(s string) *Reader
func (r *Reader) Len() int
func (r *Reader) Read(b []byte) (n int, err error)
func (r *Reader) ReadAt(b []byte, off int64) (n int, err error)
func (r *Reader) ReadByte() (byte, error)
func (r *Reader) ReadRune() (ch rune, size int, err error)
func (r *Reader) Reset(s string)
func (r *Reader) Seek(offset int64, whence int) (int64, error)
func (r *Reader) Size() int64
func (r *Reader) UnreadByte() error
func (r *Reader) UnreadRune() error
func (r *Reader) WriteTo(w io.Writer) (n int64, err error)
type Replacer
func NewReplacer(oldnew ...string) *Replacer
func (r *Replacer) Replace(s string) string
func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error)
Builder
Reader
Replacer
参考资料与拓展阅读
Python
2021-02-20

垃圾回收
Garbage Collector, 简写: GC
Python 垃圾回收是简单基于引用计数
弱引用
在计算机程序设计中,弱引用与强引用相对,是指不能确保其引用的对象不会被垃圾回收器回收的引用。一个对象若只被弱引用所引用,则被认为是不可访问(或弱可访问)的,并因此可能在任何时刻被回收。一些配有垃圾回收机制的语言,如Java、C#、Python、Perl、Lisp等都在不同程度上支持弱引用。
一句话:弱引用不增加计数,对引用计数型 GC 友好一些
垃圾回收与循环引用的问题
import gc
IDS = {}
class A:
def __del__(self):
_id = id(self)
print('A.__del__ %s: 0x%x' % (IDS[_id], _id))
OBJS = {i: A() for i in range(3)}
for i, obj in OBJS.items():
_id = id(obj)
IDS[_id] = f'OBJS[{i}]'
print('%s: 0x%x' % (IDS[_id], _id))
OBJS[1].attr = OBJS[1]
print('1' * 50)
print('====> del OBJS[0]')
del OBJS[0]
print('2' * 50)
print('====> del OBJS[1]')
del OBJS[1]
print('3' * 50)
print('====> del OBJS[2]')
del OBJS[2]
print('4' * 50)
gc.collect()
import weakref
print()
print('=' * 50)
class B:
def __init__(self, obj):
self.attrs = [obj]
def __del__(self):
_id = id(self)
print('B.__del__ %s: 0x%x' % (IDS[_id], _id))
a = A()
b = B(a)
a.xyz = b
IDS[id(a)] = 'a'
IDS[id(b)] = 'b'
del a, b # do nothing
print('=' * 40)
gc.collect() # will del a and b
print()
print('=' * 50)
class C:
def __init__(self, obj):
self.attrs = [weakref.ref(obj)]
def __del__(self):
_id = id(self)
print('C.__del__ %s: 0x%x' % (IDS[_id], _id))
a = A()
c = C(a)
a.xyz = c
IDS[id(a)] = 'a'
IDS[id(c)] = 'c'
del a, c
print('=' * 40)
gc.collect()
标准库:weakref
class weakref.ref(object[, callback]) 回调
weakref.proxy(object[, callback])
weakref.getweakrefcount(object)
weakref.getweakrefs(object)
class weakref.WeakKeyDictionary([dict])
.keyrefs()
class weakref.WeakValueDictionary([dict])
.valuerefs()
class weakref.WeakSet([elements])
Set class that keeps weak references to its elements. An element will be discarded when no strong reference to it exists any more.
class weakref.WeakMethod(method)
class weakref.finalize(obj, func, /, *args, **kwargs)
weakref.ReferenceType
weakref.ProxyType
weakref.CallableProxyType
weakref.ProxyTypes
import weakref
class Klass:
pass
obj = Klass()
ref = weakref.ref(obj)
print(ref())
del obj
print(ref()) # None
obj = Klass()
p = weakref.proxy(obj)
print(p)
del obj
print(p) # ReferenceError: weakly-referenced object no longer exists
参考资料与拓展阅读
个人 育儿 政策
2021-02-17
参考《国家学生体质健康标准(2014年修订)》,主要看看 90 分的要求。
Golang GoStdLib GoReflect
2021-02-17
func Copy(dst, src Value) int
func DeepEqual(x, y any) bool
func Swapper(slice any) func(i, j int)
type ChanDir
func (d ChanDir) String() string
type Kind
func (k Kind) String() string
type MapIter
func (iter *MapIter) Key() Value
func (iter *MapIter) Next() bool
func (iter *MapIter) Reset(v Value)
func (iter *MapIter) Value() Value
type Method
func (m Method) IsExported() bool
type SelectCase
type SelectDir
type SliceHeader
type StringHeader
type StructField
func VisibleFields(t Type) []StructField
func (f StructField) IsExported() bool
type StructTag
func (tag StructTag) Get(key string) string
func (tag StructTag) Lookup(key string) (value string, ok bool)
Type
func ArrayOf(length int, elem Type) Type
func ChanOf(dir ChanDir, t Type) Type
func FuncOf(in, out []Type, variadic bool) Type
func MapOf(key, elem Type) Type
func PointerTo(t Type) Type
func PtrTo(t Type) Type
func SliceOf(t Type) Type
func StructOf(fields []StructField) Type
func TypeOf(i any) Type
Value
func Append(s Value, x ...Value) Value
func AppendSlice(s, t Value) Value
func Indirect(v Value) Value
func MakeChan(typ Type, buffer int) Value
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
func MakeMap(typ Type) Value
func MakeMapWithSize(typ Type, n int) Value
func MakeSlice(typ Type, len, cap int) Value
func New(typ Type) Value
func NewAt(typ Type, p unsafe.Pointer) Value
func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)
func ValueOf(i any) Value
func Zero(typ Type) Value
func (v Value) Addr() Value
func (v Value) Bool() bool
func (v Value) Bytes() []byte
func (v Value) Call(in []Value) []Value
func (v Value) CallSlice(in []Value) []Value
func (v Value) CanAddr() bool
func (v Value) CanComplex() bool
func (v Value) CanConvert(t Type) bool
func (v Value) CanFloat() bool
func (v Value) CanInt() bool
func (v Value) CanInterface() bool
func (v Value) CanSet() bool
func (v Value) CanUint() bool
func (v Value) Cap() int
func (v Value) Close()
func (v Value) Comparable() bool
func (v Value) Complex() complex128
func (v Value) Convert(t Type) Value
func (v Value) Elem() Value
func (v Value) Equal(u Value) bool
func (v Value) Field(i int) Value
func (v Value) FieldByIndex(index []int) Value
func (v Value) FieldByIndexErr(index []int) (Value, error)
func (v Value) FieldByName(name string) Value
func (v Value) FieldByNameFunc(match func(string) bool) Value
func (v Value) Float() float64
func (v Value) Grow(n int)
func (v Value) Index(i int) Value
func (v Value) Int() int64
func (v Value) Interface() (i any)
func (v Value) InterfaceData() [2]uintptrDEPRECATED
func (v Value) IsNil() bool
func (v Value) IsValid() bool
func (v Value) IsZero() bool
func (v Value) Kind() Kind
func (v Value) Len() int
func (v Value) MapIndex(key Value) Value
func (v Value) MapKeys() []Value
func (v Value) MapRange() *MapIter
func (v Value) Method(i int) Value
func (v Value) MethodByName(name string) Value
func (v Value) NumField() int
func (v Value) NumMethod() int
func (v Value) OverflowComplex(x complex128) bool
func (v Value) OverflowFloat(x float64) bool
func (v Value) OverflowInt(x int64) bool
func (v Value) OverflowUint(x uint64) bool
func (v Value) Pointer() uintptr
func (v Value) Recv() (x Value, ok bool)
func (v Value) Send(x Value)
func (v Value) Set(x Value)
func (v Value) SetBool(x bool)
func (v Value) SetBytes(x []byte)
func (v Value) SetCap(n int)
func (v Value) SetComplex(x complex128)
func (v Value) SetFloat(x float64)
func (v Value) SetInt(x int64)
func (v Value) SetIterKey(iter *MapIter)
func (v Value) SetIterValue(iter *MapIter)
func (v Value) SetLen(n int)
func (v Value) SetMapIndex(key, elem Value)
func (v Value) SetPointer(x unsafe.Pointer)
func (v Value) SetString(x string)
func (v Value) SetUint(x uint64)
func (v Value) SetZero()
func (v Value) Slice(i, j int) Value
func (v Value) Slice3(i, j, k int) Value
func (v Value) String() string
func (v Value) TryRecv() (x Value, ok bool)
func (v Value) TrySend(x Value) bool
func (v Value) Type() Type
func (v Value) Uint() uint64
func (v Value) UnsafeAddr() uintptr
func (v Value) UnsafePointer() unsafe.Pointer