菜谱 4:发送带图片的邮件

我们平时需要使用 Python 发送各类邮件,这个需求怎么来实现?答案其实很简单,smtplib 和email 库可以帮忙实现这个需求。smtplib 和 email 的组合可以用来发送各类邮件:普通文本,HTML 形式,带附件,群发邮件,带图片的邮件等等。我们这里将会分几节把发送邮件功能解释完成。

smtplib 是 Python 用来发送邮件的模块,email 是用来处理邮件消息。

发送带图片的邮件是利用 email.mime.multipart 的 MIMEMultipart 以及 email.mime.image 的 MIMEImage:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'

msgText = MIMEText(
    ''' Some  HTML  text  and an image.good!''', 'html', 'utf-8')
msgRoot.attach(msgText)

fp = open('/Users/1.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()

msgImage.add_header('Content-ID', '')
msgRoot.attach(msgImage)

smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msgRoot.as_string())
smtp.quit()

注意:这里的代码并没有把异常处理加入,需要读者自己处理异常。

阅读全文 »

菜谱 3:发送 HTML 形式的邮件

我们平时需要使用 Python 发送各类邮件,这个需求怎么来实现?答案其实很简单,smtplib 和email 库可以帮忙实现这个需求。smtplib 和 email 的组合可以用来发送各类邮件:普通文本,HTML 形式,带附件,群发邮件,带图片的邮件等等。我们这里将会分几节把发送邮件功能解释完成。

smtplib 是 Python 用来发送邮件的模块,email 是用来处理邮件消息。

发送 HTML 形式的邮件,需要 email.mime.text 中的 MIMEText 的 _subtype 设置为 html,并且 _text 的内容应该为 HTML 形式。其它的就和 菜谱 2:发送普通文本邮件 一样:

import smtplib
from email.mime.text import MIMEText

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msg = MIMEText(u"""
               

你好

""", 'html', 'utf - 8') msg['Subject'] = subject smtp = smtplib.SMTP() smtp.connect(smtpserver) smtp.login(username, password) smtp.sendmail(sender, receiver, msg.as_string()) smtp.quit()

注意:这里的代码并没有把异常处理加入,需要读者自己处理异常。

阅读全文 »

菜谱 2:发送普通文本邮件

我们平时需要使用 Python 发送各类邮件,这个需求怎么来实现?答案其实很简单,smtplib 和email 库可以帮忙实现这个需求。smtplib 和 email 的组合可以用来发送各类邮件:普通文本,HTML 形式,带附件,群发邮件,带图片的邮件等等。我们这里将会分几节把发送邮件功能解释完成。

smtplib 是 Python 用来发送邮件的模块,email 是用来处理邮件消息。

发送普通文本的邮件,只需要 email.mime.text 中的 MIMEText 的 _subtype 设置为 plain。首先导入 smtplib 和 mimetext。创建 smtplib.smtp 实例,connect 邮件 smtp 服务器,login 后发送:

import smtplib
from email.mime.text import MIMEText
from email.header import Header

sender = '***'
receiver = '***'
subject = 'python email test'
smtpserver = 'smtp.163.com'
username = '***'
password = '***'

msg = MIMEText(u'你好','plain','utf-8')#中文需参数‘utf-8',单字节字符不需要
msg['Subject'] = Header(subject, 'utf-8')

smtp = smtplib.SMTP()
smtp.connect(smtpserver)
smtp.login(username, password)
smtp.sendmail(sender, receiver, msg.as_string())
smtp.quit()

注意:这里的代码并没有把异常处理加入,需要读者自己处理异常。

阅读全文 »

菜谱 1:创建一个唯一的 session ID

在网站开发的时候,常常需要生成一个唯一的的会话(session)id,这个会话 id 存储在 cookie 中或者在其它安全的地方。


	# create a unique session id
	# input - string to use as part of the data used to create the session key.
	#         Although not required, it is best if this includes some unique 
	#         data from the site, such as it's IP address or other environment 
	#         information.  For ZOPE applications, pass in the entire ZOPE "REQUEST"
	#         object.
	def makeSessionId(st):
		import md5, time, base64, string
		m = md5.new()
		m.update('this is a test of the emergency broadcasting system')
		m.update(str(time.time()))
		m.update(str(st))
		return string.replace(base64.encodestring(m.digest())[:-3], '/', '$')

	def makeSessionId_nostring(st):
		import md5, time, base64
		m = md5.new()
		m.update('this is a test of the emergency broadcasting system')
		m.update(str(time.time()))
		m.update(str(st))
		return base64.encodestring(m.digest())[:-3].replace('/', '$')
        


输入参数:st,不限制 st 唯一,但是建议传入的 st 是唯一的,可以是 IP 或者一些环境信息。

输出:唯一的 session id 字符串。

阅读全文 »

flask+sqlalchemy+postgresql 异步方案

flask+sqlalchemy+postgresql 异步方案的Github 地址: https://github.com/sixu05202004/async-flask-sqlalchemy-postgresql 使用说明: 安装所需的插件: pip install -r requirements.txt 需要在postgresql中创建数据库 test_asyn 运行 python app.py -c 用来创建测试的表; 运行 python app.py 来启动服务器,运行 python client 来测试 结果如下:: localhost:async-flask-sqlalchemy-postgresql sixu05202004$ python client.py Sending 5 requests for http://localhost:8080/test/postgres/... @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] @ 5.05s got response [200] = 5.06s TOTAL SUM TOTAL = 5.06s 注意: 1.config.py 中需要修改测试数据库的用户名和密码,并且可以修改pool_size的数量; 2.python client.py num,比如:python client.py 100可以用来模拟更多的连接; 3.如果python client.py num中得num数大于config.py中的SQLALCHEMY_POOL_SIZE = 100时候,我们会发现有些数据库连接又存在阻塞的情况。 比如,我们把SQLALCHEMY_POOL_SIZE改成10,使用python client.py 30来测试,结果如下:: localhost:async-flask-sqlalchemy-postgresql sixu05202004$ python client.py 30 Sending 30 requests for http://localhost:8080/test/postgres/... @ 5.07s got response [200] @ 5.07s got response [200] @ 5.08s got response [200] @ 5.09s got response [200] @ 5.09s got response [200] @ 5.13s got response [200] @ 5.12s got response [200] @ 5.12s got response [200] @ 5.13s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.19s got response [200] @ 5.20s got response [200] @ 5.20s got response [200] @ 5.20s got response [200] @ 10.14s got response [200] @ 10.15s got response [200] @ 10.15s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.24s got response [200] @ 10.25s got response [200] @ 10.25s got response [200] @ 10.26s got response [200] = 10.28s TOTAL SUM TOTAL = 10.28s 这是因为 连接数 》SQLALCHEMY_POOL_SIZE + SQLALCHEMY_MAX_OVERFLOW ,我们可以通过一些设置来避免这种情况(NullPool ),但是实际上 postgresql 规定了最大连接数,这个是无法避免的,因此上述的设置最好不要使用 ...

阅读全文 »

Python中的staticmethod和classmethod以及self和cls

    今日在检查代码的时候,发现其中有四个名词:staticmethod、classmethod、self和cls。怕日后记不清楚了,因此特意记录下来,以便日后翻阅查看,并且可以帮助其他的 Pythoner 们。
  • staticmethod和classmethod

          staticmethod 可以称为“静态方法”,classmethod可以称为“类成员方法”。

          两者的相同点是:都可以通过实例或者类来访问;都不能访问实例的成员变量。

          两者之间不同点有:staticmethod无需参数,classmethod需要类变量作为参数传递(不是类的实例);classmethod可以访问类成员,staticmethod则不可以

          示例:

class a(object):

    @classmethod
    def test(cls):
        return "a.test"


class b(a):
    result = 'here!'

    def __init__(self, t='self here'):
        self.t = t

    def fun1(self):
        print "a.fun1", self
        print self.t, self.result

    @staticmethod
    def fun2():
        print "a.fun2"

    @classmethod
    def fun3(cls):
        print cls.test(), cls
        print cls.result

  • self 和 cls

        self 表示一个实例的本身,cls表示一个类的本身。类中普通的方法,第一个参数需要是self,它表示一个具体的实例本身。

        如果用了staticmethod,那么就可以无视这个self,而将这个方法当成一个普通的函数使用。
        而对于classmethod,它的第一个参数不是self,是cls,它表示这个类本身。

        示例:


class a(object):

    @classmethod
    def test(cls):
        return "a.test"


class b(a):
    result = 'here!'

    def __init__(self, t='self here'):
        self.t = t

    def fun1(self):
        print "a.fun1", self
        print self.t, self.result

    @staticmethod
    def fun2():
        print "a.fun2"

    @classmethod
    def fun3(cls):
        print cls.test(), cls
        print cls.result

if __name__ == '__main__':
    test = b()
    test.fun1()
    test.fun2()
    test.fun3()
    print test.test()



运行结果:

a.fun1 <__main__.b object at 0x0247B810>
self here here!
a.fun2
a.test <class '__main__.b'>
here!
a.test



阅读全文 »

如何在Python中使用Linux epoll

一.简介 在Python2.6中引入了一个访问Linux的epoll的API。本文使用Python3简要地展示一下这个API。 二.阻塞式套接字编程示例 例1是一个简单的Python服务器:它在端口8080上监听HTTP请求消息,并把它打印到控制台,返回给客户端发送一个HTTP响应消息。 * Line 9: 创建服务器套接字。 * Line 10: 在11行上允许 bind(),即使另一个程序最近在同一端口上监听。否则程序无法运行,直到一两分钟后,前面的程序使用该端口已经完成。 * Line 11: 服务器套接字绑定到这台机器上的所有可用的IPv4地址的端口8080上。 * Line 12: 告诉服务器套接字开始从客户端接受传入的连接。 * Line 14: 该程序将会在这里等待着,直到一个连接被接受。当发生这种情况时,服务器套接字将在这台机器上创建一个新的套接字用于与客户端交互。这个新的套接字将会被 accept() 调用的 clientconnection 对象来表示。地址对象表示连接的另一端的IP地址和端口号。 * Lines 15-17: 数据开始在传输直到一个完整的HTTP请求完成。HTTP 协议是在HTTP Made Easy(http://www.jmarshall.com/easy/http/)中有详细的描述。 * Line 18: 为了验证正确的操作,打印请求到控制台。 * Line 19: 发送响应到客户端。 * Lines 20-22: 关闭到客户端的连接以及监听服务器套接字。 官方的 HOWTO (http://docs.python.org/3.0/howto/sockets.html) 对使用 Python 进行套接字编程有着更详细的说明。 Example 1 (All examples use Python 3) 1 import socket 2 3 EOL1 = b'\n\n' 4 EOL2 = b'\n\r\n' 5 response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n' 6 response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n' 7 response += b'Hello, world!' 8 9 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 11 serversocket.bind(('0.0.0.0', 8080)) 12 serversocket.listen(1) 13 14 connectiontoclient, address = serversocket.accept() 15 request = b'' 16 while EOL1 not in request and EOL2 not in request: 17 request += connectiontoclient.recv(1024) 18 print(request.decode()) 19 connectiontoclient.send(response) 20 connectiontoclient.close() 21 22 serversocket.close() 例2在15行增加了一个循环用来反复处理客户端连接,并由用户(例如,一个键盘中断)中断。这更清楚地说明服务器套接字绝不会被用来与客户端的数据交换。相反,它接受从客户端的连接,然后在服务器计算机上创建一个新的套接字,用于与客户端交互。 在第23-24行上的 finally 语句,确保监听的服务器套接字总是关闭,即使在发生异常的时候。 Example 2 1 import socket 2 3 EOL1 = b'\n\n' 4 EOL2 = b'\n\r\n' 5 response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n' 6 response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n' 7 response += b'Hello, world!' 8 9 serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 11 serversocket.bind(('0.0.0.0', 8080)) 12 serversocket.listen(1) 13 14 try: 15 while True: 16 connectiontoclient, address = serversocket.accept() 17 request = b'' 18 while EOL1 not in request and EOL2 not in request: 19 request += connectiontoclient.recv(1024) 20 print('-'*40 + '\n' + request.decode()[:-2]) 21 connectiontoclient.send(response) 22 connectiontoclient.close() 23 finally: 24 serversocket.close() 三.异步套接字和Linux epoll的好处 在例2中展示的套接字服务器是阻塞式的,因为程序在等待直到一个事件的发生。在16行的 accept() 调用阻塞着直到一个客户端的连接被接受。19行的 recv() 调用阻塞着直到接收完客户端的数据(或者甚至没有接收到任何数据)。21行的 send() 调用阻塞着直到所有返回给客户端的数据在Linux上已排队准备传输。 当一个程序使用阻塞式套接字,它通常使用一个线程(甚至是专用的进程)与这些套接字进行通信。主程序的线程包含了监听服务器的套接字,它将接受客户端的连接。它将接受所有的客户端连接,并且把新建的套接字分配给一个独立的线程,这些独立的线程将会与客户端进行交互。因为每一个线程单独与一个客户端进行通信,任何堵塞不影响其他线程执行各自的任务。 这种使用多线程的阻塞式的套接字让代码简单明了,但是带 ...

阅读全文 »

flask-cache源码简要分析

flask-cache源码文件包含下面三个文件:
  1. __init__.py:flask-cache主要功能,提供了对用户使用的各种函数;
  2. backends.py:对werkzeug.contrib.cache进行一些封装以及扩展,为__init__.py提供多种缓存类型;
  3. jinja2ext.py:jinjia2的扩展,用于缓存jinjia2模版;

jinja2ext.py

首先我们来简要剖析下flask-cache扩展jinjia2,用于缓存jinjia2模版片段。该文件提供一个类:CacheExtension,该类的父类是jinjia2.ext.Extension,在父类的基础上对parse(self, parser)进行了扩展(修改)。具体的代码需要对jinjia2有些了解,尤其是jinjia2.ext.Extension,这里不啰嗦。

backends.py

这里包含了flask-cache对外提供的内置的缓存类型:NullCache、SimpleCache、MemcachedCache 、GAEMemcachedCache、RedisCache 、FileSystemCache、SASLMemcachedCache。

  • NullCache是对werkzeug.contrib.cache中的NullCache进行简单封装;
  • SimpleCache是对werkzeug.contrib.cache中的SimpleCache进行简单封装;
  • MemcachedCache是对werkzeug.contrib.cache中的MemcachedCache进行简单封装;
  • GAEMemcachedCache是对werkzeug.contrib.cache中的GAEMemcachedCache进行简单封装;
  • RedisCache是对werkzeug.contrib.cache中的RedisCache进行简单封装;
  • FileSystemCache是对werkzeug.contrib.cache中的FileSystemCache进行简单封装;
  • SASLMemcachedCache是flask-cache扩展了werkzeug.contrib.cache中的基础类MemcachedCache,实现了一个值跨多个不同的key存储;

__init__.py

文件里面只包含一个类:Cache。该类最重要的两个函数:cached和memoize,都能作为装饰器使用。cached主要用于缓存视图函数或者非视图函数,而memoize是Memoization <http://en.wikipedia.org/wiki/Memoization>的一种体现,主要用于缓存函数结果,这样多次调用的函数,只需要计算一次。

阅读全文 »

Python魔术方法指南之二

反射 你可以通过魔术方法控制控制使用 isinstance() 和 issubclass() 内置方法的反射行为。这些魔术方法是: __instancecheck__(self, instance) :检查一个实例是不是你定义的类的实例 __subclasscheck__(self, subclass) :检查一个类是不是你定义的类的子类 这些方法的用例似乎很少,这也许是真的。我不会花更多的时间在这些魔术方法上因为他们并不是很重要,但是他们的确反应了Python中的面向对象编程的一些基本特性:非常容易的去做一些事情,即使并不是很必须。这些魔术方法看起来并不是很有用,但是当你需要的时候你会很高兴有这种特性。 可以调用的对象 你也许已经知道,在Python中,方法也是一种高等的对象。这意味着他们也可以被传递到方法中就像其他对象一样。这是一个非常惊人的特性。在Python中,一个特殊的魔术方法可以让类的实例的行为表现的像函数一样,你可以调用他们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性让Python编程更加舒适甜美。 __call__(self, [args...]) :允许一个类的实例像函数一样被调用。实质上说,这意味着 x() 与 x.__call__() 是相同的。注意 __call__ 参数可变。这意味着你可以定义 __call__ 为其他你想要的函数,无论有多少个参数。 __call__ 在那些类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。用一个实例来表达最好不过了: class Entity: def __init__(self, size, x, y): self.x, self.y = x, y self.size = size def __call__(self, x, y): self.x, self.y = x, y 会话管理 在Python 2.5中,为了代码利用定义了一个新的关键词 with 语句。会话控制在Python中不罕见(之前是作为库的一部分被实现),直到 PEP343 被添加后。它被成为一级语言结构。你也许之前看到这样的语句: with open('foo.txt') as bar: # perform some action with bar 回话控制器通过包装一个 with 语句来设置和清理行为。回话控制器的行为通过两个魔术方法来定义: __enter__(self) 定义当使用 with 语句的时候会话管理器应该初始块被创建的时候的行为。注意 __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定。 __exit__(self, exception_type, exception_value, traceback) 定义当一个代码块被执行或者终止后会话管理器应该做什么。它可以被用来处理异常,清楚工作或者做一些代码块执行完毕之后的日常工作。如果代码块执行成功, exception_type , exception_value , 和 traceback 将会是 None 。否则的话你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,确认 __exit__ 在所有结束之后会返回 True 。如果你想让异常被会话管理器处理的话,那么就这样处理。 __enter__ 和 __exit__ 对于明确有定义好的和日常行为的设置和清洁工作的类很有帮助。你也可以使用这些方法来创建一般的可以包装其他对象的会话管理器。以下是一个例子。 class Closer: '''通过with语句和一个close方法来关闭一个对象的会话管理器''' def __init__(self, obj): self.obj = obj def __enter__(self): return self.obj # bound to target def __exit__(self, exception_type, exception_val, trace): try: self.obj.close() except AttributeError: # obj isn't closable print 'Not closable.' return True # exception handled successfully 以下是一个使用 Closer 的例子,使用一个FTP链接来证明(一个可关闭的套接字): from magicmethods import Closer from ftplib import FTP with Closer ( FTP ( 'ftp.somesite.com' )) as conn : ... conn . dir () ... conn . dir () with Closer ( int ( 5 )) as i : ... i += 1 ... Not closable. i 6 你已经看到了我们的包装器如何静默的处理适当和不适当的使用行为。这是会话管理器和魔术方法的强大功能。 创建对象的描述器 描述器是通过得到,设置,删除的时候被访问的类。当然也可以修改其他的对象。描述器并不是鼓励的,他们注定被一个所有者类所持有。当创建面向对象的数据库或者类,里面含有相互依赖的属性时,描述器将会非常有用。一种典型的使用方法是用不同的单位表示同一个数值,或者表示某个数据的附加属性(比如坐标系上某个点包含了这个点到远点的距离信息)。 为了构建一个描述器,一个类必须有至少 __get__ 或者 __set__ 其中一个,并且 __delete__ 被实现。让我们看看这些魔术方法。 __get__(self, instance, owner) 定义当描述器的值被取得的时候的行为, instance 是拥有者对象的一个实例。 owner 是拥有者类本身。 __set__(self, instance, value) 定义当描述器值被改变时候的行为。 instance 是拥有者类的一个实例 value 是要设置的值。 __delete__(self, instance) 定义当描述器的值被删除的行为。instance是拥有者对象的实例。 以下是一个描述器的实例:单位转换。 class Meter(object): '''Descriptor for a meter.''' def __init__(self, value=0.0): self.value = float(value) def __get__(self, instance, owner): return self.value def __set__(self, instance, value): self.value = float(value) class Foot(object): '''Descriptor for a foot.''' def __get__(self, instance, owner): ...

阅读全文 »

Python魔术方法指南之一

介绍 此教程为我的数篇文章中的一个重点。主题是魔术方法。 什么是魔术方法?他们是面向对象的Python的一切。他们是可以给你的类增加”magic”的特殊方法。他们总是被双下划线所包围(e.g. __init__ 或者 __lt__ )。然而他们的文档却远没有提供应该有的内容。Python中所有的魔术方法均在Python官方文档中有相应描述,但是对于他们的描述比较混乱而且组织比较松散。很难找到有一个例子(也许他们原本打算的很好,在开始语言参考中有描述很详细,然而随之而来的确是枯燥的语法描述等等)。 所以,为了修补我认为Python文档应该修补的瑕疵,我决定给Python中的魔术方法提供一些用平淡的语言和实例驱使的文档。我在开始已经写了数篇博文,现在在这篇文章中对他们进行总结。 我希望你能够喜欢这篇文章。你可以将之当做一个教程,一个补习资料,或者一个参考。本文章的目的仅仅是为Python中的魔术方法提供一个友好的教程。 构造和初始化 每个人都知道一个最基本的魔术方法, __init__ 。通过此方法我们可以定义一个对象的初始操作。然而,当我调用 x = SomeClass() 的时候, __init__ 并不是第一个被调用的方法。实际上,还有一个叫做 __new__ 的方法,来构造这个实例。然后给在开始创建时候的初始化函数来传递参数。在对象生命周期的另一端,也有一个 __del__ 方法。我们现在来近距离的看一看这三个方法: __new__(cls, [...) __new__ 是在一个对象实例化的时候所调用的第一个方法。它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法。 __new__ 方法相当不常用,但是它有自己的特性,特别是当继承一个不可变的类型比如一个tuple或者string。我不希望在 __new__ 上有太多细节,因为并不是很有用处,但是在 Python文档 中有详细的阐述。 __init__(self, […) 此方法为类的初始化方法。当构造函数被调用的时候的任何参数都将会传给它。(比如如果我们调用 x = SomeClass(10, 'foo') ),那么 __init__ 将会得到两个参数10和foo。 __init__ 在Python的类定义中被广泛用到。 __del__(self) 如果 __new__ 和 __init__ 是对象的构造器的话,那么 __del__ 就是析构器。它不实现语句 del x (所以代码将不会翻译为 x.__del__() )。它定义的是当一个对象进行垃圾回收时候的行为。当一个对象在删除的时候需要更多的清洁工作的时候此方法会很有用,比如套接字对象或者是文件对象。注意,因为当解释器退出的时候如果对象还存在,不能保证 __del__ 能够被执行 放在一起的话,这里是一个 __init__ 和 __del__ 实际使用的例子。 from os.path import join class FileObject: '''给文件对象进行包装从而确认在删除时文件流关闭''' def __init__(self, filepath='~', filename='sample.txt'): #读写模式打开一个文件 self.file = open(join(filepath, filename), 'r+') def __del__(self): self.file.close() del self.file 让定制的类工作起来 使用Python的魔术方法的最大优势在于他们提供了一种简单的方法来让对象可以表现的像内置类型一样。那意味着你可以避免丑陋的,违反直觉的,不标准的的操作方法。在一些语言中,有一些操作很常用比如: if instance.equals(other_instance): # do something 在Python中你可以这样。但是这会让人迷惑且产生不必要的冗余。相同的操作因为不同的库会使用不同的名字,这样会产生不必要的工作。然而有了魔术方法的力量,我们可以定义一个方法(本例中为 __eq__ ),就说明了我们的意思: if instance == other_instance: #do something 这只是魔术方法的功能的一小部分。它让你可以定义符号的含义所以我们可以在我们的类中使用。就像内置类型一样。 用于比较的魔术方法 Python对实现对象的比较,使用魔术方法进行了大的逆转,使他们非常直观而不是笨拙的方法调用。而且还提供了一种方法可以重写Python对对象比较的默认行为(通过引用)。以下是这些方法和他们的作用。 __cmp__(self, other) __cmp__ 是最基本的用于比较的魔术方法。它实际上实现了所有的比较符号( ,==,!=,etc.),但是它的表现并不会总是如你所愿(比如,当一个实例与另一个实例相等是通过一个规则来判断,而一个实例大于另外一个实例是通过另外一个规则来判断)。如果 self other 的话 __cmp__ 应该返回一个负数,当 self == o 的时候会返回0 ,而当 self other 的时候会返回正数。通常最好的一种方式是去分别定义每一个比较符号而不是一次性将他们都定义。但是 __cmp__ 方法是你想要实现所有的比较符号而一个保持清楚明白的一个好的方法。 __eq__(self, other) 定义了等号的行为, == 。 __ne__(self, other) 定义了不等号的行为, != 。 __lt__(self, other) 定义了小于号的行为, 。 __gt__(self, other) 定义了大于等于号的行为, = 。 举一个例子,创建一个类来表现一个词语。我们也许会想要比较单词的字典序(通过字母表),通过默认的字符串比较的方法就可以实现,但是我们也想要通过一些其他的标准来实现,比如单词长度或者音节数量。在这个例子中,我们来比较长度实现。以下是实现代码: class Word(str): '''存储单词的类,定义比较单词的几种方法''' def __new__(cls, word): # 注意我们必须要用到__new__方法,因为str是不可变类型 # 所以我们必须在创建的时候将它初始化 if ' ' in word: print "Value contains spaces. Truncating to first space." word = word[:word.index(' ')] #单词是第一个空格之前的所有字符 return str.__new__(cls, word) def __gt__(self, other): return len(self) len(other) def __lt__(self, other): return len(self) len(other) def __ge__(self, other): return len(self) = len(other) def __le__(self, other): return len(self) = len(oth ...

阅读全文 »