Differences between revisions 15 and 16
Revision 15 as of 2004-11-13 08:39:44
Size: 25409
Editor: 61
Comment:
Revision 16 as of 2004-11-13 08:41:49
Size: 25437
Editor: 61
Comment:
Deletions are marked like this. Additions are marked like this.
Line 675: Line 675:
#!/usr/local/bin/python --

TableOfContents

freebsd

qmail下使用python编写脚本来过滤邮件和发送短信

实现qmail下邮件过滤和邮件到达短信通知

作者:梅劲松

时间:2004年9月10日

感谢:HD、刘鑫

短信这么火热,如果自己的邮件服务器也能和短信结合起来,那多么好啊?在qmail下,我用qmail_queue来解决邮件过滤和短信到达通知。

本邮件系统在freebsd 4.10上实施成功。

1. 对于邮件系统的安装,没有太多说的。建议大家看HD的[" http://bsd.huangdong.com/server/mail/qmail.html "]来安装。如果已经安装了有qmail+vpopmail+qmail_queue补丁的,请直接跳到4继续安装。在新装邮件服务器时有几点需要注意的是,安装qmail的时候应该将步骤改为:

cd /usr/ports/mail/qmail
make WITH_QMAILQUEUE_PATCH=yes WITH_BIG_TODO_PATCH=yes WITH_OUTGOINGIP_PATCH=yes WITH_PRESERVE_CONFIG_FILES=yes extract
cd /usr/ports/mail/qmail/work/qmail-1.03
fetch http://www.nimh.org/dl/qmail-smtpd.c
cd /usr/ports/mail/qmail
make WITH_QMAILQUEUE_PATCH=yes WITH_BIG_TODO_PATCH=yes WITH_OUTGOINGIP_PATCH=yes WITH_PRESERVE_CONFIG_FILES=yes install
make disable-sendmail
make enable-qmail
make clean

在安装vpopmail的时候可能因为port更新的原因让使用vpopmail的时候出现奇怪问题,最好是安装webmin将建立的vpopmail数据库删除后,重新建立,并指定数据库的用户权限。由于webmin的安装不是本文重点,这里将忽略。

2、关于文章里面的杀毒部分因为系统效率问题,我没有安装。各位请自行决定是否安装。

3、反垃圾邮件当然需要,我用的是[" http://anti-spam.org.cn "]的cbl+服务。使用的时候只需要将smtpd的脚本改为:

QMAILDUID=`/usr/bin/id -u qmaild`
NOFILESGID=`/usr/bin/id -g qmaild`
exec /usr/local/bin/tcpserver -H -R -l 0 -p -x \
        /usr/local/vpopmail/etc/tcp.smtp.cdb -u"$QMAILDUID" \
        -g"$NOFILESGID" -v -c100 0 smtp rblsmtpd \
        -r cblplus.anti-spam.org.cn \
        -r relays.ordb.org \
        /var/qmail/bin/qmail-smtpd /usr/local/vpopmail/bin/vchkpw-smtpd /usr/bin/true 2>&1

从[" http://anti-spam.org.cn/cgi-bin/rblclient/(你的邮件服务器的dns服务器IP地址) "]看到你的邮件服务器的流量和使用cbl+服务的情况。

4、这里是重点了。过滤和短信到达是需要qmail_queue来完成的,一定要打这个包。

使用python来实现这个功能,当然需要安装python啦。

cd /usr/ports/lang/python
make;make install;make clean

一般来讲这个安装是非常顺利的。 安装结束后。

cd /var/qmail/bin

编辑qmfilt.py,内容如下:

import os, sys, string, time, traceback, re, socket

Version  = '1.1'
PyVersion  = '1.0'
Logging  = 1
Debug    = 0
QmailD   = 82#这里需要和你/etc/password里面qmaild用户的一样
LogFile  = None
TestMode = None
Filters  = []
MailEnvelope = ''
MailData     = ''
ValidActions = { 'trap': '', 'drop' : '', 'block' :'' }

#这里是你通过ucp协议将消息发送到哪个服务器的哪个端口
def mail_sms(msg):
    host = "xxx.xxx.114.2"
    port = 9999
    buf = 500
    addr = (host,port)

    # Create socket
    UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    def_msg = msg;
    UDPSock.sendto(def_msg,addr)

    # Close socket
    UDPSock.close()

def openlog():
  global LogFile

  if not Logging:
     return
  try:
    LogFile = os.open('/var/log/qmfilt', os.O_APPEND|os.O_WRONLY|os.O_CREAT, 0744)
  except:
    if TestMode:
      print "Can't create /var/log/qmfilt"
    else:
      # Indicate a write error
      sys.exit(53)

def log(message):
  message = time.asctime(time.localtime(time.time())) + " - " + message + "\n"
  if Logging:
    os.write(LogFile, message)
    # Indicate a write error
    #sys.exit(53)
  if TestMode:
    print message


def showFilters():

  if len(Filters) == 0:
    print "No filters"
  for f in Filters:
    print "Filter -  %s - Action: %s  Regex: %s" % (f[0], f[1], f[2])

def readFilters():
  global Filters

  try:
    file = open('/var/qmail/control/qmfilt', 'r')
    configLines = file.readlines()
    file.close()
  except:
    log("Can't read /var/qmail/control/qmfilt")
    if not TestMode:
      # Indicate a 'cannot read config file error'
      sys.exit(53)

  reg = re.compile('(.*)::(.+)::(.+)::')
  for line in configLines:
    if line[0] == '#':
      continue
    m = reg.match(line)
    if m != None:
      action = string.lower(m.group(2))
      if not ValidActions.has_key(action):
         log("Invalid action in config file [%s] - SKIPPED" %(action))
         continue
      Filters.append( [m.group(1), string.lower(m.group(2)), m.group(3)] )

def readDescriptor(desc):
  result = ''
  while 1:
    data = os.read(desc, 16384)
    if data == '':
      break
    result = result + data

  return result

def writeDescriptor(desc, data):
  while data:
    num  = os.write(desc, data)
    data = data[num:]

  os.close(desc)


def filterHits():

  for regEx in Filters:
    reg = re.compile(regEx[2], re.M|re.I)
    match = reg.search(MailData)
    if match:
      log("Matched [%s]" % (regEx[0]))
      return regEx[1], regEx[0]

  return None,None


def storeInTrap(hit):
  try:
    pid = os.getpid()
    mailFile = os.open('/var/qmail/qmfilt/qmfilt.%s' %(pid), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
  except:
    log("Can't create /var/qmail/qmfilt/qmfilt.%s" %(pid))
    # Indicate a write error
    sys.exit(53)

  try:
    #如果不屏蔽会出现很多问题,至于为什么我还没弄明白,如果你找到问题所在,请告诉我。
    """(None, inode, None, None, None, None, size, None, None, None) = os.fstat(mailFile)"""
    os.rename('/var/qmail/qmfilt/qmfilt.%s' %(pid), '/var/qmail/qmfilt/%s.mail' %(inode))
  except:
    log("Can't rename /var/qmail/qmfilt/qmfilt.%s -> /var/qmail/qmfilt/%s.mail" %(pid, inode))
    # Indicate a write error
    sys.exit(53)

  try:
    envFile = os.open('/var/qmail/qmfilt/%s.envelope' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
    mesgFile = os.open('/var/qmail/qmfilt/%s.qmfilt' %(inode), os.O_WRONLY|os.O_CREAT|os.O_EXCL, 0744)
    writeDescriptor(mailFile, MailData)
    writeDescriptor(envFile,  MailEnvelope)
    writeDescriptor(mesgFile, "Matched filter [ %s] to file %s" %(hit, inode))
    log("Matched filter [ %s] to file %s" %(hit, inode))
  except:
    log("Can't create/write files into trap")
    # Indicate a write error
    sys.exit(53)

  return 0


def sendToQmailQueue():

  # Open a pipe to qmail queue
  fd0 = os.pipe()
  fd1 = os.pipe()
  pid = os.fork()
  if pid < 0:
    log("Error couldn't fork")
    sys.exit(81)
  if pid == 0:
    # This is the child
    os.dup2(fd0[0], 0)
    os.dup2(fd1[0], 1)
    i = 2
    while (i < 64):
      try:
        os.close(i)
      except:
        pass
      i = i + 1
    os.chdir('/var/qmail')
    #time.sleep(10)
    os.execv("bin/qmail-queue", ('bin/qmail-queue',))
    log("Something went wrong")
    sys.exit(127)  # This is only reached on error
  else:
    # This is the parent
    # close the readable descriptors
    os.close(fd0[0])
    os.close(fd1[0])


  # Send the data
  recvHdr = "Received: (QMFILT: %s); " %(Version)
  recvHdr = recvHdr + time.strftime("%d %b %Y %H:%M:%S", time.gmtime(time.time()))
  recvHdr = recvHdr + " -0000\n"
  os.write(fd0[1], recvHdr)
  writeDescriptor(fd0[1], MailData)
  writeDescriptor(fd1[1], MailEnvelope)

  # Catch the exit code to return
  result = os.waitpid(pid, 0)[1]
  if PyVersion > '1.5.1':
    if os.WIFEXITED(result):
      return os.WEXITSTATUS(result)
    else:
      log("Didn't exit normally")
      sys.exit(81)
  else:
    return result

def conver(msg):
  msg = msg.replace(chr(00),' ')
  msg = msg.replace('F','From:',1)
  msg = msg.replace(' T',' To:')
  return msg


def main(argv, stdout, environ):

  global TestMode
  global MailData
  global MailEnvelope
  global PyVersion

  PyVersion = string.split(sys.version)[0]

  if len(argv) > 1 and argv[1] == '--test':
    TestMode = 1

  os.setuid(QmailD)

  # Insure Environment is OK

  # Try to open log
  openlog()

  if not os.path.exists('/var/qmail/qmfilt'):
    # Indicate a problem with the queue directory
    log("Directory /var/qmail/qmfilt doesn't exist")
    if not TestMode:
      sys.exit(61)

  if os.path.exists('/var/qmail/control/qmfilt'):
    readFilters()
  else:
    if TestMode:
      print "No filter file /var/qmail/control/qmfilt - no filters applied"

  if TestMode:
    showFilters()

  # Go no further if in test mode
  if TestMode:
    sys.exit(0)



  # Get the data

  # Read the data
  MailData     = readDescriptor(0)

  # Read the envelope
  MailEnvelope = readDescriptor(1)
  if Debug:
    log(MailData)
    log(MailEnvelope)

  action,hit = filterHits()

  if action == 'trap':
    storeInTrap(hit)
  if action == 'block':
    log("Matched filter [ %s] and email was BLOCKED/Refused delivery" %(hit))
    sys.exit(31)
  if action == 'drop':
    log("Matched filter [ %s] and email was DROPPED" %(hit))
  if action == None:
    sendToQmailQueue()
  #Log
  log(conver(MailEnvelope))
  #send sms
  mail_sms(conver(MailEnvelope))

  if Debug:
    log("qmailqueue returned [%d]" %(result))
  sys.exit(0)

if __name__ == "__main__":
  try:
    main(sys.argv, sys.stdout, os.environ)

  # Catch the sys.exit() errors
  except SystemExit, val:
    sys.exit(val)
  except:
    # return a fatal error for the unknown error
    if TestMode:
      traceback.print_exc()
    sys.exit(81)

然后在cd /var/qmail/control 编辑qmfilt内容如下:

#
# This is the qmfilt control file
# If any email comes in that matches this
# filter, the mail will be redirected
# to the filter directory
#
# A filter regular expression must be on a single
# line and in between a pair of '::'
#
# Valid actions:
# trap - store in the trap directory
# block - tell the smtp sender that we won't take the mail
# drop - accept the mail, but don't queue it
#

# This will match any executable Visual Basic Scripts
VisualBasic::block::^Content-Type: application/octet-stream;\s+name="(.*\.vbs)"::
SHS::block::^Content-Type: application/octet-stream;\s+name="(.*\.shs)"::
SHB::block::^Content-Type: application/octet-stream;\s+name="(.*\.shb)"::
COM::block::^Content-Type: application/octet-stream;\s+name="(.*\.com)"::
EXE::block::^Content-Type: application/octet-stream;\s+name="(.*\.exe)"::
SCR::block::^Content-Type: application/octet-stream;\s+name="(.*\.scr)"::
PIF::block::^Content-Type: application/octet-stream;\s+name="(.*\.pif)"::
BAT::block::^Content-Type: application/octet-stream;\s+name="(.*\.bat)"::

# This is the Outlook date overflow bug
DATE::block::^Date:.{160,}::

在/var/log下建立qmfilt文件,内容为空

chown qmaild qmfilt

在/var/qmail下建立qmfilt目录。

chown -R qmaild qmfilt

好了,来让我们的程序启用吧 先测试一把

/var/qmail/bin/qmfilt.py --test

如果返回了先定义的qmfilt里面的内容,恭喜成功了一半了。内容应该和下面的相似:

Filter - VisualBasic - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.vbs)"
Filter - SHS - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.shs)"
Filter - SHB - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.shb)"
Filter - COM - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.com)"
Filter - EXE - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.exe)"
Filter - SCR - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.scr)"
Filter - PIF - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.pif)"
Filter - BAT - Action: block Regex: ^Content-Type: application/octet-stream;\s+name="(.*\.bat)"
Filter - DATE - Action: block Regex: ^Date:.{160,}

好了,让我们的过滤器用起来吧

cd /usr/local/vpopmail/etc

编译tcp.smtp内容如下

127.:allow,RELAYCLIENT="",QMAILQUEUE="bin/qmfilt.py"
:allow,QMAILQUEUE="bin/qmfilt.py"

然后生成tcp.smtp.cdb文件

tcprules tcp.smtp.cdb tcp.smtp.tmp < tcp.smtp

发个带exe为附件的邮件看看。如果成功过滤,应该在/var/log/qmfilt里面看到如下信息:

Fri Sep 10 14:37:01 2004 - Matched filter [ EXE] and email was BLOCKED/Refused delivery
Fri Sep 10 14:38:09 2004 - Matched [SCR]

如果你是用foxmail等客户端发送带exe为结尾的邮件的话,服务器会提示你拒绝接收的。到这里,你的邮件过滤已经成功了。

你可以在/var/qmail/control的qmfilt文件里面定义你需要过滤的指定代码,然后测试规则是否生效了。

关于短信到达通知,因为安全和商业上的问题。不能将服务器端的代码贴出来。我将机制给大家讲一下。

qmfilt.py这个程序里面mail_sms用udp将类似 From:[email protected] To:[email protected] 发送到指定的服务器上。服务器接收到这个数据包后,将这个数据包的内容用短信发送出去。

当然可以将邮件主题等信息一起发送到手机上,大家根据自己的情况更改吧。

这个程序是根据[" http://sourceforge.net/projects/qmfilt/ "]这个项目更改的,但是直接用他的代码会造成不能收信,大家可以在这个项目的cvs关注他的代码修改情况。

结束,谢谢!

python

python调用com以及com事件

python调用有事件发生的com时,需要一些技巧。

我们以ie这个com为例来讲解一下我的经验。

首先我们需要pywin32这个模块的支持,它提供了我们调用com便利直接方法。你可以www.sf.net搜索并下载它。

先运行如下代码:

import win32gui
import win32com
import win32com.client
import pythoncom
import time

        
class EventHandler:

    def OnVisible(self, visible):
        global bVisibleEventFired
        bVisibleEventFired = 1
    def OnDownloadBegin(self):
        print "DownloadBegin"
    def OnDownloadComplete(self):
        print "DownloadComplete"
    def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
        print "documentComplete of %s" % URL

#这里用EventHandler类来处理ie中发生的事件,这里的函数名必须和事件名称一致。
ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
ie.Visible = 1
ie.Navigate("www.aawns.com")
#这里是等待事件的发生
pythoncom.PumpMessages()
ie.Quit()

我们看到,程序运行正常,能打开我们指定的站点,并各事件被触发后都能作出正确的反映。

但是假如我们希望在事件发生后,能调用我们继承下来的一些方法和属性。你会发现无法使用。

如下代码将展示这个例子

# -*- coding: cp936 -*-
import win32gui
import win32com
import win32com.client
import pythoncom
import time

class test:
    def runtest(self):
        print 'tuntest'
        
class EventHandler:

    def OnVisible(self, visible):
        global bVisibleEventFired
        bVisibleEventFired = 1
    def OnDownloadBegin(self):
        print "DownloadBegin"
    def OnDownloadComplete(self):
        print "DownloadComplete"
    def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
        print "documentComplete of %s" % URL
        #在这里我们再调用test的runtest方法,看是否继承成功。
        self.runtest()

class runcom(test):
    def __init__(self):
        ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
        ie.Visible = 1
        ie.Navigate("www.aawns.com")
        #这里调用test的runtest方法,看是否继承成功。
        self.runtest()
        pythoncom.PumpMessages()
        ie.Quit()
a=runcom()

运行结果是错误的。

tuntest
DownloadBegin
DownloadComplete
DownloadBegin
DownloadComplete
documentComplete of http://www.aawns.com/
pythoncom error: Python error invoking COM method.
Traceback (most recent call last):
  File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 283, in _
Invoke_
    return self._invoke_(dispid, lcid, wFlags, args)
  File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 288, in _
invoke_
    return S_OK, -1, self._invokeex_(dispid, lcid, wFlags, args, None, None)
  File "C:\Python23\Lib\site-packages\win32com\server\policy.py", line 550, in _
invokeex_
    return func(*args)
  File "D:\python\test.py", line 24, in OnDocumentComplete
    self.runtest()
  File "C:\Python23\Lib\site-packages\win32com\client\__init__.py", line 454, in
 __getattr__
    raise AttributeError, "'%s' object has no attribute '%s'" % (repr(self), att
r)
exceptions.AttributeError: '<win32com.client.COMEventClass instance at 0x1554295
2>' object has no attribute 'runtest'

我们看到test类的runtest方法并没有被继承进去,为什么呢?我的理解是因为com的事件模式让你无法继承python中的self,因为在调用ie的时候并不是用EventHandler的实例而是将这个类作为了事件处理的方法(不知道这里理解是否正确,如果有更好的理解。我们交流)

经过查找了很多资料和试探了很多方法,只有采用全局变量的方式才能在事件和各个类之间传递数据。代码变更成了这样

# -*- coding: cp936 -*-
import win32gui
import win32com
import win32com.client
import pythoncom
import time

      
class EventHandler:

    def OnVisible(self, visible):
        global bVisibleEventFired
        bVisibleEventFired = 1
    def OnDownloadBegin(self):
        print "DownloadBegin"
        #先继承全局变量增加一个字符串
        global testlist
        testlist.append("DownloadBegin")
    def OnDownloadComplete(self):
        print "DownloadComplete"
        #先继承全局变量增加一个字符串
        global testlist
        testlist.append("DownloadComplete")
    def OnDocumentComplete(self, pDisp = pythoncom.Missing , URL = pythoncom.Missing):
        print "documentComplete of %s" % URL
        #先继承全局变量再打印
        global testlist
        print testlist

class runcom:
    def __init__(self):
        global testlist
        ie = win32com.client.DispatchWithEvents("InternetExplorer.Application", EventHandler)
        ie.Visible = 1
        ie.Navigate("www.aawns.com")
        #打印全局变量
        print testlist
        pythoncom.PumpMessages()
        ie.Quit()
testlist=[]
a=runcom()

可以看到,用全局变量的方式解决了于事件内传输数据的问题。

没有解决的问题:使用Twisted的时候也有相应的事件,如何保证Twisted 和com中的事件都被触发?

使用python写nt服务

如果我们想让系统启动的时候就执行某个程序,windows系统和unix系统是不一样的,对于unix只需要将要执行的命令放到rc.local中,系统重新启动的时候就可以加载了。windows就麻烦多了,如果你将程序放到启动组中,只有输入了密码后,程序才被执行,如果想在系统一启动的时候就执行程序,必须使用nt服务。 python下如何使用nt服务,其实很简单。 下载python的win32支持。我使用的是:pywin32-202.win32-py2.3.exe安装好后就可以来写我们的服务了。 我们先来建立一个空的服务,建立test1.py这个文件,并写入如下代码:

# -*- coding: cp936 -*-
import win32serviceutil
import win32service
import win32event

class test1(win32serviceutil.ServiceFramework):
    _svc_name_ = "test_python"
    _svc_display_name_ = "test_python"
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)

    def SvcStop(self):
        # 先告诉SCM停止这个过程
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        # 设置事件
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        # 等待服务被停止
        win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)

if __name__=='__main__':
    win32serviceutil.HandleCommandLine(test1)

这里注意,如果你需要更改文件名,比如将win32serviceutil.HandleCommandLine(test1)中的test1更改为你的文件名,同时class也需要和你的文件名一致,否则会出现服务不能启动的问题。 在命令窗口执行,test1.py可以看到帮助提示

C:\>test1.py
Usage: 'test1.py [options] install|update|remove|start [...]|stop|restart [...]|
debug [...]'
Options for 'install' and 'update' commands only:
 --username domain\username : The Username the service is to run under
 --password password : The password for the username
 --startup [manual|auto|disabled] : How the service starts, default = manual
 --interactive : Allow the service to interact with the desktop.

C:\>

安装我们的服务

C:\>test1.py install
Installing service test_python to Python class C:\test1.test1
Service installed

C:\>

我们就可以用命令或者在控制面板-》管理工具-》服务中管理我们的服务了。在服务里面可以看到test_python这个服务,虽然这个服务什么都不做,但能启动和停止他。 下面我们来写个复杂点的服务。 改天写,要下班了。

使用python来发扑克牌

在QQ群里面出了个题目,发52张扑克牌到4个人,要随机,速度足够快。我写了一个,请大家指正。

# -*- coding: cp936 -*-
import random,time

class timer:
    def __init__(self):
        self.start= time.time() 
    def stop(self):
        self.end= time.time()
        return  "\n 本次运行已用时 %s秒"% (self.end-self.start)

class people:
        #定义四个玩牌的人
        jack = []
        python = []
        java = []
        stephen = []
        def print_jack(self):
            i=0
            tmp = ''
            while i < 13:
                tmp += self.jack[i]+' '
                i += 1
            return tmp
        def print_python(self):
            i=0
            tmp = ''
            while i < 13:
                tmp += self.python[i]+' '
                i += 1
            return tmp
        def print_java(self):
            i=0
            tmp = ''
            while i < 13:
                tmp += self.java[i]+' '
                i += 1
            return tmp
        def print_stephen(self):
            i=0
            tmp = ''
            while i < 13:
                tmp += self.stephen[i]+' '
                i += 1
            return tmp
        
class agent(people):
    def __init__(self):
        #定义一个空的列表存放牌
        self.card = []
        #定义洗牌的次数,并不是越大牌就越乱
        self.count = 52
        #定义一个基本的牌序
        self.base = ['A','2','3','4','5','6','7','8','9','10','J','Q','K']

    def arrangement(self):
        #重新排列纸牌
        i = 0
        while i < 13:
            self.card.insert(0,'黑桃'+self.base[i])
            i += 1
        i = 0
        while i < 13:
            self.card.insert(0,'红桃'+self.base[i])
            i += 1
        i = 0
        while i < 13:
            self.card.insert(0,'草花'+self.base[i])
            i += 1
        i = 0
        while i < 13:
            self.card.insert(0,'方块'+self.base[i])
            i += 1
            
    def shuffle(self):
        #洗牌
        i = 1
        while i < self.count:
            #产生一个随机数来确定要交换牌的位置
            position = random.randint(0,51)
            #临时取出一张牌准备用来交换位置
            tmp = self.card[position]
            #交换随机位置的牌和第一张牌
            self.card[position] = self.card[0]
            self.card[0] = tmp
            i += 1
            
    def outcards(self):
        #发牌
        i = 0
        while i < 52:
            if i <= 12:
                self.jack.insert(0,self.card[i])
            elif i >= 12 and i <= 25 :
                self.python.insert(0,self.card[i])
            elif i >= 25 and i <= 38 :
                self.java.insert(0,self.card[i])
            elif i >= 38 and i <= 52 :
                self.stephen.insert(0,self.card[i])
            i += 1

start=timer()
a=agent()
a.arrangement()
a.shuffle()
a.outcards()
print a.print_jack()
print a.print_python()
print a.print_java()
print a.print_stephen()
print start.stop()

另外,0.00999999046326好象是最小计数了,再小就计算成0秒了。

stephen (last edited 2009-12-25 07:17:56 by localhost)