双木成林:喋喋不休

I leave no trace of wings in the air, but I am glad I have had my flight.

Archive for 二月, 2011

Python闭包研究

without comments

此文来自本人原JavaEye博客,原文地址

其实很早以前就想写这么一篇文章了。一直没有机会。正好今天和同事讨论Python闭包的问题,趁着没遗忘赶快记录下来。以下代码运行的Python版本是2.5。

问题还是那个很经典的问题:如下代码会抛一个错误

def foo():
    a = 1
    def bar():
        a = a + 1
    bar()
    print a

错误则是:

UnboundLocalError: local variable 'a' referenced before assignment

原因分析,直接上dis模块解析bar的汇编代码。得到以下结果:

12           0 LOAD_FAST                0 (a)
             3 LOAD_CONST               1 (1)
             6 INPLACE_ADD
             7 STORE_FAST               0 (a)
            10 LOAD_CONST               0 (None)
            13 RETURN_VALUE

可以看到,造成这个异常的结果是LOAD_FAST没有找到local变量。STORE_FAST语句的作用是绑定一个local变量。那么在储存变量之前就先去读,当然是会报错了。可是,明明是a = a + 1。而按照赋值语句先执行右边的规律来看,他应该先去外层的a那里读取值,然后再新建一个local的名字a,把值赋给local的a啊?

原因暂且放下,先看一段能正常执行的代码。

把前面代码中的a = a + 1改成b = a + 1。反汇编得到以下代码。

13           0 LOAD_DEREF               0 (a)
             3 LOAD_CONST               1 (1)
             6 BINARY_ADD
             7 STORE_FAST               0 (b)
            10 LOAD_CONST               0 (None)
            13 RETURN_VALUE            

果然按照原来设想的一样,a在这个地方变成了LOAD_DEREF,变成了访问外围的值,然后和1想加以后,储存在一个本地的变量b里面。

正确的程序和错误的程序的差别就是,错误的里面,a是赋值语句的左边。

这看起来不经心的一个差别,会不会是原因呢?答案是YES!看python的PEP227中的一段话。

If a name is bound anywhere within a code block, all uses of the name within the block are treated as references to the current block.

这句话非常拗口。我换一种通俗的方式来解释一下。模拟一下python编译器的行为。首先编译器看到了a = a + 1这句话,发现这是一个赋值语句。先检查右边,遇到了一个名字叫做a的东西。a是什么?编译器问自己。会不会是一个局部变量?于是编译器就傻傻的找到规则,规则表说:如果一个名字出现在参数声明,赋值语句(左边),函数声明,类声明,import语句,for语句和except语句中,这就是一个局部变量。ok。编译器从头到尾一看,a就在一个赋值语句的左边,那么a是一个局部变量没跑了。于是生成一条记录LOAD_FAST 0。你是局部变量,让你运行快一点。接着,分析完右边分析左边,赋值语句左边一定是一个局部变量,简单,你就在0号位置把,直接生成STORE_FAST 0,把栈顶的值给你。编译器顺利的编译结束。下面轮到虚拟机运行了。虚拟运行到这个语句就犯糊涂了,叫我LOAD_FAST 0。可是0里面什么东西都没有啊。我擦勒。只好报错了。

而第二段代码为什么能够正确执行呢?其实就是因为,编译器在整个代码块里面没有发现有绑定名字给a,也没有发现a是一个global对象,所以,就生成一个LOAD_DEREF 语句,告诉虚拟机,a不在这个里面。到别的地方去找他。

那么这个别的地方究竟是什么地方呢?如果python没有这个一定是局部变量的规则,是不是就能修改了呢?

我们继续分析。

先找到LOAD_DEREF的定义是什么?查看dis这个模块的说明,里面有如下的文字:

LOAD_DEREF(i)
Loads the cell contained in slot i of the cell and free variable storage. Pushes a reference to the object the cell contains on the stack.

大意就是,加载cell[i]到栈顶。cell是一个什么?这时候,联想到Python的CodeObject里面有一个属性叫做co_cellvars.会不会和这个有关?

查了文档以后发现如下定义

co_cellvars is a tuple containing the names of local variables that are referenced by nested functions;

被嵌套的函数引用的局部变量?好奇特的说法啊。真这么神奇?执行下列代码。

def foo():
    a = 1
    def bar():
        b = a + 1
    print 'bar cellvars:', bar.func_code.co_cellvars  

foo()  

print 'foo cellvars:', foo.func_code.co_cellvars

执行结果是:

bar cellvars: ()
foo cellvars: ('a',)

还真是的,a在bar中引用了,所以被加入到cellvars里面。*需要注意的是,他这里只是把名字放到了cellvar中,也就是说,这个闭包中的对象,依然只是一个引用而已。当这个bar调用的时候,是会顺着引用找到真正的值的。而如果真正的值被修改,在所有的bar里面都会体现。

这个过程是怎么加入的呢?反汇编一下foo的代码:

2           0 LOAD_CONST               1 (1)
            3 STORE_DEREF              0 (a)  

3           6 LOAD_CLOSURE             0 (a)
            9 BUILD_TUPLE              1
           12 LOAD_CONST               2 (<code object bar at 0x48f458, file "test.py", line 3>)
           15 MAKE_CLOSURE             0
           18 STORE_FAST               0 (bar)
           21 LOAD_CONST               0 (None)
           24 RETURN_VALUE

看到奇特的STORE_DEREF, LOAD_CLOSURE, MAKE_CLOSURE指令。

这三个指令的作用分别如下:

STORE_DEREF(i)
Stores TOS into the cell contained in slot i of the cell and free variable storage.

LOAD_CLOSURE(i)
Pushes a reference to the cell contained in slot i of the cell and free variable storage. The name of the variable is co_cellvars[i] if i is less than the length of co_cellvars. Otherwise it is co_freevars[i - len(co_cellvars)].

MAKE_CLOSURE(argc)
Creates a new function object, sets its func_closure slot, and pushes it on the stack. TOS is the code associated with the function, TOS1 the tuple containing cells for the closure’s free variables. The function also has argc default parameters, which are found below the cells.

看来是编译器发现foo函数里面有一个嵌套的bar函数以后,就把在bar中引用的局部变量a放到一个cell当中,然后将所有的对象都生成成一个tuple,赋值给bar这个funcobject的func_closure。

为了查看神奇的效果,写下面一段代码运行一下看看:

def foo():
    a = 1
    def bar():
        b = a + 1
    return bar  

b = foo()
print 'bar func_closure:', b.func_closure

如果这程序按照猜测的结果运行,那么将会返回一个cell的tuple。执行结果如下。

bar func_closure: (<cell at 0x454690: int object at 0x803388>,)

果然不出所料。那么func_closure的作用在文档里面怎么描述呢?

func_closure None or a tuple of cells that contain bindings for the function’s free variables. Read-only

看来这个东东涉及到的是Python的名字查找顺序的问题。先local,再闭包,再global。

详细内容可以参看PEP227里面有这么一句话。

The implementation adds several new opcodes and two new kinds of

names in code objects. A variable can be either a cell variable
or a free variable for a particular code object. A cell variable
is referenced by containing scopes; as a result, the function
where it is defined must allocate separate storage for it on each
invocation. A free variable is referenced via a function’s
closure.

The choice of free closures was made based on three factors.

First, nested functions are presumed to be used infrequently,
deeply nested (several levels of nesting) still less frequently.
Second, lookup of names in a nested scope should be fast.
Third, the use of nested scopes, particularly where a function
that access an enclosing scope is returned, should not prevent
unreferenced objects from being reclaimed by the garbage
collector.

相信看到前面func_closure是readonly,大家一定非常失望。看看别的语言的实现如何。

javascript的版本1。

function foo(){
    var num = 1;
    function bar(){
        var num = num + 1;
        alert(num);
    }
    bar()
}
foo();

这个版本会报NaN。。说明Python的问题Javascipt也有。

那如果说num不声明为var呢?

function foo(){
    var num = 1;
    function bar(){
        num = num + 1;
        alert(num);
    }
    bar()
}
foo();

正确提示2.。

要是Python也有这样的机制好了。。

令人高兴的是,python3里面终于改观了。从语法到底层全都支持了(貌似是一个性质)。

语法上加上了nonlocal关键字。

def foo():
    a = 1
    def bar():
        nonlocal a
        a = a + 1
        print(a)
    return bar  

foo()()

正确返回2!!

底层加上了可爱的下面两个函数。

PyObject* PyFunction_GetClosure(PyObject *op)¶
Return value: Borrowed reference.
Return the closure associated with the function object op. This can be NULL or a tuple of cell objects.
int PyFunction_SetClosure(PyObject *op, PyObject *closure)
Set the closure associated with the function object op. closure must be Py_None or a tuple of cell objects.
Raises SystemError and returns -1 on failure.

终于可以操作闭包了。哈哈哈哈。。

其实说到最后,如果python中有种机制能支持匿名代码块就好了。嘿嘿。到此结束。

Written by linluxiang

二月 23rd, 2011 at 7:13 下午

Posted in Python,技术

MacOSX下Python2.5版本的locale的编码问题

without comments

此文来自本人JavaEye博客,原文地址

今天更新mercurial的时候遇到了一个问题。

执行hg,结果报错:LookupError: unknown encoding: x-mac-simp-chinese

想到这个问题我以前在用django的时候碰到过,原来以为是django的问题,现在才知道原来是普遍的python的问题。

去hg的源代码里面minirst.py里面看了一下,发现是直接调用mercurial的encoding函数的encoding这个变量。

找到encoding.py里面,

try:
    encoding = os.environ.get("HGENCODING")
    if not encoding:
        encoding = locale.getpreferredencoding() or 'ascii'
        encoding = _encodingfixers.get(encoding, lambda: encoding)()
except locale.Error:
    encoding = 'ascii'

原来是locale这个模块搞的鬼。。

去locale.py里面看了一下,发现以下代码:

if sys.platform in ('win32', 'darwin', 'mac'):
    # On Win32, this will return the ANSI code page
    # On the Mac, it should return the system encoding;
    # it might return "ascii" instead
    def getpreferredencoding(do_setlocale = True):
        """Return the charset that the user is likely using."""
        import _locale
        return _locale._getdefaultlocale()[1]

尝试执行了一下,直接返回了’x-mac-simp-chinese’

为了了解正确的结果,python2.6 -c ‘import locale; print(locale.getpreferredencoding());’返回结果’UTF-8′.

而UTF-8正是我设置的LC_ALL和LANG的结果。

看来是这个_locale模块搞得鬼。不过_locale啊。看名字就是c写的。为了省力。直接把

if sys.platform in ('win32', 'darwin', 'mac'):

改成了

if sys.platform in ('win32'):

然后顺手搜索了一下locale.py中的_locale,把所有的都改了。

执行hg,一切正常。

顺带搜了一下这个问题python的buglist里面有没有,果然看到了。http://bugs.python.org/issue1276。不过略看了一下,发现python2.5.x被无情的忽略了。看来只能自己hack了。:)。

Written by linluxiang

二月 23rd, 2011 at 6:58 下午

Posted in Python,技术

Tagged with ,

Python中globals对象的回收顺序分析

without comments

此文来自本人原JavaEye博客,原文地址

先提示,本文需要一定的python源码基础。许多内容请参考《python源码剖析》。下面切入正题。

今天在群里有人问了一个问题。形如如下的一段程序。

class person:
    sum = 0
    def __init__(self,name):
        self.name=name
        person.sum += 1  

    def __del__(self):
        person.sum -= 1
        print "%s is leaving" % self.name  

a = person('a')
a2 = person('a2')

这段程序的预期的执行结果应该是”a is leaving”和”a2 is leaving”。但是实际上却出乎意料之外,实际的执行结果如下:

a is leaving
Exception exceptions.AttributeError: "'NoneType' object has no attribute 'sum'" in
<bound method person.__del__ of <__main__.person instance at 0x4a18f0>> ignored

为什么引用的名字不同造成的结果会有这么大的差别呢?

分析表面的原因,是person这个引用被指向了None。那他是不是真的是None呢?

__main__.person
None
Exception exceptions.AttributeError: "'NoneType' object has no attribute 'sum'"
 in <bound method person.__del__ of <__main__.person instance at 0x4a18c8>> ignored

看来是真的变成了None了。

初步分析原因,应该是程序在执行结束以后,python虚拟机清理环境的时候将”person”这个符号先于”a2″清理了,所以导致在a2的析构函数中无法找到”person”这个符号了。

但是转念一想还是不对,如果是”person”符号找不到了,应该是提示“name ‘person’ is not defined”才对。说明”person”这个符号还在,那”person”指向的class_object对象还在吗?改变程序为以下格式:

class person:
    sum = 0
    def __init__(self,name):
        self.name=name
        person.sum += 1  

    def __del__(self):
        #person.sum -= 1
        self.__class__.sum -= 1 #1
        #print "%s is leaving" % self.name  

a = person('a')
a2 = person('a2')

#1就是修改部分,利用自身的__class__来操作。运行结果一切正常。

说明python虚拟机在回收的过程中,只是将”person”这个符号设置成None了。这个结论同时带来2个问题:第一,为什么会设置成None?第二:为什么”person”会先于”a2″而晚于”a”被回收?

先来分析第二个问题。第一反应是不是按照字母的顺序来回收?但是马上这个结论被推翻。”a”和”a2″都在”person”的前面。那么难道是根据globals()这个字典的key顺序来回收?执行一下globals().keys()方法,得到以下结果:

['a', '__builtins__', '__file__', 'person', 'a2', '__name__', '__doc__'];

看来的确是这样。

但是为什么是这样?要得出这个结论,看来只有去python源码中找答案了。

大家都知道,python代码在运行的时候,会存在一个frameobject对象来表示运行时的环境。类似于c语言的栈帧,也有点像lisp的函数的生存空间,看起来答案要从frameobject.c这个文件中去找了。

在frameobject.c中发现了一个函数:static void frame_dealloc(PyFrameObject *f)。看来解决问题的关键就在眼前。

在frame_dealloc里面截取了以下一段代码:

Py_XDECREF(f->f_back);
Py_DECREF(f->f_builtins);
Py_DECREF(f->f_globals);//1
Py_CLEAR(f->f_locals);
Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_exc_type);
Py_CLEAR(f->f_exc_value);
Py_CLEAR(f->f_exc_traceback);

原来减少了引用啊。。关于Py_DECREF这个宏,python源码里面的解释是这样的:

The macros Py_INCREF(op) and Py_DECREF(op) are used to increment or decrement reference counts. Py_DECREF calls the object’s deallocator function when the refcount falls to 0;

这么说来,我们就要去找f_globals的析构函数了。f_globals是个什么呢?当然是PyDictObject了。证据么遍地都是啊,比如随手找了一个,在PyFrameObject * PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,PyObject *locals)这个函数里面有一段代码:

#ifdef Py_DEBUG
    if (code == NULL || globals == NULL || !<span style="color: #ff0000;">PyDict_Check(globals)</span> ||
        (locals != NULL && !PyMapping_Check(locals))) {
        PyErr_BadInternalCall();
        return NULL;
    }
#endif

PyDict_Check。。。检查是否是Dict对象。好吧,此处略过,直接奔向dictobject.c看看里面的代码。

static void
dict_dealloc(register dictobject *mp)
{
    register dictentry *ep;
    Py_ssize_t fill = mp->ma_fill;
    PyObject_GC_UnTrack(mp);
    Py_TRASHCAN_SAFE_BEGIN(mp)
    <span style="color: #ff0000;">for (ep = mp->ma_table; fill > 0; ep++) {
        if (ep->me_key) {
            --fill;
            Py_DECREF(ep->me_key);  #
            Py_XDECREF(ep->me_value); #仅仅只是引用计数减一
        }
    }</span>
//以下略

哈哈哈。还真是按照key的顺序来一个一个清除的。

不过,怎么又回到了Py_DECREF啊?

看来最终解释这个问题要回到GC上面了。

其实从这个地方也可以看出第一个问题的答案了,为什么是None?

从上面代码可以看出,dictobject对象在析构的时候,仅仅只是将value的引用计数减一,至于这个对象什么时候被真正回收,其实是由GC决定而不确定的。也就是说为什么是None,是因为减一了以后,凑巧GC到了而已。

根据Python本身的文档。

Warning: Due to the precarious circumstances under which __del__() methods are invoked, exceptions that occur during their execution are ignored, and a warning is printed to sys.stderr instead. Also, when __del__() is invoked in response to a module being deleted (e.g., when execution of the program is done), other globals referenced by the __del__() method may already have been deleted. For this reason, __del__() methods should do the absolute minimum needed to maintain external invariants. Starting with version 1.5, Python guarantees that globals whose name begins with a single underscore are deleted from their module before other globals are deleted; if no other references to such globals exist, this may help in assuring that imported modules are still available at the time when the __del__() method is called.

Python不能保证_del被调用的时候所有的引用都有,所以,尽量不要overried类的del_方法。

到此结束。

Written by linluxiang

二月 23rd, 2011 at 6:51 下午

Posted in Python,技术

Tagged with

用命令行编译Objective-C程序

without comments

此文来自本人原JavaEye博客,原文地址

最近在看Objc的开发,虽然Xcode很好用,但是还是不太习惯这种大型的IDE开发。 于是自己尝试着在Terminal下面编译。 先是最简单的一段程序。

#import <Foundation/Foundation.h>  

int main(int argc, const char *argv[])
{
    NSLog(@"Hello, World!");
    return 0;  

}

直接 gcc helloworld.m,报错。

错误提示如下

Undefined symbols:
  "___CFConstantStringClassReference", referenced from:
      cfstring=Hello, World! in ccCxrkF8.o
  "_NSLog", referenced from:
      _main in ccCxrkF8.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

猜测可能lib的位置不对,于是find了一下“Foundation”这个framework的位置,用以下命令编译:

gcc helloworld.m -L/System/Library/Frameworks/Foundation.framework/

还是一样的错误。

无奈之下man gcc从头看到尾。又去google了一下gcc for macosx。发现有一个-framework的选项。尝试了一下。

gcc helloworld.m -framework Foundation

终于成功了

2010-04-22 19:11:11.643 a.out[8932:903] Hello, World!

总结一下:macosx下面,objc的系统调用都是集成在framework里面的,要想编译的话,就必须用-framework选项将framework包含进来才行。

Written by linluxiang

二月 21st, 2011 at 8:28 下午

Posted in iOS,技术

Tagged with

MacOSX中链接boost库

without comments

此文来自本人原JavaEye博客,原文地址

今天在编译一个用到boost_program_options的库的时候出现了一点点小问题。总是提示说找不到boost_program_options这个库。

去/usr/local/lib下面看了一下,发现原来改名成libboost_program_options-mt.dylib了。搜了一下名字中这个mt的意思。

原来是boost1.33的新特性,就是区分了某些lib是单线程或者多线程的。这个区分导致必须显式的指明使用的是多线程还是单线程的库。

自然mt就是指的多线程,st指的单线程。

改用-lboost_program_options-mt以后,编译ok。问题解决。

Written by linluxiang

二月 21st, 2011 at 8:19 下午

Posted in C++,技术

Tagged with

如何使用logger将错误信息输出到日志

with one comment

此文来自本人原JavaEye博客,原文地址

今天在做分析http错误请求处理的时候遇到一个问题:当发生500错误的时候,如何将错误输出到日志当中呢?

搜了一下python的doc,在logging模块中有不起眼的一行代码提供了解决方案。

mylog.error('error!', exc_info=True)

其中,mylog就是Logger实例,当exc_info设置为True的时候,logger就会自动的调用sys.exc_info()函数,将traceback的信息打印到日志中。

Written by linluxiang

二月 21st, 2011 at 8:12 下午

Posted in Python,技术

Tagged with

求职总结

without comments

此文来自本人原JavaEye博客,发表于2009年10月26日。原文地址

好吧, 求职总算告一段落了.

从13号参加第一场笔试, 到24号拿到网易游戏offer, 20天时间的经历, 算是人生一大笔财富了.以后可能就没有这样的机会去体验那种心情了.
写一点总结, 希望能够帮助仍然在水深火热之中的同学和朋友.

一. 职业规划

求职的头等大事, 就是树立一个详细完整有可行性的职业规划.

其实职业规划, 不能是等到求职的时候才去想, 它的作用应该是指导大学4年期间学习方向, 以及指导工作以后的研究方向, 甚至可以决定一生的奋斗方向.所以, 最好在进大学1-2年内就确定自己的职业规划, 为之去努力, 然后在求职的时候, 针对你想进入的公司简单修改.不过这就属于是求职技巧的内容了.

职业规划我个人认为应该包含以下几个部分.1.我想要从事什么行业. 2.我想进这个行业内的哪些公司. 3.我进这些公司以后想要从事什么岗位. 4.我进公司以后应该怎么奋斗. 以上几个部分, 必须有足够能说服自己的理由.说服自己是关键, 别人的意见可以作为参考, 但是一定要有自己的观点.

  1. 就拿我自己来说吧, 我想从事互联网行业, 因为互联网行业是中国目前发展最快的行业之一, 可能甚至没有之一.
  2. 我想进的公司有, Google:互联网老大, 不用多说;网易游戏:中国自主研发实力最强的游戏公司, 而游戏行业是互联网行业中产值最大的部分;百度, 阿里巴巴, 腾讯:中国互联网三强, 产值过千亿的公司.
  3. 我想要从事的岗位是基于Unix或者linux平台的服务器端网络程序开发.因为自己是Open Source的狂热爱好者, 而且类Unix平台的服务器的市场占有率相当高.网络, 是未来的发展方向, 我们的生活必将和网络紧密结合, 以后, 上网这个词一定会消失或者变成纪念, 因为我们无时无刻不在上网.
  4. 进入公司以后, 我希望在4-5年内做到部门leader, 哈哈!

提过了愿景, 实现情况就是, 我已经满足了前三点, 第四点, 我一定会努力去实现它的.

关于想进的公司, 其实google还是心中永远的向往, 可惜google不招人啊…囧..剩下4个, 网易游戏和阿里巴巴的offer到手, 百度和腾讯被鄙视..恩.可能是骂百度太多败了不少人品吧..呵呵..

在面试中, 总会被不停的问到有关职业规划相关的问题.这个时候, 一个清晰明确的规划就十分重要了.当然, 要针对每个公司修改, 不能把面试google的回答去回答百度的问题, 那样不被刷才怪. :)

二. 简历

简历有两种, 一种是在网上招聘系统填写的在线简历, 另一种是自己精心制作的, 面试时候面试官会盯着问的简历.

网上申请时在招聘系统内填写的简历, 其实个人感觉不是很重要, 主要是系统用来刷一帮笔试名单用的. 面试官不会看.而且, 就算这一关被刷了, 也可以去霸王笔么. :)

真正重要的, 是自己精心制作的简历. 简历最好大概2-3页大小, 包括个人信息, 获得奖项, 项目经验等等. 对于偏技术类的岗位, 我认为, 项目经验是最重要的, 因为面试官会不厌其烦的问你项目相关的事项, 而且几乎全部的面试官, 都喜欢问你最不熟悉的那个项目. :)

关于写什么项目, 其实非常重要. 因为2-3页纸有限, 对于项目多的同学来说, 可能根本不够. 取舍就显得很重要了. 尽量把重要的, 与你所应聘的职位相关的项目写上去, 当然, 写上去的项目, 你的内心必须对这个项目无比熟悉. 因为这个项目就是一个大大的陷阱, 是我们设下的陷阱, 让面试官跟着我们的思路问问题的陷阱. 如果陷阱把老虎引过来了, 自己却没有办法制服老虎, 那么被老虎吃了就是唯一的下场. 当然, 风险大收益也大, 如果这个问题答的好, 面试官那里的得分可是会大大增加的.

另一点关于简历项目的要点就是, 你的简历项目, 必须能够证明你能在一个团队内良好的与同事合作. 也就是说, 一个人参与的项目, 除非巨牛, 不然少写, 突出团队项目就要多写, 而且注意, 不是突出自己在团队中的重要性, 而是作为一整个团队所获得的成绩的项目.

其他关于简历的注意事项, 不一定一个公司一个简历, 如果你把自己所有的精华都写在一个简历里面, 那么拿着这个简历就可以了. 当然, 简历里面不能有错别字啊. :)

三. 笔试

其实我个人还是比较恐惧笔试的, 因为我不知道笔试要考什么内容, 而面试我大致清楚面试官会问什么, 呵呵.

笔试题大多有以下几种:

  1. 专业题, 计算机相关专业, 就是数据结构与算法, 树, 链表, 字符串操作居多, 外加一些操作系统, 计算机网络的基本知识. 当然,如果你应聘的是dba, 或者sa, 就有一些特殊的题目, 比如sql啊, unix命令之类的. 语言基本上就是C或者C++, 很多招聘Java的职位, 考的试卷也是C++的.这个始终让我觉得很囧.
  2. 能力题, 能力题大致包括逻辑题, 智力题等等. 逻辑题比较容易做. 智力题的话, 也只有题海战术了, 网上多搜搜这个公司前几年的考题就一般能够找得到.

其实笔试考的好坏也不是很重要, 如果笔试没有进, 那么你就得把笔试题记住, 然后大胆的去霸王面把! 因为霸王面一定会问你, 你考试的时候觉得做的不好的是什么题? 回来以后有没有考虑过怎么做?

四. 面试

面试我想分开来谈, 呵呵, 因为面试太多, 不同的面试方式都不一样.

不过有几个词是通用的: 诚信, 团队, 本色!

另外, 和面试官要不亢不卑, 态度谦和. 多说谢谢是不会错的.

另外, 面试某公司面前, 应该去了解这个公司的用人原则, 或者他们创始人的名言之类的, 这个会在一定的时候, 收获奇效哦!

五. 专业面试

专业面试, 关键点就是把面试官吸引到你希望他问的方向上面.

当然, 如果你不知道面试官会问你什么问题的话, 那就应该找一家公司做试验, 看看他们的面试官会问你什么. 其他的公司大多数是差不多的. 对我自己来说, 可怜的千橡就成为了我的试验品了.

从试验公司知道了会问的问题以后, 回来立刻投入准备, 必须马不停蹄. 如果面试官问的是基础知识, 回来立刻背概念, 基础知识; 如果问项目, 回来把项目代码看一遍把关键技术要点思考一遍. 当然, 最好2个都做.

事实上这一招很有效, 在整个求职过程中, 我被问了4遍TCP三次握手.. – -!

然后要做的就是每次面试以后, 不断的复习这次面试过程中没有回答出来的问题, 强化记忆. 比如我第一次遇到select和epoll的区别的问题时候没有回答出来, 回来以后详细的看了一下, 后面的面试里面又被问了3次, 就回答的很好了.

最后要强调, 基础是非常重要的, 因为靠准备是不能准备全部的知识的, 只能平时积累了.

面试官最喜欢做的事情, 就是抓着你支支吾吾回答不清的地方问. 所以遇到自己不熟或者忘记的地方, 一定要果断的说, 不知道或者不清楚. 当然, 如果他问的问题全部都不知道, 可能就应该考虑自己知识点是否存在漏洞, 在面试回来就应该好好修补一下.

关于专业面试, 其实功夫还是在场外啊, 面试最重要的也就是把面试官引导到你熟悉的地方, 其他能做的, 就是好好的回答问题吧.

六. 群面

群面, 又叫做无领导小组讨论, 或者是AC面. 就是一帮人坐在一起讨论一个问题, 最后得出结论. 考核组员的合作能力, 处理争执的能力, 以及逻辑思维能力, 细心程度等等. 考察的东西相当的多.当然注意点也非常的多.

原来还以为面技术的不需要过群面, 结果网易游戏还是给了我这个机会让我体验了一把群面的感觉.

关于群面的考核目标里面, 我没有说结论的正确与否也是考核的目标. 因为很多讨论根本就是没有正确结果的, 所以无结果, 或者结果不能让所有人满意是很正常的情况. 大家也不要对群面的结果斤斤计较. 群面, 最重要的是过程.

群面的技巧, 按我的理解, 可以总结为以下几点, 不当出头鸟, 不当末尾兵, 说话平和, 面带微笑, 多多的鼓励人, 不要多说一句废话.

不当出头鸟是因为, 讨论的过程之中, 大家会自发的形成一个小团队, 团队必然有leader, 当然, leader不是这么好当的, 因为leader是面试官关注最多的人, 任意一点表现的不好, 都会导致得分的下降, 甚至不能进入下一轮面试. 所以, 如果觉得自己能力有限, 就不要当leader, 否则必败无疑.

不当末尾兵, 是指尽量不要当总结陈词的人. 因为总结的人对于其总结能力, 逻辑思维能力的要求非常高, 如果因为总结而导致逻辑错误, 或者说话支支吾吾, 等等状况的出现, 都会让面试官给你减分.

说话平和, 是说语气要平缓, 不急不躁, 让人听清为准, 不能像吵架一样语无伦次.

面带微笑, 这个是基本常识了.

多多鼓励人, 就是比如当主持人说, 请大家评价一下某位同学, 或者说, 大家评价一下这次面试的时候, 一定要说好的地方. 比如, 这位同学能力很强, 或者说, 某某同学时间控制的很好, 用正面的眼光去看人.

不说废话, 不是说不要当说话最少的人, 而是说说的话必须在点子上. 当整个队伍都是强人的时候, 很可能你的观点是和别人相同的, 这个时候可能就会觉得很尴尬, 自己的话都被别人说了. 那么, 着眼点就可以放在其他地方而不是结论的得出上. 比如, 提醒大家一些细节的小地方等等. 或者, 调控场面气氛等等.

群面其实还是比较能够测试一个人的性格特点的, 面试的时候什么状况都会出现, 大家记得保持冷静就是了.

最后再次强调, 谢谢不离口, 时刻保持微笑.

七. HR面

HR面往往是面试的最后一关, 一般来说, HR只有否决权没有决定权, 所以只要不犯大错误, 基本上offer就到手了.

HR面试的问题, 一般就是你的人生规划, 你最得意到事情, 最失意到事情等等, 大家到应届生网站上面可以查到一堆到问题. 所有问题到回答总结起来就是一句话, 千万不要和HR交心(借用浩哥的原话, 嘿嘿).

因为HR毕竟还是代表公司的, 如果这个时候放松了警惕, 说了一些不该说的话, 最后失意的也只有自己.

所以, 谈论自己缺点等等的时候, 就放一些无关紧要的缺点, 万金油的回答就是, “我觉得我的缺点是学习的东西偏重理论, 实践经验不足”. 这句话真是屡试不爽啊..大家一定要从中看到WS的地方, 深刻领悟之.. :)

另外, HR面还会看你是不是真的想要去他们公司工作, 如果你面试阿里巴巴, 言必谈及马云曾经说过blahblah, 我想, HR那里你的好感分会大大的增加. :)

还有一点要注意, HR面的时候, 你得时时刻刻表现出自己是团队的一份子. 比如说你最得意的项目之类的, 就一定要举出一个团队合作的例子, 尽量少搞个人英雄主义, 那是20年前的事情了.

要注意的是, 最后结束时候HR会问有没有问题要问我, 别忘了面试还没有结束, 你还是要表现自己非常想去他们公司, 还是有一个万金油的回答, “如果我拿到offer, 能不能马上过去实习呢”

哈哈, HR面一定要脸皮厚啊, 大家千万记住哦. 这是黎明前的黑暗, 光明就在眼前.

八. 感谢

好吧, 真心的感谢这段时间来给我鼓励, 陪我渡过难忘岁月的朋友们. 你们都是我的好兄弟或者姐妹. 以下感谢名单排名不分先后.

浩哥, mapx, 老万, 张师傅, reck, 老邓, 刘姐姐, 子, 波波, 小胖, 小威等等…以及所有帮助过我, 鼓励过我的人!

再次表示衷心的感谢…肺腑之言啊!

最后, 祝愿所有还在为了自己的理想奋斗的同学们, 加油! 要坚信, 自己一定会成功的.

Written by linluxiang

二月 19th, 2011 at 1:45 上午

Posted in 总结,技术

Tagged with

优化Python的DES加密程序有感

without comments

此文来自本人原JavaEye博客,发表时间2009年3月24日,可能有些东西现在看来是过时的,甚至是错误的,不过为了保证原来写的时候的感觉,所以没有修改。只在括号中注明了现在的看法。原文地址

最近信息安全的老师布置了作业。要求实现DES算法。。写了1天,优化了1天。。。小有些心得。。
首先感慨一下DES算法。。真是对人对机器都不友好的算法。。竟然还有诡异的S-BOX操作。。。
第二感慨一下Python对2进制不那么方便的支持。。连bin函数都没有。。虽然3.0有了。。可惜2.5没有。。只能自己实现,一大损失效率的地方啊。

好,接下来说说优化过程。

首先是单线程改多线程。。原来是将明文分成64bit一块的序列,每块单独加密。改成每一个64bit的块开一个线程操作。(这个在今天看来完全搞笑)

单线程加密解密一个1000长度的字符串所需时间大致和多线程加密10000长度的相等。效率提升明显。(提升效率很奇怪,忘了自己当初怎么测试的了)

其次是优化异或过程。原来的异或是整体将字符串转成整数然后再异或,发现效率很低。第一次修改为将2个字符串的每一位单独取出转成整形以后异或,效率提升不明显。后来上课时候突然想到,异或操作其实完全可以抛弃整数。1位的2个字符串的异或操作只有4种,写一个字典,用想要异或的2个字符串作为key,结果作为value。简单实现了一下。

def Xor(s1, s2):
    """
    字符串xor 

    s1 -- 第一个字符串
    s2 -- 第二个字符串
    """
    data={}
    data['0','1']=data['1','0']='1'
    data['0','0']=data['1','1']='0'
    s=[data[s1[x],s2[x]] for x in range(0,s1.__len__())]
    return ''.join(s)

三就是优化10进制转2进制。因为python没有提供 bin 函数。于是自己实现了一个。

dec2bin = lambda n:''.join([hex2bin(x) for x in hex(n)[2:].rstrip('L')]

但是在用profile做测试的时候发现的。程序运行中大量的用到了这个函数。很占cpu时间。经过观察发现主要是2个地方使用很多,第一就是异或操作的时候,因为python提供的异或只能在整数之间进行,第二就是在S_BOX运算的时候。

异或操作已经优化了。不需要10进制转2进制了。。剩下就是S_BOX运算了。经过观察发现,S_BOX里面最大的数也不过 15,于是想到可以穷举所有S_BOX里面的数字的值对应的二进制。更改代码如下。

def easyDec2Bin(s):
    """
    简单的10进制转2进制,用于简化s_box操作 

    s -- 10进制数
    """
    data={0:'0000',1:'0001',2:'0010',3:'0011',4:'0100',
              5:'0101',6:'0110',7:'0111',8:'1000',9:'1001',
              10:'1010',11:'1011',12:'1100',13:'1101',14:'1110',
              15:'1111'}
    return data[s]

第四步优化也是通过profile发现的。。发现程序运行过程中,大量的时间消耗在一个lambda函数上。

b= ''.join(map(lambda x:data[x],s))

这个函数的作用是生成一个字符串。。尝试着把这个lambda函数改成列表操作。

b= ''.join([data[x] for x in s]

效率提升明显。。。原来一直以为map操作比较快。看来并非如此。难怪Guido大叔要推广列表操作了。。

最后一步优化就是用了psyco模块,将程序变成JIT的。。效率提升很大。。。(现在看来其实这一步是最关键的。自己当初好傻)

最终优化的结果就是,加密解密1个40k的文件,原本需要将近50秒的时间,现在10秒不到就完成了。。。。

效果提升明显。不过总觉得还有提升的空间。。尝试把多线程改成Stackless版本的试试看。。。。希望能有更大的提高 。。。。(这句话现在看来完全是搞笑,看来当时的自己还是很稚嫩啊)

Written by linluxiang

二月 19th, 2011 at 12:59 上午

Posted in Python,技术

Tagged with

Django的Middleware开发有感

without comments

此文来自本人原JavaEye博客,发表于2009年2月26日。原文地址

Django应该算是目前最火的Pyhton的Web框架了把。个人感觉,超级方便的ORM,清晰而功能明确的分层以及Killer级别的后台管理都让习惯了Java那一套庞大复杂的人有了眼前一亮的感觉。

不过用久了就会发现,django整个框架之中有一个极其繁琐的地方,就好像一块整洁的布上有一个污点一样,令人觉得不爽。

那就是他的URL机制。。当然,也有人非常喜欢他的URL机制,比如说十分的灵活。不过我个人总觉得还有改进的地方。

首先我认为必须要利用正则表达式这一点,这个是特色以及精华,并且确实十分方便,在我眼里属于不能更改的部分。那么自然而然改进的地方就是正则表达式和函数的映射了。

提出问题是一方面,解决问题就是更加重要的地方了。采用何种新的方式来替换原来的URL处理呢?我为此困扰了好久。直到有一天发现了limodou大牛的Uliweb框架。被它的简洁明了的URL方式吸引了。决定采用类似Uliweb的方式处理URL。实现的具体细节,就是用Django的中间件。

使用中间件自然有他的理由。研究官网的文档后发现,Django处理一个Request的过程是首先通过中间件,然后再通过默认的URL方式进行的。所以说我们要做的就是在Middleware这个地方把所有Request拦截住,用我们自己的方式完成处理以后直接返回Response。需要注意的是,因为我不舍得放弃Killer级别的Admin,所以必须要支持默认的URL配置方式。那么我们可以简化原来的设计思路,把中间件不能处理的Request统统不管,丢给Django去处理。简直是完美的解决方案。:)。同时也不得不赞一声Django的设计。

接下来的设计就是考虑URL和函数处理的问题了。这个时候应该感谢的就是Python强大的语法了。提供了Decorator这种机制来解决这个问题。那么设计的思路转变成,在Request到达Middleware的时候,Import所有INSTALLED_APPS里面的views.py,目的自然就是执行Decorator。而在Decorator内部,根据到达的Request获得其访问地址,然后和处理它的函数一起生成RegexURLPattern对象,储存在缓存中。然后对每个Request进行处理,符合规则的就调用函数处理,不符合规则的,就交给Django自己去处理了。

以下是一小段中间件代码。比较核心的部分了。其实代码量也不大。大家看看有没有实现的问题。

    def process_request(self,request):
        path=request.path_info  #获得request的地址
        urls=settings.INSTALLED_APPS  #获得当前安装的app的位置
        def myimport(path):
            try:
                __import__('%s.views'%path)  #读取所有app的views。。目的是注册request处理函数
            except:
                pass
        map(myimport,urls)   #执行所有
        callback=expose.getCallback(path)  #获取相应url对应的处理函数
        if callback:
            return callback[0](request)  #执行。。

整体设计大致如此,完成了0.1版本,已经放在我的代码库里面了.http://code.google.com/p/linllx/

目前还存在的问题就是。第一,对于需要命名分组的正则表达式的处理还不是很有头绪。第二,对于404错误的处理究竟要如何进行,目前的实现来看不是很好。请看见本文的牛人们提供一下思路。。

总得来说,Django中间件的开发并不像我想像中的那么难,甚至可以说,还是比较简单的。下一步着手开发一个类似于Java中的Filter的中间件把。。当然,这个还是需要继续完善的。。

Written by linluxiang

二月 19th, 2011 at 12:58 上午

Posted in Uncategorized,技术

使用greenlet中一点简单心得

with one comment

想要对greenlet的switch机制进行测试,于是写了以下这段代码。


from greenlet import *

def test1(x, y):
    print 'in test1'
    z = gr2.switch(x+y)
    print z
    print 'out test1'

def test2(u):
    print 'in test2'
    print u
    gr1.switch(42)
    print 'out test1'

gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello", " world")

运行结果:

in test1
in test2
hello world
42
out test1

从这个层面可以看出。greenlet的协程其实是一个切换机制。就是主动将自己的运行过程让给别的协程。切换需要有目标,并且可以在switch里面传入参数。如果是第一次运行,switch的参数就是作为协程的run的函数的参数传入。如果是运行中,就作为switch的返回值。
以上隐含的内容是,如果在运行中想要切换到别的协程去做事,那么不能保证那个协程一定会返回结果。
考虑在greenlet上面加上一个隐含回复机制。

Written by linluxiang

二月 16th, 2011 at 2:08 下午

Posted in Python,技术

Tagged with ,