浏览模式: 标准 | 列表 | 订阅

appCan离线手册手机版

作者:ciniao 时间:2012-08-05 分类:记录 2 Comments

    * 文章同步发布至appCan官方论坛http://bbs.appcan.cn/forum.php?mod=viewthread&tid=3525
    7月10日:
    公开源代码,供大家一起学习讨论参考。
    下载地址:http://uubox.me/app/appcanbook/appcanbook.zip

    7月5日更新:
    android版:http://uubox.me/app/appcanbook/appcanbook.apk
    IOS版:http://uubox.me/app/appcanbook/appcanbook.ipa
    之前一直在使用官方提供的appcanplay调试程序,每次修改好后,都需要copy到SD里去,操作比较麻烦,于是遂在本工具里增加了一个“项目调试工具”。实现原理:输入地址,点击开始调试后,系统会uexWindow.open一个name为root2的窗口,指向您所输入的地址,以达到调试的目的。见下图:
    

    6月28日:
    众所周知,目前appcan官方提供的API手册有多么的难用,本人经过几天查阅,终于不堪重负,决定用appcan本身来开发一个离线手册,经过一天一夜的整理,现在委婉发布0.1测试版本,尚有部分API未整理完成。

    离线版有什么优势?
    1:几乎重写了官方的所有示例,更简洁明了。
    2:随时随地都可以查看。 
    3:提供直接【执行代码】功能,可以更直观的看到最终效果。 

    使用中有BUG或文档不对怎么办?
    本文档未非官方版本,若有歧义时,请以官方在线版本API文档为准,我也会在最快时间内更新完成,并在线推送给已安装的用户来更新版本。若你在使用中有什么问题或建议,可以直接在本帖留言反馈。

    软件截图:
     

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

用appCan连接腾讯&新浪微博并调用API实例

作者:ciniao 时间:2012-08-03 分类:JavaScript 3 Comments

    * 文章同步发布至appCan官方论坛:http://bbs.appcan.cn/forum.php?mod=viewthread&tid=4644
    点击登陆授权后,会调用API获取当前用户的微博信息,如果要调用其他接口,直接查看开放平台文档即可。
    腾讯微博API文档
    新浪微博API文档


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<a href="javascript:qqweiboLogin()">用腾讯微博登陆</a><br/>
<a href="javascript:sinaweiboLogin()">用新浪微博登陆</a>
<div id="aa"></div>
<script>
function sinaweiboLogin(){
    var W = new Weibo('sina');
    W.login('3183294709','http://weibo.uutown.com/oauth.html',function(){
        var me = this;
        this.api('users/show',function(json,result){
            document.getElementById('aa').innerHTML=result;    
        },{uid:me['sinauid']});
    });
}

function qqweiboLogin(){
    var W = new Weibo('qq');
    W.login('801205896','http://weibo.uutown.com/oauth.html',function(){
        this.api('user/info',function(json,result){
            document.getElementById('aa').innerHTML=result;        
        });
    });
}

(function(){
var __WBME;
var Weibo = window.Weibo = function(type){
    this.cfg = {};
    this.cfg.sina = {
        name : '新浪',
        login : 'https://api.weibo.com/oauth2/authorize?client_id={appKey}&response_type=token&display=wap&redirect_uri={backUrl}',
        api : 'https://api.weibo.com/2/{api}.json?access_token={access_token}'
    };
    this.cfg.qq = {
        name : '腾讯',
        login : 'https://open.t.qq.com/cgi-bin/oauth2/authorize?client_id={appKey}&response_type=token&redirect_uri={backUrl}',
        api : 'https://open.t.qq.com/api/{api}?oauth_consumer_key={appKey}&access_token={access_token}&openid={openid}&clientip={clientip}&oauth_version=2.a&scope=all&format=json'
    };
    this.type = type;
    this.oauthWindow = 'weiboLoginWin';
    __WBME = this; //fix:appcan onOAuth时,会导致this指向错误
};
Weibo.prototype = {
    /*
    开始登陆
    appKey:应用的appkey
    backUrl:应用的回调地址
    *以上2项必须和开放平台的后台设置一致,否则必定会授权失败
    
    succCall:授权成功后触发该函数
    */
    login : function(appKey,backUrl,succCall){
        this.appKey = appKey;
        this.backUrl = backUrl;
        this.succCall = succCall;
        
        var type = this.type;
        uexWindow.onOAuthInfo=this.onOAuth;
        uexWindow.toast("1","5","正在加载"+ this.cfg[type].name +"微博登陆页面...",5000);
        //格式化登陆地址
        var url = this.cfg[type].login;
        uexWindow.open(this.oauthWindow,"0",
        url.replace('{appKey}',appKey).replace('{backUrl}',encodeURIComponent(backUrl)),
        "2","0","0","5");
        return this;
    },
    /*
    当授权窗口的URL发生变化时,appcan会触发该函数
    */
    onOAuth:function(winNam,url){
        var me = __WBME;
        if(winNam == me.oauthWindow && url.indexOf(me.backUrl)==0){
            //fix:SINA会回调2次,设置1S内只
            if(me.lastCall && new Date()-me.lastCall<1000)return;
            
            var par = url.split('#')[1],par = par.split('&');
            for(var k in par){
                var _karr = par[k].split('=');
                me[me.type + _karr[0]] = localStorage[me.type + _karr[0]] = _karr[1];
            }
            uexWindow.toast("0","5","授权成功 正在返回...",3000);
            me.lastCall = new Date();
            me.succCall && me.succCall.call(me);
        }
        return me;
    },
    /*
    授权成功后,返回login信息
    */
    getLoginInfo : function(type){
        var t = (type || this.type);
        return {
            'access_token':localStorage[t+'access_token'],
            'expires_in':localStorage[t+'expires_in'],
            'remind_in':localStorage[t+'remind_in'],
            'uid':localStorage[t+'uid'],
            'openid':localStorage[t+'openid'],
            'openkey':localStorage[t+'openkey']
        };
    },
    /*
    调用微博的API接口(所有接口可进入微博对应的文档查询)
    api : 接口地址,如:user/info
    succCall : 成功时回调函数
    data : 接口的参数,可选,如:{reqnum:1,fopenid:5673}
    requestType : 请求的类型,默认为GET
    errCall : 失败时回调函数
    */
    api : function(api,succCall,data,requestType,errCall){
        var info = this.getLoginInfo();
        var type=this.type,apiBase = this.cfg[type].api;
        
        var url = apiBase.replace('{api}',api)
        .replace('{access_token}',info['access_token'])
        .replace('{appKey}',this.appKey)
        .replace('{openid}',info['openid'])
        .replace('{clientip}','127.0.0.1');
        
        var rq = (requestType||'GET'),
        httpID = (this.httpID || 0) + 1;
        
        //拼接GET请求的url地址
        if(rq=='GET' && data){
            var par = [];
            for(k in data){
                par.push(k + '=' + encodeURIComponent(data[k]));    
            }
            url+= ('&'+par.join('&'));
        }        
        var httpID = (this.httpID || 0) + 1;
        uexXmlHttpMgr.open(httpID, rq, url,"");
        //设置POST内容
        if(rq=='POST' && data){
            for(var k in data){
                uexXmlHttpMgr.setPostData(httpID, "0", k, data[k]);
            }
        }
        uexXmlHttpMgr.onData = function(opid,status,result){
            uexXmlHttpMgr.close(httpID);
            var json = eval('('+ result +')');
            succCall && succCall(json,result);
        };
    uexXmlHttpMgr.send(httpID);
        this.httpID = httpID+1;
        return this;
    }
};

})();
</script>

</body>
</html>


#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

为什么程序员/工程师难升职?

作者:ciniao 时间:2012-04-16 分类:记录 No Comments

    今天看到微博上@hellodba发的一个帖子:“内部晋升越来越困难,但是外部来的大P越来越多,所以很多人都选择跳槽”,之后我从三个方面简要的进行 了回答:“外面来的总是有包装的,内部的都是肉身PK,此一输;外面来的总是小股人马,内部的一批批的,升谁都伤感情,此二输;外面来的通常都是大佬推荐 的,没有特别重大机会,人不会来,内部的就不解释了,成果都被大佬吸收,难有机会,此三输”。之后讨论不断,我也余兴未了,继续写来。

    这个世界上有一类人特别苦逼,苦逼到什么程度呢?他们省吃俭用攒钱买房,结果房价越来越贵;公司外部竞争激烈,他们工作异常繁忙,披星戴月,日复一日;技 术更新行业罕见,他们要随时调整心情,随时学习知识;他们长期和机器为伍,大多比较呆傻,比较单纯;还有很多不一一例举,这一类人就是程序员。

    而就是这么一类程序员过着这么苦逼的生活,在公司内部却难以获得公平的晋升机会,外来的和尚总是在不断打破平衡,甚至是刚毕业的新和尚拿得都比老和尚多,这是全行业都罕见的奇观,IT人有幸经历了。

    某创业公司,某个程序员要离职,老板甚至不问问他直接领导的意见,就同意了,没有挽留,之后大骂不忠诚,这个人拿3k,拿了2年,他走了以后,老板用5k雇了个新面孔,但就是不愿意给这个老人晋升,不愿意给加到哪怕是4k。

    某上市公司,游戏部门突然从外部空降了一个领导,原因是原大佬被挖走以后,剩下的人升谁都有意见,难以服众,不从外部请人来镇不住局面,这个人一来,大家是团结了,团结起来和这个人斗,但最后还是和解了。

    某国际大公司,某人伪造简历,包装的如花似玉,获得高职,工作主要有下属完成,他只需要汇众汇报即可,越混路越宽,直到某天事发,依然是高官。

    某IT企业,某清华同学离职时语重心长的对我说,XX(可以理解为网游,搜索,电商任意一种)是00-02年毕业的这些人清华人的机会, 我们就是比某人强十倍也没有机会,也得从下面做起,搜索的天时不属于我,此人去了某金融分析软件公司,目前是高管,同期留在某IT企业的其他同学依然过着 苦逼的生活。
举了这么多例子,我想说得是为什么不给你晋升这个问题,或者晋升很难,为什么?

1)大佬的问题

    你晋升困难,最大的主观原因在你自己,最大的客观原因在你的直接上司。付责任人的说,目前很多企业的领导是不合格的,他们大多80后,没有为他人着想的思 想基础,一味的考虑自己,不顾下属,曾经我对某人说,你说你是合格的领导,你说出你下属每个员工租房在哪里,每月多少房租,我就同意你是合格的领导,结果 他羞愧不言。你晋升不了,很大程度上是你的领导没有帮你,连你每月房租多少都不知道,你指望他帮助你提高技术水平,帮助你晋升?

2)大佬的大佬的问题

    你大佬的大佬,level很高,他需要引入新鲜血液,他知道这个队伍缺什么,这个是他思考的问题,他需要找牛的人来补这个缺口,于是一个光鲜照人的牛人进 来了(虽然两年后也会泯为众人)给队伍带来了新鲜的血液,但你的大佬升不上去,你大佬边上的位置被这个人占了,你的位置在哪?

3)公司的问题

    各大企业给员工的再教育和培训都是不尽相同的,但大多口号是一致的,在工作中锻炼成长,这句话是最扯淡的,国外很多大公司是有很完善的培训和再教育计划 的,会给员工一个充电的机会,并且给其一个完善的培训后,以便于让他在新升职的岗位上能够很好的胜任。在国内大公司都在找牛人,就是不愿意自己培养,原因 是什么,不解释,你懂得。

4)你的兄弟

    很多时候让你升不了职恰恰是因为和你一起战斗的兄弟,他们工作也很不错,你升职了,他们怎么办?这也是一个平衡的问题,你很努力,为什么你没有带动你的兄 弟一起努力,你上去了,需要你这帮兄弟的支持,他们会支持你吗?曾有一个说了一句话,我觉得很值得回味,“当大家都认为你该升职了,就是你升职的时候 了”,细细品味,很有道理。

5)你自己的问题

    最后你升不了职是你自己的问题,每天工作很忙,没时间充电,每天工作压力很大,来不及学习,每天这个那个,一年下来碌碌无为。你提高了自己的效率了嘛?你 周围有朋友再帮你吗?你知道你要学什么嘛?你知道什么样的工作才能超出领导的期望?,你超出领导期望后却没有升职和领导沟通过吗?我曾在某企业,我周围的 几乎所有人加薪升职都是和领导沟通后才获得的。指望主动给你加薪升职,不如指望自己的沟通。

6)还是你自己的问题

    你选择的这个行业是不是对的,公司是不是对的,就好像我说的这个清华的同学这个例子。如果你能耐大可以选大公司,PK到一票牛人上去,如果你能耐不大,去成熟大公司,还心理期盼高薪升职就不现实了,不如去一个有前途的中小公司,开创自己的事业。

从企业角度出发,如何创建一个合理公平的晋升机制呢?

1)第一流大佬才会招第一流的人,第二流大佬只会招第三流的人,因此公司一把手必须是第一流的,价值观才能靠谱,制度才靠谱,没熟读历史,不理解中国文化的,建议不要做公司一把手。

2)晋升的制度必须公平,面向每一个人,每一个层次,这往往很难做到,做前端的和做后台的不好比,但做前端的可以和做前端的比。必须要有公开公平的比拼,已获得升职机会。例如某公司做一个高维矩阵分解的难题,大家机会均等,性能最快,效果最好,胜出者升职,带领团队。

3)鼓励公司职员交流,传播和帮助他人的文化,一个人如果乐于助人,帮助他人提高技术水平,这个人升职升上来,大家都会顶,反之,你保守,不帮助他人,水平再牛,升职上来也没人支持。

4)可以给职员一些挑战的机会,提供更多的资源,比如某公司的闪电计划,超越了谷歌搜索效果,就是一个很好的例子,要敢于给一些勇于挑战的职员更多的资源,在严酷的战斗中考验,并提供充分弹药。

5)给予内训机会,邀请业界牛人讲座,送职员去美帝考察开会乃至工作等等。培训机会是发达国家企业的一种非常好的激励措施,一个岗位5个人培训,最好的上岗,这是一个很公平的机会,培训机构足够独立。

    方法有很多,只要这第一流的大佬,心中有着这帮打生打死的兄弟,办法总是有的,不要总是考虑自己的业绩,考虑自己的乌纱帽,做到这一点很难很难,但制度不是只有这位大佬可以制定,任何职员都应该积极投身到制度建立的过程中,要敢于提出自己的观点,毕竟公司是大家的公司。

    我就写这么多,我是一个十年一线程序员的身份写这篇博客的,我努力做到客观,但我相信我更多代表的是劳方立场。

作者:梁斌
来源:http://blog.sina.com.cn/s/blog_593af2a70100w0iv.html

程序员也敢吃10元的盒饭

作者:ciniao 时间:2011-12-23 分类:记录 5 Comments

    又是一个阳光灿烂的中午,看了一上午的报纸,茶水也顺带喝了不少,肚子早已经咕咕作响了,今天中午吃点什么了,貌似楼下的新开张的盒饭还不错,于是我来到楼下准备买上一盒。
    菜色还不错,价格有6元,8元,10元,12元,20元的,像哥这样的精英管理人才,怎么着也的吃最高级的才配合身份,就在我准备购买时,一个响亮的声音响起,
    "老板,给我一份10元的盒饭!"
    顺势撇了一眼,一个小伙子,眉开眼笑的靠近盒饭铺,今天是1号,看样子是发工资了。就当他走近时,看到了我,刚才的欢愉的表情瞬时黯淡下去,他知道我认出了他,靠,满头白里带一点黑的头发,永远没睡醒的眼神,以及那凌乱的胡渣子,都出卖了他的身份。我继续狠狠的盯着他,他越发的羞愧了,我犀利的眼神正在和他做着底层通信,我默默的向他传达一个信息:"你,也配吃10元的盒饭?".
    他哀怜的眼神似乎在祈求我不要拆穿他的身份,可惜,哥这么有正义感的人,怎么能在这个时候放弃原则!
    "你不是隔壁公司的程序员么?"
    就这一瞬间,他整个人似乎崩溃下去,刚才欢愉的表情彻底变为哭丧,周围的小摊贩以及路人甲乙丙丁,都纷纷投来了鄙视的眼光,他瘟鸡一样的双手抓着头发,痛苦的蹲了下去。
    就在这时,人群中终于有人忍不住了,大声呵骂到:"呸,程序员也敢吃10元的盒饭,真不要脸"
    一位老大娘好心的提醒到:"小伙子啊,你一个程序员,挣点钱不容易啊,怎么吃10元的盒饭啊"
    一名打扮妖艳入时的姑娘说到:"人家当小姐的都才吃10元,你也敢要10元的?"
    我义正言辞的给他说到:"我说一句话顶你写一万行代码,也才吃20元的盒饭,你竟敢吃10元的"
    老板也发话了:"是程序员啊,太不好意思了,你吃6元的吧,不然人家知道我卖了你10元的,我这生意就做不了啊"
    他终于发出颤抖的声音说到:"对不起,我刚才说错了,给我一份6元的"。
    这时人群中爆发出激烈的掌声,我知道,这是我又一次坚持原则,换来的荣誉的赞赏!

python可变参数与标准输出的重定位

作者:ciniao 时间:2011-09-27 分类:python 2 Comments

    使用python的内置函数print时,大家会发现它是支持任意多个参数,也就是说,print的调用参数是不固定的。例如:
print "你好"
print "我是",name
print "现在是", time , " 你在干什么呢", name , "!"

    这得益与python的“可变参数”,在Python里,可以使用*和**来设置可变参数,它们的区别是*传递一个参数元组,**传递一个参数字典,二者可以同时混合使用。混合使用时要加些小心,因为python中他们的次序是重要的。参数归为4类,不是所有的类别都需要。他们必须按下面的次序定义,不用的可以跳过。
    1)必须的参数
    2)可选的参数
    3)过量的位置参数
    4)过量的关键字参数
def funName(a, b=None, *c, **d):
    这个次序是必须的,因为*args和**kwargs只接受那些没有放进来的其他任何参数。没有这个次序,当你调用一个带有位置参数的函数,python就不知道哪个值是已声明参数想要的,也不知道哪个被作为过量参数对待。
    也要注意的是,当函数能接受许多必须的参数和可选的参数,那它只要定义一个过量的参数类型即可。看下面的例子:
def add(a, b, c):
    return a + b + c
>>> add(1, 2, 3)
6
>>> add(a=4, b=5, c=6)
15
>>> args = (2, 3)
>>> add(1, *args)
6
>>> kwargs={'b': 8, 'c': 9}
>>> add(a=7, **kwargs)
24
>>> add(a=7, *args)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() got multiple values for keyword argument 'a'
>>> add(1, 2, a=7)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: add() got multiple values for keyword argument 'a'

    注意这个例子的最后几行,特别留意当传递一个元组作为可变参数时,是否要显式的传递关键字参数。因为python使用次序规则来扩展过量的参数,那位置参数要放在前面。这个例子中,最后两个调用是相同的,python不能决定那个值是给a的。

    下面举一个例子来模仿print的实现:
>>> import sys
>>> def myprint(*argv):
    sys.stdout.write(" ".join([str(i) for i in argv]) + "\n")

>>> print "I believe", 2012, "is the end of the world."
I believe 2012 is the end of the world.
>>> myprint("I believe", 2012, "is the end of the world.")
I believe 2012 is the end of the world.
>>> print "tuple:", (1, 2, 3), "list:", [1, 2, 3], "dict:", {"begin":-2012, "end":2012}
tuple: (1, 2, 3) list: [1, 2, 3] dict: {'begin': -2012, 'end': 2012}
>>> myprint("tuple:", (1, 2, 3), "list:", [1, 2, 3], "dict:", {"begin":-2012, "end":2012})
tuple: (1, 2, 3) list: [1, 2, 3] dict: {'begin': -2012, 'end': 2012}

    print默认是输出到stdout中,在终端运行的程序,无重定向的情况下,print输出到控制台。如果要做代码里实现把print的输出写入到log文件中,可以通过修改stdout的文件对象来实现。同理,重定位标准输入和标准错误输出分别修改stdin和stderr的文件对象即可。
    下面的例子捕捉所有print的输出,让输出的每一行前增加一个时间的显示:
import sys, time
class MyOutput():
    def __init__(self, fd):
        self.formatTime()
        self.out = fd
        self.newLine = True
    def formatTime(self):
        return time.strftime("%H:%M:%S  ", time.localtime())
    def write(self, s):
        if self.newLine:
            self.out.write(self.formatTime())
            self.newLine = False
        self.out.write(s)
        if s.endswith("\n"):
            self.newLine = True
    def flush(self):
        self.out.flush()
sys.stdout = MyOutput(sys.stdout)

print "Program begin."
mylist = [5, 4, 3, 2, 1]
print "prev:  ", mylist
mylist.sort()
print "after: ", mylist
time.sleep(3)
print "Program end."

运行效果如下图:


本文部分内容来源于:http://xiaoxia.org/?p=4270
 CiNiao's Document

浏览模式: 标准 | 列表 | 订阅

PHP向socket服务器收发数据

作者:ciniao 时间:2011-08-31 分类:php 1 Comments

    刺鸟原创文章,转载请注明出处
    由于项目的服务端服务端采用了PHP+python的结构,这必然就会涉及到2个语言,即:PHP和python之间的通信处理。
    python要主动向PHP发送数据,很简单,通过http方式调用接口即可,而PHP要向python发送数据,则需要使用到php的socket相关功能,该功能的最简代码如下:

/*socket收发数据
@host(string) socket服务器IP
@post(int) 端口
@str(string) 要发送的数据
@back 1|0 socket端是否有数据返回

返回true|false|服务端数据
*/
function sendSocketMsg($host,$port,$str,$back=0){
    $socket = socket_create(AF_INET,SOCK_STREAM,0);
    if ($socket < 0) return false;
    $result = @socket_connect($socket,$host,$port);
    if ($result == false)return false;
    socket_write($socket,$str,strlen($str));
    
    if($back!=0){
        $input = socket_read($socket,1024);
        socket_close ($socket);    
        return $input;
    }else{
        socket_close ($socket);    
        return true;    
    }    
}

    socker_read的第二个参数用以指定读入的字节数,你可以通过它来限制从客户端获取数据的大小。
    注意:socket_read函数会一直读取壳户端数据,直到遇见\n,\t或者\0字符,PHP脚本把这些字符看做是输入的结束符。

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

python中的lambda函数

作者:ciniao 时间:2011-08-30 分类:python 1 Comments

    lambda函数也叫匿名函数,即,函数没有具体的名称。先来看一个最简单例子:
def f(x):
    return x**2
print f(4)

Python中使用lambda的话,写成这样:
g = lambda x : x**2
print g(4)

lambda表达式在很多编程语言都有对应的实现。比如C#:
var g = x => x**2
Console.WriteLine(g(4))

    那么,lambda表达式有什么用处呢?很多人提出了质疑,lambda和普通的函数相比,就是省去了函数名称而已,同时这样的匿名函数,又不能共享在别的地方调用。其实说的没错,lambda在Python这种动态的语言中确实没有起到什么惊天动地的作用,因为有很多别的方法能够代替lambda。同时,使用lambda的写法有时显得并没有那么pythonic。甚至有人提出之后的Python版本要取消lambda。

    回过头来想想,Python中的lambda真的没有用武之地吗?其实不是的,至少我能想到的点,主要有:
    1. 使用Python写一些执行脚本时,使用lambda可以省去定义函数的过程,让代码更加精简。
    2. 对于一些抽象的,不会别的地方再复用的函数,有时候给函数起个名字也是个难题,使用lambda不需要考虑命名的问题。
    3. 使用lambda在某些时候让代码更容易理解。

lambda基础
    lambda语句中,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值。lambda语句构建的其实是一个函数对象,见证一下:
g = lambda x : x**2
print g
<function <lambda> at 0x00AFAAF0>

    C#3.0开始,也有了lambda表达式,省去了使用delegate的麻烦写法。C#中的lambda表达式关键字是=>,看下面的一个例子:
var array = new int[] {2, 3, 5, 7, 9};
var result = array.Where(n => n > 3); // [5, 6, 9]

C#使用了扩展方法,才使得数组对象拥有了像Where,Sum之类方便的方法。Python中,也有几个定义好的全局函数方便使用的,他们就是filter, map, reduce。
>>> foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]
>>>
>>> print filter(lambda x: x % 3 == 0, foo)
[18, 9, 24, 12, 27]
>>>
>>> print map(lambda x: x * 2 + 10, foo)
[14, 46, 28, 54, 44, 58, 26, 34, 64]
>>>
>>> print reduce(lambda x, y: x + y, foo)
139


非lambda不可?
    上面例子中的map的作用,和C#的Where扩展方法一样,非常简单方便。但是,Python是否非要使用lambda才能做到这样的简洁程度呢?在对象遍历处理方面,其实Python的for..in..if语法已经很强大,并且在易读上胜过了lambda。比如上面map的例子,可以写成:
print [x * 2 + 10 for x in foo]
非常的简洁,易懂。filter的例子可以写成:
print [x for x in foo if x % 3 == 0]
同样也是比lambda的方式更容易理解。

    所以,什么时候使用lambda,什么时候不用,需要具体情况具体分析,只要表达的意图清晰就好。一般情况下,如果for..in..if能做的,我都不会选择lambda。 

lambda broken?
    在数学教学中,经常会使用到lambda,比如有一位老兄就遇到这样一个问题。他想创建一个函数数组fs=[f0,...,f9] where fi(n)=i+n. 于是乎,就定义了这么一个lambda函数:
fs = [(lambda n: i + n) for i in range(10)]
但是,奇怪的是:
>>> fs[3](4)
13
>>> fs[4](4)
13
>>> fs[5](4)
13

结果并没有达到这位老兄的预期,预期的结果应该是:
>>> fs[3](4)
7
>>> fs[4](4)
8
>>> fs[5](4)
9

问题其实出在变量i上。上面的代码换个简单的不使用lambda的缩减版本:
i = 1
def fs(n):
    return n + i
print fs(1) # 2

i = 2
print fs(1) # 3

可见,上面没有达到预期的原因是lambda中的i使用的是匿名函数外的全局变量。修改一下:
fs = [(lambda n, i=i : i + n) for i in range(10)]
>>> fs[3](4)
7
>>> fs[4](4)
8
>>> fs[5](4)
9

本文作者:CoderZh
文章转自:http://www.cnblogs.com/coderzh/archive/2010/04/30/python-cookbook-lambda.html

用python来开发webgame服务端(4)

作者:ciniao 时间:2011-08-30 分类:python 3 Comments

    刺鸟原创文章,转载请注明出处
    前面的工作都已准备就绪,现在我们得来看看服务端怎么和客户端之间进行通信了,Python和FLASH之间的通信,我整理为以下3种:

    1、用现成的协议及类库处理,比如:pyamf
    2、自己封包进行二进制数据流通信
    3、用JSON字符串通信

一、JSON和二进制数据流的优缺比较
    pyamf有比较现成的文档,因此,这里我主要研究研究后两种。我们先简单分析下JSON和数据流各自的优缺点:
    JSON:
    优点:数据结构灵活,无需先制定复杂的协议;跨语言之间基本都有完整的解决方案。
    缺点:传送的数据因为要增加json的特征符(',"",:,{等),导致数据量较大;明文传输,无安全性可言。
    二进制数据流:
    优点:数据量小,无协议文档的话,较难破解内容。
    缺点:数据不灵活,需要先制定足够完善的协议文档。

    权衡利弊,在当前项目中,采用二进制数据流是更为合适的方案。

二、拆包和粘包
    我们下面来了解下python对于数据流通信这块的功能。根据TCP/IP通信的特点,不管是采用JSON字符串,还是二进制数据流,都必须面临的一个问题是:数据的拆包和粘包,具体的问题表现如下:
    采用如下代码,向socket服务器发送5000长度的数据,可以看到,在数据大于4000左右的时候被拆包,触发了服务端的2次dataReceived事件。
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect(("127.0.0.1", 5200))
conn.send('abcdefghij'*500)

服务端接收情况:


    也即是说,在这里我们得粘包,保证数据完整后,才执行对应的逻辑。还有另外一种情况:客户端两次短暂的间隔时间内向服务端发送数据流时,TCP协议会将这2次数据合并为一次后,发送给服务端,如果遇到这样的情况,就需要我们对数据进行拆包处理。

三、常见的数据格式
    传输的数据中,常见的包括以下类型:字符串(长度可变),字节(1位),长整数(4位),无符号整形(4位),短整形(2位)。以上数据中,字符串是长度未定的,因此我们得使用一个短整型在字符串前,以便程序能知道后面的string占了几位。
    另外,为了计算封包的完整性,我们需要在封包首尾各指定一特殊的标示位。为了快速的读取数据,我们需要在封包头记录其长度。综上,我们制定了如下封包格式:
0x11 + 封包长度 + 协议号 + 数据 + 0x22    当中协议号int型(如:3290),如果数据是string,则保存为 short(str长度) + string(str内容)

四、python的二进制处理库
    有了封包格式,我们再来看下python中如何编码实现封包,这时,我们需要用到struct库,struct库主要有以下3个方法:
struct.pack(fmt,v1,v2,.....)    将v1,v2...等参数的值进行pack处理,pack的格式由fmt指定。被pack参数必须严格符合fmt。最后返回一个包装后的字符串。

struct.unpack(fmt,string)    顾名思义,解包,返回一个由解包数据(string)按照fmt格式解包后的元组(即使仅有一个数据也会被解包成远祖)。

struct.calcsize(fmt)    用来计算fmt格式所描述的结构的大小

    其中,fmt的格式所代表的含义和更多对struct的介绍,请浏览官方文档

五、我们的pack
    接下来,需要写一个方法,来按照我们的封包格式,对数据进行pack处理。

#coding:utf-8
#基于python2.6
'''
按照我的习惯,重写struct的格式化符为:
s=字符串
b=字节
i=长整数
u=无符号整形
n=短整形
'''
datafmt = {
    '1001':'ssbi',
    #指定1001协议的数据格式,如果按如上指定,则表示该封包由
    #字符串 + 字符串 + 字节 + 长整型
    #构成
}
import struct
#对数据进行pack数据 comid(string)=协议号 data(list)=数据内容
def pack(comid,data):
    global datafmt
    if comid not in datafmt:
        print comid,'协议fmt格式读取错误'
    
    fmtStr = datafmt[comid]
    fmtStrRes = []
    idx = 0
    fixString ={}

    for k in fmtStr:
        if k=='n':
            fmtStrRes.append('h')
        elif k=='b':
            fmtStrRes.append('b')
        elif k=='u':
            fmtStrRes.append('I')
        elif k=='i':
            fmtStrRes.append('i')
        elif k=='s':
            _strLength = len(data[idx])
            fixString[idx] = _strLength #记录str的长度
            fmtStrRes.append('h'+ str(_strLength) +'s')
        idx = idx + 1
    
    fmt = '<'+''.join(fmtStrRes)

    #将字符串的长度值插入到data中
    if len(fixString)>0:
        idx = 0
        for k,v in fixString.items():
            data.insert(k+idx,v)
            idx = idx + 1
    
    print 'data=',data
    print 'fmt=',fmt

    res = struct.pack(fmt,*data)
    print 'pack=',res

pack('1001',['abc','中文',3,7654])

运行看看:


至于unpack和拆包粘包,就交给你来自己练手了,应该没有难度了吧!

#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

python实现QQ机器人

作者:ciniao 时间:2011-08-30 分类:python 11 Comments

    刺鸟原创文章,转载请注明出处
    目前网上有不少的实现QQ机器人的方法,都不太稳定甚至都已经失效了,而且我们还要冒着QQ号被盗用的风险。其实我们可以自己实现一个QQ自动应答的机器人,思路非常简单:通过模拟登录3G版QQ,来实现相关的操作:

    一、首先我们得看看3GQQ的相关协议
    为此,我们需要一个支持WAP的浏览器,可以使用Firefox的wmlbrowser插件,打开FF后,访问地址:https://addons.mozilla.org/zh-CN/firefox/search/?q=wmlbrowser&cat=all&x=17&y=11
    
    二、进入3GQQ的进行协议分析
    3GQQ的地址是:http://pt.3g.qq.com/s?aid=nLogin3gqq 用安装了wmlbrowser插件的FF打开页面后,启用firebug,即可监视提交的数据。
    

    三、源代码
    以下源代码只实现了登陆和收发消息,如果需要更多的功能,可通过上面的方法自己监视协议即可,若你开发了更多的功能,可留言告诉我,一起完善该程序:
    *注:代码中的HTTP请求写了2个版本的,若要采用pycurl的版本,请到http://www.lfd.uci.edu/~gohlke/pythonlibs/下载对应的pycurl库。

    

#coding:utf-8
#基于python2.6版本开发
import httplib,urllib,os,threading,re
import sys 
reload(sys) 
sys.setdefaultencoding('utf8') 

class PYQQ:
    def __init__(self):
        self.reqIndex = 0
    
    #HTTP请求
    def httpRequest(self,method,url,data={}):
        try:
            _urld = httplib.urlsplit(url)
            conn = httplib.HTTPConnection(_urld.netloc,80,True,3)
            conn.connect()
            data = urllib.urlencode(data)
            if method=='get':
                conn.putrequest("GET", url, None)
                conn.putheader("Content-Length",'0')
            elif method=='post':
                conn.putrequest("POST", url)
                conn.putheader("Content-Length", str(len(data)))
                conn.putheader("Content-Type", "application/x-www-form-urlencoded")
            
            conn.putheader("Connection", "close")
            conn.endheaders()
            
            if len(data)>0:
                conn.send(data)

            f = conn.getresponse()
            self.httpBody = f.read().encode('gbk')
            f.close()
            conn.close()
        except:
            self.httpBody=''
        return self.httpBody
    #HTTP请求的pycurl版本,和上面的程序选一即可
    def httpRequest_(self,method,url,data={}):
        import pycurl,StringIO
        c = pycurl.Curl()
        c.setopt(pycurl.URL,url)
        if method=='post':
            import urllib
            c.setopt(c.POSTFIELDS, urllib.urlencode(data))
        
        c.fp = StringIO.StringIO()
        c.setopt(pycurl.WRITEFUNCTION,c.fp.write)
        c.perform()

        self.httpBody = c.fp.getvalue().encode('gbk')
        del c.fp
        c.close()
        c = None
        return self.httpBody
    #通过首尾获取字符串的内容
    def getCon(self,start,end):
        findex = self.httpBody.find(start)
        if findex == -1 : return None
        tmp = self.httpBody.split(start)
        
        eindex = tmp[1].find(end)
        if eindex == -1:
            return tmp[1][0:]
        else:
            return tmp[1][0:eindex]
    #获取postfield的值
    def getField(self,fd):
        KeyStart = '<postfield name="'+ str(fd) +'" value="'
        return self.getCon(KeyStart,'"/>')
    #获取登陆验证码,并保存至当前目录的qqcode.gif
    def getSafecode(self):
        url = self.getCon('<img src="','"')
        import urllib2
        pager = urllib2.urlopen(url)
        data=pager.read()
        file=open(os.getcwd()+'\qqcode.gif','w+b')
        file.write(data)
        file.close()
        return True
    #登陆QQ
    def login(self):
        self.qq = raw_input('请输入QQ号:')
        self.pwd = raw_input('请输入密码:')
        s1Back = self.httpRequest('post','http://pt.3g.qq.com/handleLogin',{'r':'324525157','qq':self.qq,'pwd':self.pwd,'toQQchat':'true','q_from':'','modifySKey':0,'loginType':1})

        if s1Back.find('请输入验证码')!=-1:
            self.sid = self.getField('sid')
            self.hexpwd = self.getField('hexpwd')
            self.extend = self.getField('extend')
            self.r_sid = self.getField('r_sid')
            self.rip = self.getField('rip')

            if self.getSafecode():
                self.safeCode = raw_input('请输入验证码(本文件同目录的qqcode.gif):')
            else:
                print '验证码加载错误'
            
            postData = {'sid':self.sid,'qq':self.qq,'hexpwd':self.hexpwd,'hexp':'true','auto':'0',
                        'logintitle':'手机腾讯网','q_from':'','modifySKey':'0','q_status':'10',
                        'r':'271','loginType':'1','prev_url':'10','extend':self.extend,'r_sid':self.r_sid,
                        'bid_code':'','bid':'-1','toQQchat':'true','rip':self.rip,'verify':self.safeCode,
            }
            s1Back = self.httpRequest('post','http://pt.3g.qq.com/handleLogin',postData)
        
        self.sid = self.getCon('sid=','&')
        print '登陆成功'
        self.getMsgFun()    
    #定时获取消息
    def getMsgFun(self):
        self.reqIndex = self.reqIndex + 1
        s2Back = self.httpRequest('get','http://q32.3g.qq.com/g/s?aid=nqqchatMain&sid='+self.sid)

        if s2Back.find('alt="聊天"/>(')!=-1:
            #有新消息,请求获取消息页面
            s3back = self.httpRequest('get','http://q32.3g.qq.com/g/s?sid='+ self.sid + '&aid=nqqChat&saveURL=0&r=1310115753&g_f=1653&on=1')
            
            #消息发起者的昵称
            if s3back.find('title="临时会话')!=-1:
                _fromName = '临时对话'
            else:
                _fromName = self.getCon('title="与','聊天')
            
            #消息发起者的QQ号
            _fromQQ = self.getCon('num" value="','"/>') 
            
            #消息内容
            _msg_tmp = self.getCon('saveURL=0">提示</a>)','<input name="msg"')
            crlf = '\n'
            if _msg_tmp.find('\r\n')!=-1: crlf = '\r\n'
            _msg = re.findall(r'(.+)<br/>'+ crlf +'(.+)<br/>',_msg_tmp)
            
            for _data in _msg:
                self.getMsg({'qq':_fromQQ,'nick':_fromName,'time':_data[0],'msg':str(_data[1]).strip()})
        
        if self.reqIndex>=30:
            #保持在线
            _url = 'http://pt5.3g.qq.com/s?aid=nLogin3gqqbysid&3gqqsid='+self.sid
            self.httpRequest('get',_url)
            self.reqIndex = 0

        t = threading.Timer(2.0,self.getMsgFun)
        t.start()    
    #发送消息
    #qq 目标QQ
    #msg 发送内容
    def sendMsgFun(self,qq,msg):
        msg = unicode(msg,'gbk').encode('utf8')
        postData = {'sid':self.sid,'on':'1','saveURL':'0','saveURL':'0','u':qq,'msg':str(msg),}
        s1Back = self.httpRequest('post','http://q16.3g.qq.com/g/s?sid='+ self.sid +'&aid=sendmsg&tfor=qq',postData)
        print '发送消息给',qq,'成功'    
    #收到消息的接口,重载或重写该方法
    def getMsg(self,data):
        print data['time'],"收到",data['nick'],"(",data['qq'],")的新消息"

        self.sendMsgFun(data['qq'],data['nick']+',我收到了你的消息:'+ data['msg'])

QQ = PYQQ()
QQ.login()


#本文由刺鸟原创,欢迎转载,但请保留出处,也欢迎对本文感兴趣的同学多多交流。#

用PHP对memcache的数据(key-value)进行遍历操作

作者:ciniao 时间:2011-08-27 分类:php No Comments

    目前,用到memcache的公司和网站也越来越多。Memcache的客户端操作一般都只提供了get,set等简单的操作,这些操作都是非常高效的。  虽然memcache是个key-value存储的系统,但是在某些时候,我们可能需要遍历memcache的数据。     

    PHP代码实现
$host='127.0.0.1';
$port=11211;
$mem=new Memcache();
$mem->connect($host,$port);

$items=$mem->getExtendedStats('items');
$items=$items["$host:$port"]['items'];
foreach($items as $key=>$values){
    $number=$key;
    $str=$mem->getExtendedStats ("cachedump",$number,0);
    $line=$str["$host:$port"];
    if( is_array($line) && count($line)>0){
        foreach($line as $key=>$value){
            echo $key.'=>';
            print_r($mem->get($key));
            echo "\r\n";
        }
    }

    由此可以实现查找memcache某个前缀的key的数据,或者查询某些value的key。甚至实现数据库的like功能。请注意:遍历memcache的操作并没有memcache的get操作那么高效。

    转自:http://blog.csdn.net/sunli1223/article/details/3193146