每周至少写一道算法题,阅读并点评至少一篇英文文章,学习至少一个技术技巧,分享一篇有观点和思考的技术文章。首先完成了这将近一万字的翻译,感触挺多的,一个技术的发展往往在不经意间产生。之后的算法写了一道堆相关的题目,tip 是在这周遇到的两个 python 工具,最后是关于返回值为什么最好不要用 null 的文章。

Review

Making Wrong Code Look Wrong

这篇文章的作者是 Avram Joel Spolsky (汉文:周思博),是 Fog Creek Software 的 CEO,其中一个最出名的产品有 Stack Overflow

使错误的代码更容易看出错误,文章以作者面包店工作清洁机器为例子,讲述了内行人和外行人的不同见解,来思考代码编码的时候的规范以及一些不明显的,容易误导的错误的解决方案。之后在一系列例子中引出了代码约定的作用以及风靡一时的匈牙利表示法,介绍了这个表示法的来源,发展,以及最后为什么会消失。总而言之,最后的结论也就是在编写代码或在查阅代码的时候,能在眼睛所到处能获得更多的信息,而不仅仅只是运行之后才知道,虽然匈牙利表示法在现在强类型检查和一些智能 IDE 下变得有些不必要了,不过其中的思想也值得学习。

翻译:文章翻译 | Making Wrong Code Look Wrong

(文章比较长,有很多翻译不到位的,欢迎指出)

Algorithm

347. Top K Frequent Elements

求前 k 个出现次数最高的元素。这个是典型的用堆来解决的题目,而且因为建一个堆是很常见的操作,所以 Python 语言也有一些相关的第三方库来辅助。

首先看一个最简单的版本,之后在一次用自己的方案替代。

from typing import List
from collections import Counter

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 直接使用 Counter
        count = Counter(nums)
        return [key[0] for key in count.most_common(k)]

这里用到了两个第三方的库,首先是 Counter 对象,它是 collections 库下的一个功能类,而 collections 提供了很多容器数据相关的方法。Counter 是一个计数器工具,提供了快速和方便的计数功能,内部是以 key、value 存储的方式存储值,传入一个 list ,里面会根据每个元素出现的次数统计每个 key 出现的次数,而其中的 most_common 则可以返回前 k 个出现次数最多的元素( key 和出现次数组成的元组),之后在返回的时候取出值来就行。

当然其中也可以用上 heapd 的方法建一个堆,或者利用其 nlargest 的方法取出前 k 个最大的值,简单改一下。

from typing import List
import heapq
from collections import Counter

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 直接使用 Counter
        count = Counter(nums)
                return heapq.nlargest(k, count.keys(), key=count.get)

这个 nlargest 方法就可以求出数据集中前 k 个大的值,而这个等效于:

sorted(iterable, key=key, reverse=True)[:n]

这样的方式。

后面尝试使用 Python 自带的堆的方式解决这个问题,同样的 Counter 也可以自己简单实现:

from typing import List
import heapq

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        num_dict = dict()
        ans = []
        head = []
        for num in nums:
            num_dict[num] = num_dict.get(num, 0) + 1
        for key, value in num_dict.items():
            # 因为求最大的数,headp 默认建立最小堆,这里取个负数,并且将出现次数做主键,值放第二位
            heapq.heappush(head, (-value, key))
        for i in range(k):
            # 取出出现此时最多的元素,将值加到结果中
            ans.append(heapq.heappop(head)[1])
        return ans

依旧使用 headp 类,这个类其中的主要功能就是实现了一个最小堆,可以很轻松的将列表以原地排序的方式实现一个最小堆,并且也提供了插入和删除堆顶元素的操作函数。其中,因为题目要求的是出现次数最大的前 k 个,所以这里对值进行取反操作,保存进堆中,最后循环 k 次,将值从堆顶弹出得到答案。

Tip

Fabric

这是一个 Python 的远程部署神器。它提供可丰富的与 SSH 进行交互的接口,可以在本地或在远程服务器上进行自动化、流水化地执行 Shell 命令,非常适合做部署和运维相关的工作。

这里需要注意,Fabric 有不同版本,并且写法相差很大,不同点参考:http://www.fabfile.org/upgrading.html#upgrading

安装

pip install fabric

上面这个默认安装的是 2.x 的版本。

pip install Fabric3==1.14.post1

下面的例子以上面这个版本为主。

Hello

建一个文件:fabfile.py ,编写内容:

def hello():
    print("Hello world!")

之后在 Shell 脚本中执行:fab hello ,其中 fab 命令在 pip 安装的时候就装好的了。输出:

$ fab hello
Hello world!

Done.

也可以带参数,例如:

def hello(name):
    print("Hello {}!".format(name))

在执行这个任务时就需要带上参数:

$ fab hello:jaelyn
Hello jaelyn!

Done.

执行本地命令

使用 local 函数。

from fabric.api import local

def ls_local():
    local('ls')

执行远程命令

from fabric.api import run, env

env.hosts = ['example1.com', 'example2.com']
env.user = 'xixi'
env.password = 'haha'

def hello():
    run('ls')

使用 env 的方式设置环境信息,首先 hosts 是主机名,可以同时在多个主机上同时运行 Shell 命令,之后设置用户名密码,最后使用 run 函数执行命令。

参考:

Arrow: Better dates & times for Python

正如标题所言,是 Python 中处理时间的神器。

安装

pip install arrow

基本使用

>>> import arrow
>>> arrow.get('2013-05-11T21:23:58.970460+07:00')
<Arrow [2013-05-11T21:23:58.970460+07:00]>

>>> utc = arrow.utcnow()
>>> utc
<Arrow [2013-05-11T21:23:58.970460+00:00]>

>>> utc = utc.shift(hours=-1)
>>> utc
<Arrow [2013-05-11T20:23:58.970460+00:00]>

>>> local = utc.to('US/Pacific')
>>> local
<Arrow [2013-05-11T13:23:58.970460-07:00]>

>>> local.timestamp
1368303838

>>> local.format()
'2013-05-11 13:23:58 -07:00'

>>> local.format('YYYY-MM-DD HH:mm:ss ZZ')
'2013-05-11 13:23:58 -07:00'

>>> local.humanize()
'an hour ago'

>>> local.humanize(locale='ko_kr')
'1시간 전'

(上面的基本使用来自官方文档)

参考:

list 分组

记录一个 Python 的操作,可以将 list 进行按 k 个分组:

calories = [1,2,3,4,5,6,7,8,9]
k = 2
for num in zip(*[iter(calories)]*k):
    print(num)

输出:

(1, 2)
(3, 4)
(5, 6)
(7, 8)

也可以使用 zip_long 来替代,就可以全部输出。

Share

Why NULL is Bad?

以 java 的一段代码为例子,介绍了为什么在面向对象规范中使用 null 是个非常糟糕的做法,以及给出了两种替代方案,之后详细介绍了在获取返回值为 null 的时候引发的一系列问题,呼吁不应该将 null 做为返回值。

分类: 生活

0 条评论

发表回复

您的电子邮箱地址不会被公开。