@Author : Lewis Tian ([email protected])
@Link : github.com/taseikyo
@Range : 2021-06-06 - 2021-06-12
本文总字数 3871 个,阅读时长约:7 分 34 秒,统计数据来自:算筹字数统计。
Photo by Nat Belfort on Unsplash
山重水复疑无路,柳暗花明又一村 —— 陆游《游山西村》
- 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 内置库,其目的是改进内置容器的功能
- 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()
从可迭代对象中减去对应元素
- 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)
- 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)])
- defaultdict
带默认值的 dict
,这个我也经常使用
from collections import defaultdict
dictt = defaultdict(int)
dictt['a'] = 2
print(dictt['a'])
print(dictt['b'])
# 2
# 0
- deque
双向队列,可以从两边添加/删除元素
3、itertools
这个库包含了处理迭代器的各种函数,我用的实际上并不多
product(iterable,iterable)
,笛卡尔积permutation(iterable)
,所有可能的排列combinations(iterable,n)
,所有可能的指定长度的组合combinations_with_replacement(iterable,n)
所有可能的指定长度的组合,可重复accumlate(iterable)
,返回累加和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
库用来将日志写入文件,它一共有五个级别:
- Debug:用于诊断具有详细信息的问题
- Info:成功的确认
- Warning:当意外情况发生时
- Error:由于一个比警告更严重的问题
- 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 🔝
永不言弃,只要没到最后就还有希望
很喜欢毛主席的一句话:凡事要从最坏处着眼,往最好处努力。
在我看过的书中,最喜欢的两本书是《论语》和《三国演义》(想读《毛概》一直没读)
很早就读了这两本书,因此我也被这两本书深深影响着。
从中学到了很多做人的哲学,特别是诸葛亮,未算胜而先算败,我总是习惯去考虑最坏的结果,因此总是被老妈说不自信,其实并不是。
凡事往坏处想,至少能提前做好一些准备,不至于到了事发不知所措;再者没到最后谁能知道结果是怎么样的,只要没放皇榜,你我皆有可能。
为此我习惯做的就是未算胜而先算败,凡事从最坏处着眼,而去往最好处努力。