@Author : Lewis Tian (taseikyo@gmail.com)
@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
review
提高 Python 技能的 10 个高级概念(Medium)
tip
golang 中 slice 和 map 的坑(中文)
1、异常处理
我记得之前在某期提过这点,Python 中异常处理的 try
代码块语法如下所示:
复制 try :
pass
except Exception as e :
raise
else :
pass
finally :
pass
else
代码块只有在 try
块成功执行时才执行
处理多个异常时,可以嵌套:
复制 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 内置库,其目的是改进内置容器的功能
它接受一个可迭代对线并返回一个有序的字典,其中键是一个元素,值是出现的次数
复制 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()
从可迭代对象中减去对应元素
这个我用的比较多,比默认的元组多了可以命名的特点
复制 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)
有序字典,在最新的 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)])
带默认值的 dict
,这个我也经常使用
复制 from collections import defaultdict
dictt = defaultdict ( int )
dictt [ 'a' ] = 2
print (dictt[ 'a' ])
print (dictt[ 'b' ])
# 2
# 0
双向队列,可以从两边添加/删除元素
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
库用来将日志写入文件,它一共有五个级别:
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
则会只打印命令而不执行
也可以设置选项默认仅打印而不执行:
在命令前面加一个 !
将会运行上一条命令,如 !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
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
复制 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
复制 func main () {
var nums [] int
fmt. Println (nums, len (nums), cap (nums))
}
输出:
通过输出来看我们会发现不管是数据还是 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." )
}
}
输出:
输出结果是不是出乎意料!不过聪明如你肯定已经通过自己的经验想到了答案。
一个有分配空间(empty)一个没有分配空间(nil)
以上就是我使用 slice 遇到的坑,这里不再针对 map 做特殊分析了。
1. 只要没到最后就还有希望
永不言弃,只要没到最后就还有希望
很喜欢毛主席的一句话:凡事要从最坏处着眼,往最好处努力 。
在我看过的书中,最喜欢的两本书是《论语》和《三国演义》(想读《毛概》一直没读)
很早就读了这两本书,因此我也被这两本书深深影响着。
从中学到了很多做人的哲学,特别是诸葛亮,未算胜而先算败,我总是习惯去考虑最坏的结果,因此总是被老妈说不自信,其实并不是。
凡事往坏处想,至少能提前做好一些准备,不至于到了事发不知所措;再者没到最后谁能知道结果是怎么样的,只要没放皇榜,你我皆有可能。
为此我习惯做的就是未算胜而先算败,凡事从最坏处着眼,而去往最好处努力。
readme | previous | next