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

python中xrange和range的异同

作者:ciniao 时间:2011-09-05 分类:python No Comments

range
    函数说明:range([start,] stop[, step]),根据start与stop指定的范围以及step设定的步长,生成一个序列。
range示例: 
>>> range(5) 
[0, 1, 2, 3, 4] 
>>> range(1,5) 
[1, 2, 3, 4] 
>>> range(0,6,2)
[0, 2, 4]

xrange
    函数说明:用法与range完全相同,所不同的是生成的不是一个数组,而是一个生成器。
xrange示例: 
>>> xrange(5)
xrange(5)
>>> list(xrange(5))
[0, 1, 2, 3, 4]
>>> xrange(1,5)
xrange(1, 5)
>>> list(xrange(1,5))
[1, 2, 3, 4]
>>> xrange(0,6,2)
xrange(0, 6, 2)
>>> list(xrange(0,6,2))
[0, 2, 4]

    由上面的示例可以知道:要生成很大的数字序列的时候,用xrange会比range性能优很多,因为不需要一上来就开辟一块很大的内存空间,这两个基本上都是在循环的时候用:
for i in range(0, 100): 
print i 
for i in xrange(0, 100): 
print i 

    这两个输出的结果都是一样的,实际上有很多不同,range会直接生成一个list对象:
a = range(0,100) 
print type(a) 
print a 
print a[0], a[1] 

    输出结果:
<type 'list'>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
0 1

    而xrange则不会直接生成一个list,而是每次调用返回其中的一个值:
a = xrange(0,100) 
print type(a) 
print a 
print a[0], a[1] 

    输出结果:
<type 'xrange'>
xrange(100)
0 1

    所以xrange做循环的性能比range好,尤其是返回很大的时候,尽量用xrange吧,除非你是要返回一个列表。

#本文系转载文章,原文作者及来源不详。#

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()


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