Shell's Home

支付宝的一项设计问题

Dec 30, 2012 - 1 minute read - Comments

话说最近,贝壳成批更新了一些密码。在更新到网络支付系统的时候,心血来潮做了个分析。感觉网银系统很不安全。尤其是支付宝的手机客户端。我们下面以一个实际例子说明一下支付宝系统(其实远不止支付宝)的问题。 情况说明 贝壳的支付宝系统做了手机和淘宝帐号绑定,开启了手机动态口令,无线支付,和手机宝令三项功能。由于可以从淘宝或者支付宝登录,因此设定了70bit以上的高强度密码,1年更换一次。支付密码强度略弱,是30bit级别的,一年更换一次。支付宝账户安全等级高。使用支付宝的系统包括两台电脑和一台手机,一台是linux,一台是受限windows,只安装特定软件,不浏览和安装风险网页。总体来说,这个安全结构在支付宝用户中都是有数的。 风险在于支付宝手机客户端上,贝壳在评估的时候发现,支付宝手机客户端可以记录密码。这是一个非常具有风险的事情。因此贝壳模拟评估了一下,假如你丢失了手机,会发生什么事情。 低于200的支付 首先,由于支付宝客户端保存了密码,因此你可以很容易的登录支付宝。上面有绑定快捷支付的话(这是贝壳的常态,相信也有很多人有绑定,或者账户内有余额),在200以下就可以直接支付给对方,不需要任何额外验证。贝壳绑定的浦发银行发出了提款警告,但是很可惜,由于手机在恶意者手上,这一行为没有任何用处。 当然,常识告诉我们,如果你丢了个手机只损失200,这个问题可以忽略不计。 超过200的支付 如果超过200,则需要支付密码和手机验证码。在这个例子中,手机验证码是没有用处了,主要的保护就在支付密码上。然而,支付宝客户端具有找回支付密码的功能。幸好,他是基于身份证的。贝壳的身份证不是每个人都知道,因此在超过200的时候是安全的。 是否还有可能,从支付宝中恢复原始密码呢?因为有原始密码,就可以用网页访问支付宝,使用手机来恢复支付密码。关于这点,直到撰文为止,贝壳没有确认。 然而,问题其实远比这个简单。在没有支付宝密码的情况下。你可以申请忘记密码。然后支付宝会要求你输入你的账号来恢复密码。帐号?我记得在支付宝客户端上有记录。。。果然,输入帐号后,贝壳收到了支付宝的验证码。输入验证码后,贝壳重设了支付宝密码。在这一过程中,贝壳绑定的邮箱收到了警告——不过依然很可惜,唯一能够即时提示的手机现在在恶意者手中,主人一无所觉。 然后就是很没有新意的重设支付密码过程,借助手里的手机,我们完整的得到了整个支付宝的所有权,并且可以提光绑定了快捷支付的整张信用卡。在这里的唯一阻碍就是信用卡开卡行的支付限额。超过限额后需要使用密码和手机验证码来解除(浦发),手机验证码可以无视,密码是未知的。 结论 粗算了一下,丢一台手机,在一个小时内没有察觉,大约会造成4000元-4500元的损失,这还没有算手机。如果发卡行没有设定限额,最高大约是10W元。 我们来细数一下,整个过程里面的问题。 手机客户端不应当保存用户名密码,尤其是涉及资金的问题 对于频繁登录的问题,可以允许客户绑定一个手机token来解决。token等效于密码,但是不能做所有涉密操作,不能修改安全性,找回密码等。客户需要可以通过安全手段查看当前有多少个手机绑定了token,并且可以删除。贝壳之所以认为支付宝的系统有弱点,就是因为找遍整个支付宝系统,找不到删除token的地方。不能删除的token等于没用。 丢失密码的找回需要更加慎重 单纯的手机找回密码是有风险的,当然,单纯的邮箱找回也有。一种方法是同时验证手机和邮箱可以立刻找回密码,否则需要等待一定时间(例如几个小时,等待主人发现手机丢失)。 但是这仍然有风险。一般android里都会绑定邮件接收吧,借助这个很容易收到邮件。设计一个安全的密码找回方案并不是那么容易的,看来只有多等几个小时的方案略安全一些了。 同时这里也提醒一下。如果你在银行挂失银行卡找回密码的时候,觉得业务很方便。贝壳建议你立刻更换银行。在找回密码时越麻烦的银行,才是越安全的。 基于手机的核心验证体系 这是关键中的关键。我们可能会设计一个安全机制,里面有一步两步三步,必须全部通过才能操作资金。然而实际使用中,如果这三步都是依赖于同一个设备,那就对安全性没有任何提升——没有人敢保证手机不会丢失吧。钱包丢失最多损失几百,手机丢失呢? 目前,整个网银系统(不止是支付宝)都越来越多的依赖于手机。在手机越来越智能的今天,其可靠性实际上正在变差。手机可能丢失,也可能被安装恶意软件(尤其是root/越狱过的手机)。我们可以列出当手机丢失,或者安装了高权限的恶意软件时,会发生的安全问题。 个人信息泄密,包括朋友通讯方式,备忘,生日,身份证号码,家庭地址等。其中有些就涉及安全。 支付宝密码和支付密码可以重设(即,泄露)。 宝令丢失。 网银验证码无意义。 支付宝警告无意义。 网银警告无意义。 实际上,这不是支付宝的问题,而是用户在选择和设计自己的安全系统时,无意中造成的问题。 解决方案 暂时还没有太好的方案。 首先期待支付宝将手机登录改为token,取消密码保存,隐藏用户名,用户可以删除token,以解决直观的威胁。 期待手机丢失时快速发现的应用/功能。如果能在5-10分钟内停机,可以有效的解决手机丢失造成的风险。(贝壳向中国移动确认了,当手机停机后,是不能收取短信的) 当确实发生手机丢失时,应当首先立刻停机(因为停机速度快),然后向银行挂失(凡是绑定了手机的银行都要),然后以最快速度修改所有密码。 涉及资金的系统要多考虑一下,设计安全结构的时候要注意,如果有多于一个的方法进入系统,安全性决定于最弱的那个系统。如果所有步骤的安全性都依赖于手机,那么要考虑,丢了手机的时候会发生什么。

速度对比

Dec 29, 2012 - 4 minute read - Comments

引用 +------------+----------+-------------+------------+-------------+-------------+ |bps |B/s |network |storage |port |bus | +------------+----------+-------------+------------+-------------+-------------+ |56/48K |5.6/4.8K |Modem 56k | | | | +------------+----------+-------------+------------+-------------+-------------+ |57.6/28.8K |7.2/3.6K |GPRS | | | | +------------+----------+-------------+------------+-------------+-------------+ |236.8/236.8K|29.6/29.6K|EDGE (2.75G) | | | | +------------+----------+-------------+------------+-------------+-------------+ |1M |125K |Bluetooth 1.1| | | | +------------+----------+-------------+------------+-------------+-------------+ |1536K |192K | | |USB low speed| | +------------+----------+-------------+------------+-------------+-------------+ |1,536/512K |192/64K |ADSL (G.Lite)| | | | +------------+----------+-------------+------------+-------------+-------------+ |3M |375k |Bluetooth 2.0| | | | +------------+----------+-------------+------------+-------------+-------------+ |10M |1.25M |10BASE-T | | | | +------------+----------+-------------+------------+-------------+-------------+ |12M |1.

异常和错误的几条军规

Nov 27, 2012 - 1 minute read - Comments

如果处理不了,就地崩溃,留尸不埋,供后人评价。 偷偷埋尸,100军棍。处理不了偷偷埋尸,拉出去先轮后杀。 忽略错误只有两种合法情况。 逻辑上可以忽略,记log,忽略。 逻辑上期待异常,不记log,忽略。 逻辑上不可以忽略的忽略,100军棍。 逻辑上可以忽略,没有记log,50军棍。 错误在函数间传递的唯一理由,是可以期待别人那里有个错误处理函数,能够对的上这个错误。 没人处理错误的乱传递,20军棍。 该你处理的,处理,不该你处理的,别乱处理。 乱处理错误的,先轮后杀。

python环境部署

Nov 22, 2012 - 1 minute read - Comments

abstract 本文的目的,在于教授使用virtualenv创立python环境,对环境的管理和使用,以及代码和部署的用法范例。在阅读完本文后,你应当可以。 创立,部署,管理virtualenv环境 使用virtualenv环境进行编码 virtualenv环境建立 virtualenv是python的虚环境管理包,他的主要目的是为了隔离环境。其中包含以下两个范畴。 在虚环境中安装包,不需要对系统进行修改,不会对系统造成污染。 在系统中安装的包,不会对虚环境造成污染。这主要是出于版本安全考虑。 因此,virtualenv默认会阻止你使用系统中安装的包。要解决这个问题,需要在建立虚拟环境时指定参数–system-site-packages。 virtualenv的环境可以通过执行virtualenv path加以建立。当建立完成后不可移动,需要一些特殊调整,使用参数–relocatable对此没有帮助。 virtualenv环境的激活和反激活 virtualenv环境是通过替换系统环境变量工作的。在激活后会替换系统的提示符,提示你进入环境。一般我们使用source \$VIRTUALENVPATH/bin/activate来激活。激活后直接执行deactivate反激活。 virtualenv替换系统环境变量的方式是在path前加入virtualenv的bin路径,使自己的python优于系统python执行。同时替换pythonhome,变更lib查找路径。因此,对于某些可以指定pythonhome的应用(例如网络部署),直接指定pythonpath为virtualenv路径即可。 注意,由于virtualenv的工作方式,因此当你执行su/sudo bash后,virtualenv环境都有可能消失,但是提示符仍旧生效。建议通过sudo执行脚本,脚本内进行source比较安全。或者直接sudo目标程序也可以,不要新建上下文。 如果需要保持持续的环境激活,可以将source \$VIRTUALENVPATH/bin/activate加入~/.bashrc。 当virtualenv激活后,后续的pip安装和python使用都会使用virtualenv内的版本。因此下文未经特殊说明,都是指在激活环境后进行操作。 virtualenv环境的管理 主要包括两种手段,安装和删除。一般使用pip install package name进行安装。pip uninstall package name进行删除。 virtualenv环境的保存和恢复 virtualenv环境可以保存和恢复。所谓保存和恢复,是指在安装过包的环境中保存包列表(和具体版本),在未安装(或版本错误)的环境中启用。 一般通过pip freeze > filename进行保存。在目标机器上执行pip install -r filename进行恢复。

python入门指引

Nov 19, 2012 - 1 minute read - Comments

前言 其实我也不知道python怎么入门,由我来写这个真的不是很合适。我学python是直接找了dive into python来看。然后照着写了几个例子。大概两天后,就能磕磕绊绊的上路了。就好像拿筷子,都不记得怎么学会的拿筷子,怎么来教人呢? 不过最近在python-cn的列表里面,我大概连续数周都持续看到“python入门看哪本教程比较好”,实在是不堪其扰。干脆就写个简单的guide,有心的人自己看。没心的——那我也没办法了。 基本知识 首先,你要了解一个事情。很多你不会的东西并不属于python。例如你不知道网络通讯的流程,你不知道文件的权限和打开标志用法,你不知道fork和stdin/stdout的关系。这些python教不会你。如果你缺乏这些和语言/库无关的相关知识,请自行补课。如果你缺乏计算机基础理论,请自行补课。 因此不要随便给我发邮件/留言/咨询,为什么这个问题在python里无法解决。为什么python无法所见即所得,为什么python无法热部署,为什么python无法用于嵌入式开发。在问这个问题之前,请先确认“这是一个python的问题”。例如GIL,或者脑残lambda。如果你不确定,请自己搜索一下相关的文章,确认一下。在提问前,看看“提问的智慧”。如果你确实搜过了,找不到,那就问吧,没办法。 入门 在网络上,python入门的两大基础书籍分别是(后面有朋友补充了一本,我也加上): A Byte of Python 中文版 Dive Into Python 中文版 Learn Python The Hard Way, 2nd Edition 中文版 后面基本就是看python-doc,我推荐你跳过一堆有的没的,直接看Library Reference。python本身就是易读性极强的代码,文档又相当漂亮,内置库又全。大部分情况下,python-doc都应当能解决你的问题。 web web是程序员的一大去向。python程序员入门必须要过的一个框架就是django。不要纠结了,django在python社区中名气太大,用的人太多。因此入门材料是最多的,社区最大,门槛最低。如果你要入门web,必然从django开始。在不熟悉python的情况下,我不推荐你贸然从其他框架开始入门。 当然,如果你已经熟悉python了,考虑入门web框架,可以参考专精一节。 爬虫 python下说到爬虫开发,入门首选Scrapy。原因和上面一样,社区最大,用的人最多。好不好用就见仁见智了。反正我的所有爬虫框架都是用自己基于gevent写的库。 ui python的ui框架也很多,很复杂。同样,如果是入门,我建议从qt的两个框架,pyqt和pyside开始入门。关于这两家的恩怨我就不多废话了。 专精 所谓专精,是指使用python在特定工作上。我们基本分为几个领域。 系统和部署 virtualenv:基本凡是在商用环境中部署的,建议都用这个。可以将python自带在源码里面,避免迁移/集成问题。 python-daemon:写daemon的时候比较方便。 网络 说到网络,基本就是除web外。 twisted:非常强大的网络库,各种协议支持全面,不过reactor模式真是纠结。 gevent:异步协程模式的网络库。 Scapy:强大的网络库,基本啥都能干。 pyzmq:我一直不觉得zeromq是一个mq。我觉得他是一个抽象网络层。 web容器 python web框架的一大特点,是容器/框架/ORM/template可以分开自己玩。 注意,容器和框架是两码事情。容器是python web运行的环境,框架是解析环境的玩意。两者间一般都使用wsgi接口进行连接。这是python的标准做法,fastcgi/scgi也会被转换为wsgi进行连接。但是也不是没有其他选择。一般我们有以下模式: cgi:python-doc中自带了cgi模块。 mod_python:embed in apache。 下面是wsgi接口的容器。wsgi的优点在于我们可以在这些容器上运行任意一款支持wsgi的框架。 flup:支持提供fastcgi, scgi, AJP接口,web server可以用这三种协议进行连接。 Google App Engine:PaaS服务。 Gunicorn:直接提供http服务。 mod_wsgi:使用内部协议和apache集成。 twisted:直接提供http服务。 tornado:直接提供http服务。 uWSGI:使用内部协议和nginx集成。 werkzeug:直接提供http服务。 建议的部署模式是,用apache的,去mode_wsgi。用nginx的,去uwsgi。用GAE的,直接可用。其他,通通转发。

老大啊,你这是什么鬼名字

Nov 3, 2012 - 1 minute read - Comments

sbcl 不知道大家用没用过common lisp,这是一种语言,最早是为了计算理论而生的。这种语言以规模大,难掌握,实现多,标准不统一而著称。 Steel Bank Common Lisp,简称sbcl,就是其中一种实现。他派生于SMUCL,是目前为止我看到的最好的开源实现。反正我在求解N多问题的时候,sbcl在速度和内存占用比上的表现相当抢眼。在lisp系列中称第一,和go这种静态语言相比也不遑多让。 但是每次我看到这个缩写都忍不住想吐槽,这是什么鬼名字啊。SB Common Lisp? debian 大家也许听过,也许没听过,debian社区曾经为了中文名字的事情争吵过。 从我看到的材料上看,很多人对debian的默认念法是——大便。。。 lua 我就不吐槽了,号称屌丝语言。 LZ不哭,站起来lua。 go google的奇葩语言。每次我要查某个用法的时候,都很郁闷。为什么呢?因为几乎所有的网页都命中”go”。 以StartProcess为例,我这里搜素go StartProcess的第一个命中在MSDN,微软有个API就叫StartProcess。再后面就是财经新闻。在很后面才能看到go的API。 etc IT界还有什么奇怪的名字?

异常之殇

Oct 30, 2012 - 1 minute read - Comments

异常之殇 辗转开解 辗转开解(stack unwinding)说的其实是这么一个现象。当执行流从深层向浅层转移时,深层调用所产生的栈上对象(stack object)需要销毁,资源需要释放。对于面对对象语言而言,往往就会执行到析构函数。 辗转开解中的异常 辗转开解真正令人迷惑之处在于,如果在析构函数中发生错误怎么办?在异常处理中发生异常,我们可以继续向上抛出。但是在辗转开解代码中出现异常,上层应当收到两个异常呢?还是一个? 无论是哪种可能,都没有完美自恰的符合直觉,因此这一般是一个未定义的行为。在C++中,进程会整个彻底崩溃掉的。因此,千万不要在析构函数内抛出(或者可能抛出)异常。 如果分离析构和资源销毁 一种做法是,在析构时不做资源销毁,转而提供专门的函数来执行资源销毁过程。析构只处理简单的delete等操作。然而这种做法的杯具在于,你在任何时候,一旦使用对象,都必须使用finally来保证销毁函数的调用。在发生异常时,栈上对象的辗转开解是自动的,析构函数的调用也是自动的,但是销毁函数的调用就是手工的了。 拷贝构造和隐式转换 和构造相反,对于构造函数,我们不能限制异常使用。你必须捕获构造函数的异常。 假如构造函数出了错 普通函数出错,你有两种选择。1. 异常。2. 返回值。构造函数出错,是没有选项2的。因此构造函数凡是出错必定异常。 而如果构造函数可能出错,而你期望捕获他,你就不能栈上构造一个对象出来。因为这会导致栈上对象的作用域被限定在捕获他所用的try块之内。 分离构造的尝试 和析构函数类似,我们可以尝试在构造函数外,提供一个构造函数,来替代构造的初始化过程。这样可以很大程度上保证构造函数不出错。 然而,首先,这样的代码就会变的复杂。每次构造函数完成调用后,都必须调用初始化函数。而且,有两种特殊的构造函数你不可能使用这种方法来解决。 拷贝构造和隐式转换 是的,这两种构造函数分别叫做拷贝构造(copy construct)和隐式转换(implicit casting)。我们举例来说。如果你在函数内建立了一个对象,你希望返回这个对象,怎么做呢?第一个思路是引用返回。不幸的是,要做引用返回,这个对象必须是堆上对象,而非栈上对象。因为栈上对象在返回后会销毁掉。如果要返回栈上对象,唯一靠谱的方案是先将对象复制到堆上,然后再复制到调用者的栈里。 C++中有一类特殊的优化,叫做对象返回优化。当编译器察觉到你需要返回栈上对象时,那么编译器会直接获得调用者栈里的对象地址。这样可以避免两次的拷贝过程。然而,如果没有对象返回优化(或者没有识别出来),那么就需要两次复制以保证正确性。而C++里,默认的复制过程是内存拷贝。 对于很多对象,内存拷贝是错误的行为。例如字符串,一种字符串的加速方法叫做共享内存字符串。两个字符串对象会共享一个内存块,以避免重复内容的开销。直到其中一块需要修改时,复制才真的继续。对于这种情况,直接拷贝会明显的导致错误。因此C++有一种特殊的构造函数,叫做拷贝构造。 在拷贝构造的时候,调用是由C++隐式发生的,你根本没有先构造,再调用的机会和权力。因此,试图分离构造在技术上不可行。 隐式转换是另一种情况。当你传递的参数和实际被赋值对象的类型不一致时(例如调用了某个函数,其参数类型不一致),C++会试图将你的对象转换为目标对象。如果是内部类型,这个被称为内部隐式转换。unsigned char可以被无错的转换为unsigned long,这个大家都知道。但是如果是对象,转换行为就需要由构造函数定义,这个叫做隐式转换构造函数。 另外,隐式转换也是OO中的一大问题。我强烈建议你用explicit禁用所有隐式转换,改为显式转换。这会费一点事,但是却可以避免很多问题。 分离构造/析构的邪恶之处 ZMQ的作者曾经吐槽过这种在构造/析构之外再定义初始化/清除代码的努力。他的观点是,如果万一在构造函数中加入了代码,会引起半构造现象。为了解决这个问题,会使得整个类带上状态。我在上面已经假定这件事情不会发生了,否则代码会更加复杂,问题也更加严重。 二次异常 是的,你不应当在异常处理代码中抛出异常。当然,这里的异常指的是你的异常处理代码不应当发生异常。经过逻辑判定,当前的异常应当由更上层处理的情况不在此列。 如果在异常处理中抛出异常,很可能导致的结果就是异常处理没有完成。而未完成的异常处理会发生什么问题,那只有天晓得。这个在任何带有异常系统的语言中都是成立的。

面对对象的吐槽——类型之殇

Oct 29, 2012 - 1 minute read - Comments

继承之殇 讲继承问题,我们首先得定义什么是继承(inherit),他是用来干吗的。 所谓继承,就是当两种实体,满足其中一种必然全部都满足另一种的定义(is a)。一旦构成继承,可以带来以下好处(简单起见,我们直接就管这俩实体一个叫派生类一个叫父类): 派生类具备父类所有已经实现的方法,毋须再实现一遍——除非需要重写(override)。 派生类可以当作父类使用,凡是使用父类的地方给与派生类也对。 继承的最主要作用,是用于复用(reuse)。 内涵和外延 形式逻辑里面有一句话,内涵越大,外延越小。在继承上,如果我们严格按照定义来做,会发生很反人类的事情。因为类的定义是依赖于内涵的, 我们还是看平行四边形,长方形和正方形的例子。我们用两边长度,夹角来定义平行四边形。然后如何定义长方形?夹角为pi/2。然后如何定义正方形?两边长度相当。 不知道你是否看出了问题。是的,按照正统来定义,数据的约束只会越来越多。因为派生类必须是(ISA)父类,因此父类的约束必须全部满足。我们接着上面的例子,我们为平行四边形定义一个方法,设定夹角大小。那么在长方形中,这个方法如何处理?一旦用户调用方法设定夹角大小,必然会破坏长方形定义,因此这个方法只能重写抛错。 为什么?从逻辑的本源来说,平行四边形是“两组对边分别平行”,并没有说夹角的事情。到长方形的时候才说,长方形是夹角为90度的平行四边形。显然,长方形是不能设定夹角的。因此,我们要么承认,不是每个平行四边形都可以设定夹角的,例如长方形不行。要么承认,每个平行四边形都可以设定夹角,长方形不是平行四边形。显然,后者违背逻辑,我们只能得出结论,不是每个平行四边形都可以设定夹角。 同样,正方形的例子也说明,不是每个平行四边形都可以设定两个分离的边长。如果以此标准来定义类,那么必然得到的是正确而无用的逻辑玩具。平行四边形没有夹角,我们就不能定义面积计算的函数,也不能——基本什么都不可以。更过分的是,我们还不能定义两个分离的边长,因为定义并没有告诉我们,边长一定不等。照此下去,我们除了一个空空荡荡的“平行四边形”这个名字外,什么都定义不下去。 为了解决这个问题,实践中,我们采取的都是,平行四边形是可以设定夹角的,然后对特例做抛错处理。这其实在本质上就违背了继承的原初意义。 继承和聚合 继承的另一个容易混淆的地方,就是分不清继承和聚合。 其实从逻辑上说。继承和聚合根本就不是一回事情。例如你有(have a)一条狗,你可以让狗做任何狗可以做的事情,例如追猎物。我们可以说,你可以做的事情和狗没有区别,所以——你就是(ISA)一条狗?! 傻子都不会弄错其中的区别! 我们说,如果一个东西看起来像鸭子,叫起来像鸭子,走起路来像鸭子,我们就可以当他是一只鸭子,说的是弱类型语言。而且我们只能认为,我们不知道那个东西是什么(这是弱类型的特点),总之可以当他是一只鸭子用。但是这不代表那个东西就是一只鸭子,他也可以是鸭子的代理人,或者拥有一只鸭子。在静态类型语言中,为了复用就不管三七二十一,直接声明PNG图像是一种BMP图像的——这绝对是逻辑上错误的行为。 然而,你自己数数你在代码里面犯过多少次错? 多重继承 继承本身的问题我们先不说,我们再说一个很常见的问题——多重继承。 既然我们说,只要一种满足ISA谓词判定,就可以认为是继承。那么理论上,我们就不能否决双重继承。例如我们定义了平行四边形,又定义了中心对称图形。那么长方形就同时是(ISA)这两者。从逻辑关系上,我们说长方形可以合法的继承两者。 但是如果我们真的在程序内设定将长方形继承两者,马上会引起一连串的问题。 当多重继承发生冲突时 首先第一个是继承冲突。即当两个父类都具备同一个方法的时候,对派生类做方法调用会发生什么行为? 肯定不能只调用一个,这会因此另一个父类的方法间发生内在不一致。这违背了继承的好处2。 也不能两个都调用。两者的先后次序可能引发逻辑问题,因此先调用谁都是错误的。而且函数还有返回值问题——你返回谁的返回值呢?如果多值返回合并,这和函数原始的定义又发生了悖离,从而又违背了继承的好处2。 因此,我们只能宣布这是个错误。 既然是个错误,鉴于类间函数可能存在的内在联系,其他继承的函数也未必能够正常使用。 你看,明明是合法的多重继承,居然造成了不可复用的结果。这就是继承冲突。 菱形继承 如果说继承冲突还是一个比较好考虑的问题的话,菱形继承就是一个让人吐血的东西了。 所谓菱形继承,就是两个父类继承同一个基类。在这种情况下,对父类的调用会间接转到基类上。那么,基类的函数会调用几次呢? 继承冲突的几种解法 所有冲突的函数,父类必须都无实现。 不得多重继承。这是很扯淡的,不过也是大多数时候的做法。我的编程指南之一就是——在C++中,任何时候都不要使用多重继承。 使用其中一者。python是个典型的使用其中一者的例子,具体使用的按照继承编写顺序展开成MRO次序决定。然而这直接违背了继承类是(ISA)父类的定义。因此不要以为在python中,继承后总是没问题的。有的时候可能会出现继承后不能正常工作的情况。 强制用户解决。要求用户必须人工定义函数,解决继承冲突的问题。从逻辑上说,如果用户定义的函数可以同时兼容于两个父类,就可以彻底化解多重继承冲突问题。然而杯具的是,很多时候在逻辑上,继承冲突是无解的。 区分接口和继承 父类没有实现冲突的函数,那么派生类中就不必纠结于调用谁的问题了。但是这引发了另一个问题——这就无法复用了。作为这一解法的极限,java不允许多重继承——除非继承的父类都是没有实现的类。这其实不是继承,而是实现(implement)接口(interface)。 接口编程是一个很有道理的东西,COM里面大量着重于接口。但是接口也有自己扯淡的地方——接口是一个编写期的东西,他最大的用途就是编译期类型检查。接口并不能复用(reuse)代码。如果你有一个接口,叫做平行四边形。里面有个方法,用于计算平行四边形面积。然后你实现了长方形和正方形——那么杯具来了,你需要在两个里面通通实现一遍这个方法,即使他们基本没区别。 当然,接口本身的好坏各有评价。你看,接口的唯一作用,就是声明类提供了某些函数。当我们对方法传入一个新的类的时候,我们必须将新的类也实现一下接口——哪怕这个类其实已经实现了这些方法。只要不实现接口,方法就不认可。这是强制编译器类型检查(静态类型语言)的基础。因此一般来说,静态类型语言,使用接口。动态类型语言,duck typing。

高考和大学的选择

Oct 28, 2012 - 1 minute read - Comments

背景 最近很多家长在为了高考的事情争。非京籍家长要求北京市教委给予外地孩子公平待遇,京籍家长认为北京本地教育资源有限,应维持现有状态。 另外,我在weibo上和一个交大的特招生吵,他认为特招生deserve大学(原话),我认为归根到底是个利益问题。 OK,以上是背景。 大学是一个什么? 中国的大学扩招比例是所罕见,其中有一个很大的因素是因为各种家长都认为孩子应当上大学。上过大学的孩子就跳龙门了。在我的观点来看(当然,我也有很多佐证),上大学不是跳龙门,谁上什么大学才是。我的一位朋友,是从偏远的农村来的,他们那里有个有趣的现象。凡是没上大学,高中毕业就工作的,家里多半都盖起了小楼,娶了媳妇,在本地工作。上了普通大学的,一般都还住在很旧的老房子里面,孩子在远方的城市,没结婚,刚刚还完,或者还在还学校的贷款。而上了一流大学的,则家里又比较富足,有很大一部分家长直接去了孩子所在的城市,很可能这些城市甚至不在中国。最后一种是否幸福,如人饮水冷暖自知。但是我要说的是,为什么会产生这样的分层变化? 关键是大学是什么样的一个东西。 我们为大学付钱,大学给我们各种资源。当然,好不好,值不值,就是一个很关键的问题。很多大学并没有资源,或者说没有足够的资源。这样的大学就是来赚你的钱的。中国人千年以来都认为,学而优则仕,念书是能当官的。于是再穷不能穷孩子,都变着态的往大学送。这种人的钱不赚赚谁的?自然,孩子没有得到好的教育,工资和普通高中生拉不开差距,家里又很是交了一笔学费,不穷你穷谁? 这年头家长也不都是傻子,我们从来不为进入这种大学而争论。问题是,那些好的,能够给你资源的学校,谁在进?谁应当进? 谁进大学? 首先你应当明白,大学并不是象牙塔,他不天然是为了让好的学生变得更强而出现的。大学的出现,是他的设立者为了他的理想而建的。如果他的理想碰巧是,让好的学生变的更强。那么,是的,这也是一种可能。但是你应当明白,另一种可能性是,为各种有钱人世家教育子女,让他们的子女比别人的子女更强。我得说,这也是一个可能。 那么中国的大学是什么? 刚刚我们说了,一种大学是赚钱。我们从不争论谁应当进去挨一刀。至于那些能带给你什么的大学,一般都是国家掌握的。这些大学创立的目标是什么,他们选学生的标准就是什么。国家运作这些大学是为了什么呢? 还是赚钱。中国的大学培养了相当大的一批高级产业工人,这是个不争的事实。有产业工人才有GDP啊。既然是培养产业工人的,那么招人的标准就相当明白——增加产业工人培养的成功率。一般来说,好的学生比较容易培养成优秀的产业工人。但是这不是正向相关的,里面总要附加点什么。首先,政治必须正确。政治不正确的学生培养出来是危险品。其次,学生要足够好,不够好的不培养难度太高。于是你就知道高考是怎么一回事了。首先,你必须政治正确,否则根本不会有高考机会。其次,考试。 谁应当进大学? 当然,我们是没有办法对国家施加影响力的。我们只是从理论的角度说。如果全民可以投票,来决定进入大学的制度,谁应当进去? 当然你也应该想的到,谁应当进大学是个政治问题,而不是技术问题。本质上,决定谁进大学就是决定谁在将来拥有利益。这是一个利益之争,永远是。我和那位交大的特招吵,就是因为他觉得特招理所应当进大学,因为他们deserve。而且他不觉得这和利益有什么关系。我不觉得谁是理所应当进入大学的,大学不是赛跑,跑赢拿奖品。大学不是养鸡场,养出一批换一批。所以我觉得以考试为基础来决定谁进大学本身就是个问题,高考有问题,特招当然也有问题。 但是我们没法脱离高考,你们都知道为什么。既然谁进大学是个政治问题,那么一种无论听起来多么合理,却看起来不见得公平,操作起来更可能有黑幕的方案,是绝对不可能上的了台面的。而目前,问所有人,觉得还算公平的方案,就是高考,大家一碗水端平。即使是高考这么看似公平的方案,也有无数不公平的细节在争吵中。特招生多了,平衡发展的学生家长就要有意见。取消特招了,偏科生家长就要有意见。开放对地区的人数限制,招生比例高的城市家长有意见。不开放,外地家长有意见。你知道的——无论社会民主还是不民主,这是永远的利益之争。所谓民不民主,只是这个问题是由大家投票解决,还是领导拍板解决。 关于地区之争,我首先引述一篇文章民主的细节 – 谁有特权上大学,文章需要翻墙,我引述如下。 看了刘瑜《民主的细节》,我没觉得美国像天堂,但中国真的很像个炼狱。 一个简单的例子。 刘瑜引用罗尔斯《正义论》的解释说:只有当你不知道自己可能是谁时,才能想清楚什么是正义。“他有一个术语,叫“无知之幕”,也就是一个人在对自己的社会处境暂时失明的情形。一个站在“无知之幕”后面的人,既可能是比尔盖茨,也可能是一个非洲饥民。如果你觉得正义就是杀光富人瓜分他的财产,万一“无知之幕”一拉开,发现自己就是比尔盖茨,恐怕你会后悔得一头撞死。如果你觉得正义就是WINDOWS 2000卖5000美元一套,万一“无知之幕”一拉开,发现自己其实是非洲饥民,估计也要捶胸顿足。” 然后,她举了美国人关于“谁有特权上大学”的争论。自60年代的平权运动始,许多人主张给黑人和妇女在入学上优惠,以补偿之前遭遇的歧视,叫做“补偿性正义原则”;但是矫枉过正的结果,产生“逆向歧视”,70年代中,一个叫巴克的白人学生不满自己在考大学上屡屡败给各方面都比自己差的黑人学生,上诉最高法院,法院裁定对黑人学生实行定额制是违宪的,但原则上仍支持平权行动,这个叫“程序性正义原则”。后来的40年,美国人民一直在如何协调这两种正义间苦苦挣扎与思索……中国呢? “好了,终于可以回到咱们开头提的那个问题了――来自火星的你,被扔到大城市、内地、边疆的可能性各三分之一,你会如何设计高考分数线方案? 你可能会说:三个地方分数线一样嘛!大家公平竞争嘛! 你也可能会说:让边区分数线低一点,其他两个地方一样,因为那些地区贫穷,教育条件有限,人家北京上海的孩子用电脑打字,俺们这里还是凿壁借光呢。 你还可能会说:我选择让北京上海分数线低一点,其他两个地方一样。因为……因为……咦? 你们地球夏天真热啊? 我们知道,这三种选择,第一种叫“程序性正义原则”;第二种叫“补偿性正义原则”;第三种,姑且称之为“夏天总是很热”原则吧。” 元芳你怎么看? 大学值不值得进? 首先我给光知道赚钱的大学定个性吧。这种大学您就别去了,去了等于白去。 其次,对于指望搞研究的,真的对学术有兴趣的。我的建议是你尽快找好老婆,尽快走,出国读研究生。中国也是干活,外国也是干活。人家待遇比中国高不说,还没P事,能让你专心做研究。国内但凡是个院校都是一堆P事,搞行政的时间长过做学问的时间。 最后,您要是指望出人头地的——那就吵吧。。。 我的立场? 我其实是特招的受益者,当年我是物理竞赛的二等奖。但是,那毕竟是过去了。现在我物理方程都不记得几个了,要做计算更是要依赖程序了。而且——我也不会再高考了。 所以其实个我对高考的立场就是个无知之幕——谁知道将来会生儿子女儿,是偏文科还是理科。我连将来会不会在中国,想不想生都不知道呢。 所以我在偏科问题上的立场是随大流的,一般来说,强到特招的概率总比平均分数高的概率来的低吧。至于地区之争,我得说,原则上我支持向贫困地区倾斜。但是行动上支持北京和上海保持本地人低分数线。 听起来很悖论,但是这是诚实的想法。

cython编译细节

Oct 25, 2012 - 1 minute read - Comments

两点简述: 可以使用cython –embed来编译一个pyx,生成带main的代码,然后用gcc直接编译过去。大概样例是这样的: cython –embed $^ gcc $(shell python-config –includes) $(shell python-config –libs) -O2 -o $@ $^ pyx的文件名会被转换为变量,所以所有在变量中不应当出现的符号也别出现,例如-。