生命是一种长期而持续的累积过程

 

【文章作者是清华电机系彭明辉老师】

许多同学应该都还记得联考前夕的焦虑:差一分可能要掉好几个志愿,甚至于一生的命运从此改观!到了大四,这种焦虑可能更强烈而复杂:到底要先当兵,就业,还是先考研究所?

我 就经常碰到学生充满焦虑的问我这些问题。可是,这些焦虑实在是莫须有的!生命是一种长期而持续的累积过程,绝不会因为单一的事件而毁了一个人的一生,也不 会因为单一的事件而救了一个人的一生。属于我们该得的,迟早会得到;属于我们不该得的,即使侥幸巧取也不可能长久保有。如果我们看清这个事实,许多所谓人 生的重大抉择就可以淡然处之,根本无需焦虑。而所谓人生的困境,也往往当下就变得无足挂齿。

我自己就是一个活生生的例子。从一进大学就决定不再念研究所,所以,大学四年的时间多半在念人文科学的东西。毕业后工作了几年,才决定要念研究所。硕士毕业后,立下决心:从此不再为文凭而念书。谁知道,世事难料,当了五年讲师后,我又被时势所迫,整装出国念博士。

出 国时,一位大学同学笑我:全班最晚念博士的都要回国了,你现在才要出去?两年后我从剑桥回来,觉得人生际遇无常,莫此为甚:一个从大一就决定再也不钻营学 位的人,竟然连硕士和博士都拿到了!属于我们该得的,哪样曾经少过?而人生中该得与不该得的究竟有多少,我们又何曾知晓?从此我对际遇一事不能不更加淡 然。当讲师期间,有些态度较极端的学生会当面表现出他们的不屑;从剑桥回来时,却被学生当做不得了的事看待。这种表面上的大起大落,其实都是好事者之言, 完全看不到事实的真相。从表面上看来,两年就拿到剑桥博士,这好像很了不起。但是,在这两年之前我已经花整整一年,将研究主题有关的论文全部看完,并找出 研究方向;而之前更已花三年时间做控制方面的研究,并且在国际著名的学术期刊中发表论文。而从硕士毕业到拿博士,期间七年的时间我从不停止过研究与自修。 所以,这个博士其实是累积了七年的成果,或者,只算我花在控制学门的时间,也至少有五年),根本也没什么好惊讶的。

常人不从长期而持续的累积过程来看待生命因积蓄而有的成果,老爱在表面上以断裂而孤立的事件夸大议论,因此每每在平淡无奇的事件上强做悲喜。可是对我来讲,当讲师期间被学生瞧不起,以及剑桥刚回来时被同学夸大本事,都只是表象。

事实是:我只在乎每天二十四小时点点滴滴的累积。拿硕士或博士只是特定时刻里这些成果累积的外在展示而已,人生命中真实的累积从不曾因这些事件而终止或加添。常有学生满怀忧虑的问我:

老师,我很想先当完兵,工作一两年再考研究所。这样好吗?

很好,这样子有机会先用实务来印证学理,你念研究所时会比别人了解自己要的是什么。

可是,我怕当完兵又工作后,会失去斗志,因此考不上研究所。

那你就先考研究所好了。

可是,假如我先念研究所,我怕自己又会像念大学时一样茫然,因此念的不甘不愿的。

那你还是先去工作好了!

可是……”

我 完全可以体会到他们的焦虑,可是却无法压抑住对于这种话的感慨。其实,说穿了他所需要的就是两年研究所加两年工作,以便加深知识的深广度和获取实务经验。 先工作或先升学,表面上大相迳庭,其实骨子里的差别根本可以忽略。在朝三暮四这个成语故事里,主人原本喂养猴子的橡实是早上四颗下午三颗,后来改为朝三暮 四,猴子就不高兴而坚持改回到朝四暮三。其实,先工作或先升学,期间差异就有如朝三暮四与朝四暮三,原不值得计较。但是,我们经常看不到这种生命过程中长 远而持续的累积,老爱将一时际遇中的小差别夸大到攸关生死的地步。

最 讽刺的是:当我们面对两个可能的方案,而焦虑的不知何所抉择时,通常表示这两个方案可能一样好,或者一样坏,因而实际上选择哪个都一样,唯一的差别只是先 后之序而已。而且,愈是让我们焦虑得厉害的,其实差别越小,愈不值得焦虑。反而真正有明显的好坏差别时,我们轻易的就知道该怎么做了。可是我们却经常看不 到长远的将来,短视的盯著两案短期内的得失:想选甲案,就舍不得乙案的好处;想选乙案,又舍不得甲案的好处。如果看得够远,人生常则八,九十,短则五,六 十年,先做哪一件事又有什么关系?甚至当完兵又工作后,再花一整年准备研究所,又有什么了不起?当然,有些人还是会忧虑说:我当完兵又工作后,会不会因为 家累或记忆力衰退而比较难考上研究所?

我 只能这样回答:一个人考不上研究所,只有两个可能:或者他不够聪明,或者他的确够聪明。不够聪明而考不上,那也没什么好抱怨的。假如你够聪明,还考不上研 究所,那只能说你的决心不够强。假如你是决心不够强,就表示你生命中还有其他的可能性,其重要程度并不下于硕士学位,而你舍不得丢下他。既然如此,考不上 研究所也无须感到遗憾。不是吗?

人生的路这么多,为什么要老斤斤计较著一个可能性?我高中最要好的朋友,一生背运:高中考两次,高一念两次,大学又考两次,甚至连机车驾照都考两次。毕业后,他告诉自己:我没有人脉,也没有学历,只能靠加倍的诚恳和努力。现在,他自己拥有一家公司,年收入数千万。

一个人在升学过程中不顺利,而在事业上顺利,这是常见的事。有才华的人,不会因为被名校拒绝而连带失去他的才华,只不过要另外找适合他表现的场所而已。反过来,一个人在升学过程中太顺利,也难免因而放不下身段去创业,而只能乖乖领薪水过活。福祸如何,谁能全面知晓?

我 们又有什么好得意?又有什么好忧虑?人生的得与失,有时候怎么也说不清楚,有时候却再简单不过了:我们得到平日累积的成果,而失去我们不曾努力累积的!所 以重要的不是和别人比成就,而是努力去做自己想做的。功不唐捐,最后该得到的不会少你一分,不该得到的也不会多你一分。

好 像是前年的时候,我在往艺术中心的路上遇到一位高中同学。他在南加大当电机系的副教授,被清华电机聘回来开短期课程。从高中时代他就很用功,以第一志愿上 台大电机后,四年都拿书卷奖,相信他在专业上的研究也已卓然有成。回想高中入学时,我们两个人的智力测验成绩分居全学年第一,第二名。可是从高一我就不曾 放弃自己喜欢的文学,音乐,书法,艺术和哲学,而他却始终不曾分心,因此两个人在学术上的差距只会愈来愈远。反过来说,这十几二十年我在人文领域所获得的 满足,恐怕已远非他所能理解的了。我太太问过我,如果我肯全心专注于一个研究领域,是不是至少会赶上这位同学的成就?我不这样想,两个不同性情的人,注定 要走两条不同的路。不该得的东西,我们注定是得不到的,随随便便拿两个人来比,只看到他所得到的,却看不到他所失去的,这有什么意义?

有 次清华电台访问我:老师你如何面对你人生中的困境?我当场愣在那里,怎么样都想不出我这一生什么时候有过困境!后来仔细回想,才发现:我不是没有过困境, 而是被常人当作困境的境遇,我都当作一时的际遇,不曾在意过而已。刚服完兵役时,长子已出生却还找不到工作。我曾焦虑过,却又觉得迟早会有工作,报酬也不 至于低的离谱,不曾太放在心上。念硕士期间,家计全靠太太的薪水,省吃俭用,对我而言又算不上困境。一来,精神上我过的很充实,二来我知道这一切是为了让 自己有机会转行去教书(做自己想做的事)。三十一岁才要出国,而同学正要回系上任教,我很紧张(不知道剑桥要求的有多严),却不曾丧气。因为,我知道自己过去一直很努力,也有很满意的心得和成果,只不过别人看不到而已。我没有过困境,因为我从不在乎外在的得失,也不武断的和别人比高下,而只在乎自己内在真实的累积。

我 没有过困境,因为我确实了解到:生命是一种长期而持续的累积过程,绝不会因为单一的事件而有剧烈的起伏。同时我也相信:属于我们该得的,迟早会得到;属于 我们不该得的,即使一分也不可能加增。假如你可以持有相同的信念,那么人生于你也会是宽广而长远,没有什么了不得的困境,也没有什么好焦虑的了。

 

 

理解__getattr__, __setattr__

 

The following methods can be defined to customize the meaning of attribute access (use of, assignment to, or deletion of x.name) for class instances.

object.__getattr__(self, name)

Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

Note that if the attribute is found through the normal mechanism, __getattr__() is not called. (This is an intentional asymmetry between __getattr__() and __setattr__().) This is done both for efficiency reasons and because otherwise __getattr__() would have no way to access other attributes of the instance. Note that at least for instance variables, you can fake total control by not inserting any values in the instance attribute dictionary (but instead inserting them in another object). See the __getattribute__() method below for a way to actually get total control in new-style classes.

object.__setattr__(self, name, value)

Called when an attribute assignment is attempted. This is called instead of the normal mechanism (i.e. store the value in the instance dictionary). name is the attribute name, value is the value to be assigned to it.

If __setattr__() wants to assign to an instance attribute, it should not simply execute self.name = value — this would cause a recursive call to itself. Instead, it should insert the value in the dictionary of instance attributes, e.g., self.__dict__[name] = value. For new-style classes, rather than accessing the instance dictionary, it should call the base class method with the same name, for example, object.__setattr__(self, name, value).

 

例子(Python Recipe6.1):

 

#!/usr/bin/python

class Temperature(object):
    coefficients = {'c': (1.0, 0.0, -273.15), 'f': (1.8, -273.15, 32.0),
                    'r': (1.8, 0.0, 0.0)}
    def __init__(self, **kwargs):
        # default to absolute (Kelvin) 0, but allow one named argument,
        # with name being k, c, f or r, to use any of the scales
        try:
            name, value = kwargs.popitem()
        except KeyError:
            # no arguments, so default to k=0
            name, value = 'k', 0
        # error if there are more arguments, or the arg's name is unknown
        if kwargs or name not in 'kcfr':
            kwargs[name] = value             # put it back for diagnosis
            raise TypeError, 'invalid arguments %r' % kwargs
        setattr(self, name, float(value))
    def __getattr__(self, name):
        # maps getting of c, f, r, to computation from k
        print '__getattr__: %s' % str(name)
        try:
                        eq = self.coefficients[name]
        except KeyError:
            # unknown name, give error message
            raise AttributeError, name
        return (self.k + eq[1]) * eq[0] + eq[2]
    def __setattr__(self, name, value):
        # maps settings of k, c, f, r, to setting of k; forbids others
        print '__setattr__: name = %s, value = %s' % (str(name), str(value))
        if name in self.coefficients:
            # name is c, f or r -- compute and set k
            eq = self.coefficients[name]
            self.k = (value - eq[2]) / eq[0] - eq[1]
        elif name == 'k':
            # name is k, just set it
            object.__setattr__(self, name, value)
        else:
            # unknown name, give error message
            raise AttributeError, name
    def __str__(self):
        # readable, concise representation as string
        return "%s K" % self.k
    def __repr__(self):
        # detailed, precise representation as string
        return "Temperature(k=%r)" % self.k

if __name__ == '__main__':
        t = Temperature(f=70)
        print t.__dict__
        print t.c
        print t.f
        print t.k

输出为:

simplyzhao@simplyzhao-laptop:~/code/python$ ./recipe2ed_6_1.py
__setattr__: name = f, value = 70.0
__setattr__: name = k, value = 294.261111111
{'k': 294.26111111111106}
__getattr__: c
21.1111111111
__getattr__: f
70.0
294.261111111


 

 

 

 

"yield" in recursive generator

来源: www.javaeye.com/topic/338111

 

二叉树的中序遍历,递归的genertor(想当然的解法,和遍历二叉树的算法完全一样)

  1. class tree_t:  
  2.     def __init__(self, data, left = None, right = None):  
  3.         self.left = left  
  4.         self.data = data  
  5.         self.right = right  
  6.   
  7. left = tree_t("left")  
  8. right = tree_t("right")  
  9. root = tree_t("root", left, right)  
  10.   
  11. def infix_iterate(tree):  
  12.     if tree.left:  
  13.         infix_iterate(tree.left)  
  14.     yield tree  
  15.     if tree.right:  
  16.         infix_iterate(tree.right)  
  17.   
  18. for tree in infix_iterate(root):  
  19.     print tree.data



二叉树的中序遍历,递归的genertor(正确的解法)

  1. class tree_t:  
  2.     def __init__(self, data, left = None, right = None):  
  3.         self.left = left  
  4.         self.data = data  
  5.         self.right = right  
  6.   
  7. left = tree_t("left")  
  8. right = tree_t("right")  
  9. root = tree_t("root", left, right)  
  10.   
  11. def infix_iterate(tree):  
  12.     if tree.left:  
  13.         for child in infix_iterate(tree.left):  
  14.             yield child  
  15.     yield tree  
  16.     if tree.right:  
  17.         for child in infix_iterate(tree.right):  
  18.             yield child  
  19.   
  20. for tree in infix_iterate(root):  
  21.     print tree.data



第1个例子没有任何问题,很直观。但在第2个例子时,发现程序只输出一行代码root,仔细看了下参考资料,才注意到正确的递归调用和我想当然的不一样:

  1. 想当然的写法:  
  2. if tree.left:  
  3.     infix_iterate(tree.left)  
  4.   
  5. 正确的写法:  
  6. if tree.left:  
  7.     for child in infix_iterate(tree.left):  
  8.         yield child  


不知道为什么python的递归generator没有采用例子2中那样直观的写法?是实现困难的原因吗?

 

 

Python里带有yield表达式(*)的函数是generator function,而调用一个generator function总是返回一个iterator;iterator不调用其next()方法是不会执行的;iterator总是通过yield表达式来提供next()方法的返回值,自然,谁调用next()方法谁就会获得返回值。

在 楼主的第二种写法里,那两个if语句里的调用并不是“递归调用”,而只是单纯的获得了两个iterator,并且没有让它们执行;generator function要真的“递归”起来,就得像第三种写法那样,在generator function的函数体里获得了一个iterator之后,遍历这个iterator里的值并逐个yield返回出去。for...in语句所做的正好就是拿到一个iterable然后逐个遍历里面的值,所以正好适合用在这个场景。

要是直接带第二种写法上加点东西就能看到得到的iterator里发生的事:

Python代码
  1. class tree_t:  
  2.     def __init__(self, data, left = None, right = None):  
  3.         self.left = left  
  4.         self.data = data  
  5.         self.right = right  
  6.   
  7. left = tree_t("left")  
  8. right = tree_t("right")  
  9. root = tree_t("root", left, right)  
  10.   
  11. def infix_iterate(tree):  
  12.     if tree.left:  
  13.         print (infix_iterate(tree.left).next().data)  
  14.     yield tree  
  15.     if tree.right:  
  16.         print (infix_iterate(tree.right).next().data)  
  17.   
  18. for tree in infix_iterate(root):  
  19.     print tree.data  


当然这样不能用于遍历就是了,只是用来说明generator function里调用了“自己”得到的是什么。