关于角色模型

最近一年一直比较关心并行计算,一开始因为 python 的线程锁实在麻烦,然后又因为一直受到 k8s 的弹性伸缩影响,加上最近又看了不少函数计算的材料和相关的 POC。我之前曾经设想过,如果将程序都用函数计算的方式,同时通过 DSL 来连接这些函数,这样既可以应对访问压力,又可以处理复杂逻辑,加上弹性,费用也不会太高,达到最佳 ROI。

看到阿里云最新发布的类似 numpy 的 python 包用于并行计算来处理本来需要大内存的机器学习运算,其中提到了通过角色模型。发现这个角色模型或许可以满足前面设想的一些逻辑。

wiki 告诉我们:在计算机科学中,角色模型(英语:Actor model)是一种并发运算上的模型。“角色”是一种程序上的抽象概念,被视为并发运算的基本单元:当一个角色接收到一则消息,它可以做出一些决策、创建更多的角色、发送更多的消息、决定要如何回答接下来的消息。角色模型在1973年于Carl Hewitt、Peter Bishop及Richard Steiger的论文中提出。

支持角色模型的语言非常多,

角色模型推崇的哲学是“一切皆是角色”,这与面向对象编程的“一切皆是对象”类似,但是面向对象编程通常是顺序执行的,而角色模型是并行执行的。角色是一个运算实体,回应接受到的消息,同时并行的:

发送有限数量的消息给其他角色;
创建有限数量的新角色;
指定接受到下一个消息时的行为。
以上操作不含有顺序执行的假设,因此可以并行进行。

发送者与已经发送的消息解耦,是角色模型的根本优势。这允许进行异步通信,同时满足消息传递的控制结构。

消息接收者是通过地址区分的,有时也被称作“邮件地址”。因此角色只能和它拥有地址的角色通信。它可以通过接受到的信息获取地址,或者获取它创建的角色的地址。

角色模型的特征是,角色内部或之间进行并行计算,角色可以动态创建,角色地址包含在消息中,交互只有通过直接的异步消息通信,不限制消息到达的顺序。

几乎所有主流语言都支持角色模型,在 Python 里面保持更新的库是:

Pykka:https://github.com/jodal/pykka
Pulsar:http://quantmind.github.io/pulsar/

之后会继续研究角色模型的使用和实用性。

每天编程:继续谈谈 python pathlib

本文内容翻译自 python3_with_pleasure 

pathlib 现在是 python 3 的标准库,避免你来写一大堆 os.path.join。

from pathlib import Path

dataset = 'wiki_images'
datasets_root = Path('/path/to/datasets/')

train_path = datasets_root / dataset / 'train'
test_path = datasets_root / dataset / 'test'

for image_path in train_path.iterdir():
    with image_path.open() as f: # note, open is a method of Path object
        # do something with an image

我们可以看到一个在机器学习中使用到的场景,训练数据的路径和测试用路径,通过很简单的 / 符号就连接起来了。代码安全、简洁、可读性好。

pathlib.path 有一大堆的方法和属性,我们可以减少很多花费在搜索引擎上的时间。

p.exists()
p.is_dir()
p.parts
p.with_name('sibling.png') # only change the name, but keep the folder
p.with_suffix('.jpg') # only change the extension, but keep the folder and the name
p.chmod(mode)
p.rmdir()

每天编程:非常好用的 python 路径文件处理库 pathlib

的确,我之前也是习惯用 os.path.join ,倒是也没有觉得有什么不好,直到我看到了 pathlib,应该是 python 3.x 时代引入的新的专门更加优雅的处理比较麻烦的路径拼接、文件是否存在、路径是否存在等操作系统文件路径相关的Python 官方函数。

比如你可以用 path1 / path2 / filename 这样的简单直观的方法来完成路径和文件名的拼接,通过 PurePosixPath 和 PureWindowsPath 的类显式的兼容多操作系统。

原来的 os 库发展太久了,如同 python 的日期时间库一样,在长时间的进化中包和其函数的结构越来越混乱了。从下面的 os 和 pathlib 的函数比较可见一斑:

os and os.pathpathlib
os.path.abspath()Path.resolve()
os.chmod()Path.chmod()
os.mkdir()Path.mkdir()
os.rename()Path.rename()
os.replace()Path.replace()
os.rmdir()Path.rmdir()
os.remove()os.unlink()Path.unlink()
os.getcwd()Path.cwd()
os.path.exists()Path.exists()
os.path.expanduser()Path.expanduser() and Path.home()
os.path.isdir()Path.is_dir()
os.path.isfile()Path.is_file()
os.path.islink()Path.is_symlink()
os.stat()Path.stat()Path.owner(),Path.group()
os.path.isabs()PurePath.is_absolute()
os.path.join()PurePath.joinpath()
os.path.basename()PurePath.name
os.path.dirname()PurePath.parent
os.path.samefile()Path.samefile()
os.path.splitext()PurePath.suffix

Pathlib 非常完美的体现了 Python 的优雅,更多信息可以参考官方文档。打算将 fishbase 中的一些封装的文件处理函数逐步更换为对 Pathlib 的封装,Python 2.7 实在没有必要支持了。

不做世界的鸡肋

昨天,周五,很高兴收到出版社的来信,书稿终于通过了初审。

开发二部一年一度的迎新趣味知识竞赛,如期举行,又是百来号人,可惜,不少同事的名字我都叫不全。

晚上在神旺自助餐,部门的尾牙。记得第一次到这个餐厅,还是2011或者2012年某个冬天,那天下雪,回去时候在内环,车滑的厉害。那时候公司还在漕河泾,去到这个餐厅,就是觉得宜山路很堵。时间流逝,每年新春前,各类聚餐,公司年会,顿感岁月神偷。

昨天,周五,两位相处很长时间的同事 last day,多少有点伤感。记得2014年,我们聚餐的时候,和他们相谈甚欢,其中一位说等上市后,可以驾车去西藏云云,加上原来负责运维的胡老师退休,2014年之前的“老人”,只剩下我了。

不进则退,人生就是这样的不容易,也是无奈,也是自勉。没有人可以安逸,达摩克里斯之剑永悬。

今天,周六,看完了“无名之辈”,其中一位演员有同事认识,据说从2006年开始坚持表演,其貌不扬,演技精湛。

片尾曲,特别震撼,平静的旋律,极有张力的演绎:

我们不做世界的鸡肋!

Call of Duty,记录一下

很惭愧,2015年买的 Call of Duty – Advanced Wafare,之前每一年大概就玩了一小时,加上 PS4 的手柄操作很不熟练,所以一直有点畏难。一直到今天,才终于过关第一个首尔战役,拿到了两枚勋章。

游戏机大概是每个男生从小时候开始的梦想,现在恐怕早就不止男生喜欢打游戏了,我觉得我女儿最喜欢的也是各类手机游戏,这个话题太大。

我们小时候还没有什么游戏机,红白机都已经是我初中年代的产物了,那时候我已经是 Apple II 电脑的狂热 fans,当然也在电脑上花费了很多游戏时间,很难抵挡那些游戏带来的魅力。现在我们无法想象和理解当年那些看上去那么粗糙,很多画面要靠联想才知道说啥的游戏,怎么就让我们乐不思蜀了。

后来其实对很多游戏都是浅尝辄止,或许也是因为中学时代的这个原因,是有点后悔,如果当时多花费一些时间在编程上,说不定就会更好。有时候,假期里很长时间泡在学校的机房,其实大部分时候都是在玩,所以这个深深的悔意使得后来我对游戏处于一个敬而远之的态度,小游戏玩玩,但是绝不沉迷。后来,控制力好了很多,也不刻意了。甚而又购买了不少制作游戏的工具,圆一下少年时代的游戏制作梦想!

有时候,看到女儿贪玩,虽然要说她,但其实内心很理解,我们就是这样长大的,没什么好后悔的,这就是人生。

每天编程:用 Python 的类方法构造通用函数

今天花了十分钟将之前 fishbase 的身份证和银行卡的7个函数修改为类方法,这样调用基本没啥改变,调用方法优雅一些,并且用类管理之后,暴露接口等都比较好控制,类相比函数的好处就不用再说了。(很遗憾,20多年前我在用 Turbo Pascal 以及后来 Delphi 的很长一段时间内,都没有理解这一点,一直是面向过程编程,大约一直到 2005年才突然顿悟,应该用面向对象的方法,包括和自己的共享软件中的故事,以后可以专门聊聊。)

我在代码里保留了一小段 Classmethod 的例子:

class CardBin(object):

    @classmethod
    def a(cls):
        return 'hello'

    @classmethod
    def b(cls):
        return CardBin.a() + ' world'


print(CardBin.a())
print(CardBin.b())

这个例子定义了一个类方法 a,还有一个使用了类方法 a 的类方法 b,以及外部使用这两个类方法的例子。

类方法的定义和与静态方法的好处就不多说了,至少使用的时候不用实例化,这一点对我们来说在这个场景就很好。

每天编程:怎么判断身份证号码是否正确

继续分享生成测试数据库时候的一些心得,在生成假数据时,如何判断身份证号码是否正确,和银行卡一样,身份证最后一位是校验码,不过计算方法不太一样。算法这里不详细叙述,网上很多。下面的代码可以实现这一功能,输入身份证的前面17位,返回校验码。

def get_checkcode(id_number_str):
    """
    计算身份证号码的校验位;

    :param:
        * id_number_str: (string) 身份证号的前17位,比如 3201241987010100
    :returns:
        * 返回类型 (tuple)
        * flag: (bool) 如果身份证号格式正确,返回 True;格式错误,返回 False
        * checkcode: 计算身份证前17位的校验码

    举例如下::

        from fishbase.fish_data import *

        print('--- fish_data idcard_get_checkcode demo ---')

        # id number
        id1 = '32012419870101001'
        print(id1, idcard_get_checkcode(id1)[1])

        # id number
        id2 = '13052219840731647'
        print(id2, idcard_get_checkcode(id2)[1])

        print('---')

    输出结果::

        --- fish_data idcard_get_checkcode demo ---
        32012419870101001 5
        13052219840731647 1
        ---

    """

    # 判断长度,如果不是 17 位,直接返回失败
    if len(id_number_str) != 17:
        return False, -1

    id_regex = '[1-9][0-9]{14}([0-9]{2}[0-9X])?'

    if not re.match(id_regex, id_number_str):
        return False, -1

    items = [int(item) for item in id_number_str]

    # 加权因子表
    factors = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)

    # 计算17位数字各位数字与对应的加权因子的乘积
    copulas = sum([a * b for a, b in zip(factors, items)])

    # 校验码表
    check_codes = ('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')

    checkcode = check_codes[copulas % 11].upper()

    return True, checkcode

这段代码已经可以基本实现网上大部分的身份证校验器的功能了,在实际生成身份证假数据的时候,问题还要复杂一些,因为我们不能随便那一串数字,去生成一个校验码,那个意义不太大。转载如下:

公民身份号码是特征组合码,由前十七位数字本体码和最后一位数字校验码组成。排列顺序从左至右依次为六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。

地址码: 表示编码对象常住户口所在县(市、旗、区)的行政区划代码。对于新生儿,该地址码为户口登记地行政区划代码。需要没说明的是,随着行政区划的调整,同一个地方进行户口登记的可能存在地址码不一致的情况。行政区划代码按GB/T2260的规定执行。

出生日期码:表示编码对象出生的年、月、日,年、月、日代码之间不用分隔符,格式为YYYYMMDD,如19880328。按GB/T 7408的规定执行。原15位身份证号码中出生日期码还有对百岁老人特定的标识,其中999、998、997、996分配给百岁老人。

顺序码: 表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。

校验码: 根据本体码,通过采用ISO 7064:1983,MOD 11-2校验码系统计算出校验码。算法可参考下文。前面有提到数字校验码,我们知道校验码也有X的,实质上为罗马字符X,相当于10.


fishbase 扩展包 for python:
源代码:https://github.com/chinapnr/fishbase
文档:https://fishbase.readthedocs.io/en/latest/
pypi :https://pypi.org/project/fishbase/ 


重新打理了一下本站

wordpress 5.0.2 的变化太大了,特别是在编辑文章方面,还不是太适应,但是已经感觉功能只强大。

今天1月1日,本来还是给自己排了一些例行工作,突然想稍微放松一下,让大脑休息一天,换点事情做做。

wordpress 的主题很久没有更换了,换一下,做了两张照片,点缀一下。

生活需要有点仪式感。年纪大了, 一些道理想的通透了。

年少的时候,说人生苦短,为赋新词强说愁。现在真的感觉,要抓紧每一天,时间的流逝,实在超过我的预期。

今年,女儿发给我的新年祝福中说:不要让自己太辛苦哦!

明年看来要多赚点钱,多旅行,再写点东西,做自己喜欢的事情!

每天编程:怎么校验银行卡是否有效

最近在公司有一个 fakedata 的项目,用来生成各类假数据,在各种测试环境,其中就有银行卡卡号的生成,中国这方面是有自己的标准的,详细的介绍可以参考这篇文章,写的很清楚。

简单来说,就是国内的银行卡卡号,主要都是银联标准,然后前面的4-8位称之为卡 bin,这个卡 bin 是可以区分是具体哪个银行,以及是否是借记卡还是贷记卡(信用卡),关键是最后一位校验位。

从校验卡号的角度来说,可以在应用的尽量前端进行判断,如果卡号校验错误,就不用去进行四要素判断,更不能进行实际的账户交易了。校验码对了,再根据卡 bin 判断是什么银行和什么性质的银行卡(这个下次再说)。

def get_bankcard_checkcode(card_number_str):
    """
    计算银行卡校验位;

    :param:
        * card_number_str: (string) 要查询的银行卡号
    :returns:
        checkcode: (string) 银行卡的校验位

    举例如下::

        from fishbase.fish_data import *

        print('--- fish_data get_bankcard_checkcode demo ---')

        # 不能放真的卡信息,有风险
        print(get_bankcard_checkcode('439188000699010'))

        print('---')

    输出结果::

        --- fish_data get_bankcard_checkcode demo ---
        9
        ---

    """
    total = 0
    even = True

    for item in card_number_str[-1::-1]:
        item = int(item)
        if even:
            item <<= 1
        if item > 9:
            item -= 9
        total += item
        even = not even

    checkcode = (10 - (total % 10)) % 10

    return str(checkcode)

实际算法部分不算复杂,参考了一些网上看到的资料。需要的朋友可以借鉴。


fishbase 扩展包 for python:
源代码:https://github.com/chinapnr/fishbase
文档:https://fishbase.readthedocs.io/en/latest/
pypi :https://pypi.org/project/fishbase/ 

告别 2018

没想到时间总是这么匆忙,2018快要结束了,印象深刻的还是上半年3月左右,公司准备上市的时候,上市团队的同事和我说还有100天云云,转眼就到了12月31日,去年此刻,在名古屋的片段,还历历在目,白川乡的大雪。

着实也是辛苦的,各类事情,种种忙碌,也是诸多的焦虑,好在都熬了过来。而最大的感悟便是:他强由他强,清风拂山岗。

人生不易,也算愈挫愈勇,未来的路,想想也不容易(有些艰难,都不敢细想),但是还是要继续走下去,继续坚持,这才是应该的态度。

再见 2018,你好 2019!