Skip to content

Latest commit

 

History

History
544 lines (383 loc) · 12.7 KB

202106W2.md

File metadata and controls

544 lines (383 loc) · 12.7 KB

@Author : Lewis Tian ([email protected])

@Link : github.com/taseikyo

@Range : 2021-06-06 - 2021-06-12

Weekly #32

readme | previous | next

本文总字数 3871 个,阅读时长约:7 分 34 秒,统计数据来自:算筹字数统计

Photo by Nat Belfort on Unsplash

山重水复疑无路,柳暗花明又一村 —— 陆游《游山西村》

Table of Contents

  • algorithm
  • review
    • 提高 Python 技能的 10 个高级概念(Medium)
    • 提高 Bash 使用效率的一些小技巧(英文)
  • tip
    • golang 字符串遍历问题(中文)
    • golang 中 slice 和 map 的坑(中文)
  • share
    • 只要没到最后就还有希望

algorithm 🔝

review 🔝

1、异常处理

我记得之前在某期提过这点,Python 中异常处理的 try 代码块语法如下所示:

try:
    pass
except Exception as e:
    raise
else:
    pass
finally:
    pass
  • try 代码块包含需要执行的代码
  • except 代码块包含执行失败时执行的代码
  • else 代码块只有在 try 块成功执行时才执行
  • finally 代码块总是执行,它独立于其他块

处理多个异常时,可以嵌套:

try:
   f = open('myfile.txt')
   s = f.readline()
   i = int(s.strip())
except OSError as err:
   print("OS error: {0}".format(err))
except ValueError:
   print("Could not convert data to an integer.")
except:
   print("Unexpected error:", sys.exc_info()[0])
   raise
finally:
   print("Operation Successfully Done!!")

2、collections 库

collections 是 Python 内置库,其目的是改进内置容器的功能

  1. Counter

它接受一个可迭代对线并返回一个有序的字典,其中键是一个元素,值是出现的次数

from collections import Counter

data = [1,1,1,1,2,3,4,3,3,5,6,7,7]
count = Counter(data)
print(count)

# Counter({1: 4, 3: 3, 7: 2, 2: 1, 4: 1, 5: 1, 6: 1})

它有三个额外的函数:elements() 根据次数返回数据(迭代器的形式);most_common() 返回元组列表([(value1, counts1), (value2, counts2)]),其中包含按排序顺序排列的最常见元素;subtract() 从可迭代对象中减去对应元素

  1. namedtuple

这个我用的比较多,比默认的元组多了可以命名的特点

from collections import namedtuple

Direction = namedtuple('Direction','N,S,E,W')
dt = Direction(4,74,0,0)
print(dt)

# Direction(N=4, S=74, E=0, W=0)
  1. OrderedDict

有序字典,在最新的 Python 中,已经支持了这点

from collections import OrderedDict

dictt = OrderedDict()
dictt['a'] = 5
dictt['d'] = 2
dictt['c'] = 1
dictt['b'] = 3
print(dictt)

# OrderedDict([('a', 5), ('d', 2), ('c', 1), ('b', 3)])
  1. defaultdict

带默认值的 dict,这个我也经常使用

from collections import defaultdict

dictt = defaultdict(int)
dictt['a'] = 2
print(dictt['a'])
print(dictt['b'])

# 2
# 0
  1. deque

双向队列,可以从两边添加/删除元素

3、itertools

这个库包含了处理迭代器的各种函数,我用的实际上并不多

  1. product(iterable,iterable),笛卡尔积
  2. permutation(iterable),所有可能的排列
  3. combinations(iterable,n),所有可能的指定长度的组合
  4. combinations_with_replacement(iterable,n)所有可能的指定长度的组合,可重复
  5. accumlate(iterable),返回累加和
  6. groupby(iterable,key=FUNC),返回一个具有连续键和组的迭代器
from itertools import groupby

gps = groupby([1,2, 3, 4, 5, 6, 7, 8, 9, 10], key=lambda x : x > 5)

for v, gp in gps:
    print(v, list(gp))

# False [1, 2, 3, 4, 5]
# True [6, 7, 8, 9, 10]

4、lambda

匿名函数,基本上扯到 Python 的高级技巧都会提到它

5、装饰器(decorator)

装饰器是 Python 中的一个特性,它在不显式修改现有代码的情况下向现有代码添加一些新功能,它有两类:函数装饰器和类装饰器

6、生成器(generator)

yield 的函数称为生成器,生成器只在请求时生成一个项,占用的内存空间也很小。

def fibon(limit):
  a,b = 0,1
  while a < limit:
      yield a
      a, b = b, a + b

for x in fibon(10):
   print (x)

7、多线程(threading)和多进程(multiprocessing)

you can use threading if your program is network bound or multiprocessing if it's CPU bound

8、Dunder/Magic methods

特殊方法,即前后有两个下划线的函数

num =  5
num*6
# 30

num.__mul__(6)
# 30

9、日志

日志是在执行时捕获代码流的过程,日志有助于轻松地调试代码

Python 有个 logging 库用来将日志写入文件,它一共有五个级别:

  1. Debug:用于诊断具有详细信息的问题
  2. Info:成功的确认
  3. Warning:当意外情况发生时
  4. Error:由于一个比警告更严重的问题
  5. Critical:程序不能自行运行的严重错误

10、上下文管理器

主要是指 with语句

Implementing a Context Manager as a Class:需要自己实现 __enter____exit__ 方法:

class File(object):
    def __init__(self, file_name, method):
        self.file_obj = open(file_name, method)
    def __enter__(self):
        return self.file_obj
    def __exit__(self, type, value, traceback):
        self.file_obj.close()

with File('demo.txt', 'w') as opened_file:
    opened_file.write('Hola!')

这篇文章介绍了一些 bash 的历史记录的技巧,基本上我没用过,但是我转移到 zsh 之后,使用自动补全插件搭配自己写的一个移除重复命令,基本上够我日常使用了,下面的是这篇文章中提到的几个 trick:

1、删除无意义的命令

使用下面这行命令之后,将会忽略之后使用的这些命令,不再添加到历史记录中(大概,没验证过)

export HISTIGNORE='pwd:exit:fg:bg:top:clear:history:ls:uptime:df'

下面命令将导致以空格开头的命令不会写入历史记录:

export HISTCONTROL=ignorespace

ignoredups 选项将值存储一份命令副本(不会写入重复命令)

2、不要丢失重要的历史记录

没有 histappend 选项,如果打开多个并发 shell 会话,那么只保存最后一个退出 shell 的条目

shopt -s histappend
export HISTSIZE=10000

3、有效地回忆命令

!! 命令会自动调用前一个命令

$ pwd
/etc
$ !!
pwd
/etc

sudo !! 则会以 sudo 权限执行前一个命令,如果在命令后面加上 :p 则会只打印命令而不执行

也可以设置选项默认仅打印而不执行:

shopt -s histverify

在命令前面加一个 ! 将会运行上一条命令,如 !ping 则会运行上一条以 ping 开头的命令

4、使用 !$!*

使用 !$ 将展开最后一个命令的最后一个参数

$ mv list.txt items.txt
$ vim !$
vim items.txt
$ cp !$ shopping.txt
cp items.txt shopping.txt

使用 !* 将展开前一行上所有参数

$ rm /var/log/httpd/access.log /var/log/httpd/error.log
$ touch !*
touch /var/log/httpd/access.log /var/log/httpd/error.log

5、将前一行的匹配单词替换为 ^

下面就是将 error 替换为 access

$ rm /var/log/httpd/error.log
$ ^error^access
rm /var/log/httpd/access.log

tip 🔝

golang 中字符串遍历的三种方式

1、range 遍历

package main

import "fmt"

func main() {
    str := "烫烫烫烫"
    for i, ch := range str {
    // ch 的类型为 rune 默认 utf-8 编码,一个汉字三个字节
    fmt.Println(i, ch)
  }
}
// 0 28907
// 3 28907
// 6 28907
// 9 28907

2、for 循环

package main

import "fmt"

func main() {
    str := "烫烫烫烫"
    for i := 0; i < len(str); i++ {
        // 依据下标取字符串中的字符,类型为 byte
        ch := str[i]
        fmt.Println(i, ch)
    }
}
// 0 231
// 1 131
// 2 171
// 3 231
// 4 131
// 5 171
// 6 231
// 7 131
// 8 171
// 9 231
// 10 131
// 11 171

3、转换为 rune 输出

package main

import "fmt"

func main() {
    str := "烫烫烫烫"
    array := []rune(str)
    for i := 0; i < len(array); i++ {
        // 依据下标取字符串中的字符,类型为 byte
        ch := array[i]
        // unicode 编码转十进制输出
        fmt.Println(i, ch)
    }
}
// 0 28907
// 1 28907
// 2 28907
// 3 28907

一、浅拷贝同根

func main() {
    nums := [3]int{}
    nums[0] = 1

    fmt.Printf("nums: %v, len: %d, cap: %d\n", nums, len(nums), cap(nums))

    dnums := nums[0:2]
    dnums[0] = 5

    fmt.Printf("nums: %v, len: %d, cap: %d\n", nums, len(nums), cap(nums))
    fmt.Printf("dnums: %v, len: %d, cap: %d\n", dnums, len(dnums), cap(dnums))
}

输出:

nums: [1 0 0], len: 3, cap: 3
nums: [5 0 0], len: 3, cap: 3
dnums: [5 0], len: 2, cap: 3

slice 若不是深拷贝或者重新生成新空间,无论通过参数传递还是使用 := 或者 [:] 赋值都存在同根性。

二、扩容摆脱同根

slice 与 array 最大的区别在于 slice 不需要指定大小会自动扩容等一些特性,我们在接受并习惯同根性后。

slice 在多次 append 元素时,若满足扩容策略,这时候内部就会重新申请一块内存空间,将原本的元素拷贝一份到新的内存空间上。

此时其与原本的数组就没有任何关联关系了,再进行修改值也不会变动到原始数组。

func main() {
    nums := [3]int{}
    nums[0] = 1

    fmt.Printf("nums: %v, len: %d, cap: %d\n", nums, len(nums), cap(nums))

    dnums := nums[0:2]
    dnums = append(dnums, []int{2, 3}...)
    dnums[1] = 1

    fmt.Printf("nums: %v, len: %d, cap: %d\n", nums, len(nums), cap(nums))
    fmt.Printf("dnums: %v, len: %d, cap: %d\n", dnums, len(dnums), cap(dnums))
}

输出:

nums: [1 0 0], len: 3, cap: 3
nums: [1 0 0], len: 3, cap: 3
dnums: [1 1 2 3], len: 4, cap: 6

三、empty 与 nil

  • empty
func main() {
    nums := []int{}
    renums := make([]int, 0)

    fmt.Printf("nums: %v, len: %d, cap: %d\n", nums, len(nums), cap(nums))
    fmt.Printf("renums: %v, len: %d, cap: %d\n", renums, len(renums), cap(renums))
}

输出:

nums: [], len: 0, cap: 0
renums: [], len: 0, cap: 0
  • nil
func main() {
    var nums []int
    fmt.Println(nums,len(nums),cap(nums))
}

输出:

[] 0 0

通过输出来看我们会发现不管是数据还是 len 和 cap 都是相同的输出内容。

那我们就来用代码来证明一下他们是否真的一致

func main() {
    var nums []int
    renums := make([]int, 0)
    if nums == nil {
        fmt.Println("nums is nil.")
    }
    if renums == nil {
        fmt.Println("renums is nil.")
    }
}

输出:

nums is nil.

输出结果是不是出乎意料!不过聪明如你肯定已经通过自己的经验想到了答案。

一个有分配空间(empty)一个没有分配空间(nil)

以上就是我使用 slice 遇到的坑,这里不再针对 map 做特殊分析了。

share 🔝

1. 只要没到最后就还有希望

永不言弃,只要没到最后就还有希望

很喜欢毛主席的一句话:凡事要从最坏处着眼,往最好处努力

在我看过的书中,最喜欢的两本书是《论语》和《三国演义》(想读《毛概》一直没读)

很早就读了这两本书,因此我也被这两本书深深影响着。

从中学到了很多做人的哲学,特别是诸葛亮,未算胜而先算败,我总是习惯去考虑最坏的结果,因此总是被老妈说不自信,其实并不是。

凡事往坏处想,至少能提前做好一些准备,不至于到了事发不知所措;再者没到最后谁能知道结果是怎么样的,只要没放皇榜,你我皆有可能。

为此我习惯做的就是未算胜而先算败,凡事从最坏处着眼,而去往最好处努力。

readme | previous | next