python中文参数的问题的问题

Python中的生产者消费者问题 - 文章 - 伯乐在线
& Python中的生产者消费者问题
我们将使用Python线程来解决Python中的生产者—消费者问题。这个问题完全不像他们在学校中说的那么难。
如果你对生产者—消费者问题有了解,看这篇博客会更有意义。
为什么要关心生产者—消费者问题:
可以帮你更好地理解并发和不同概念的并发。
信息队列中的实现中,一定程度上使用了生产者—消费者问题的概念,而你某些时候必然会用到消息队列。
当我们在使用线程时,你可以学习以下的线程概念:
Condition:线程中的条件。
wait():在条件实例中可用的wait()。
notify() :在条件实例中可用的notify()。
我假设你已经有这些基本概念:线程、竞态条件,以及如何解决静态条件(例如使用lock)。否则的话,你建议你去看我上一篇文章。
引用维基百科:
生产者的工作是产生一块数据,放到buffer中,如此循环。与此同时,消费者在消耗这些数据(例如从buffer中把它们移除),每次一块。
这里的关键词是“同时”。所以生产者和消费者是并发运行的,我们需要对生产者和消费者做线程分离。
from threading import Thread
class ProducerThread(Thread):
def run(self):
class ConsumerThread(Thread):
def run(self):
from threading import Thread&class ProducerThread(Thread):&&&&def run(self):&&&&&&&&pass&class ConsumerThread(Thread):&&&&def run(self):&&&&&&&&pass
再次引用维基百科:
这个为描述了两个共享固定大小缓冲队列的进程,即生产者和消费者。
假设我们有一个全局变量,可以被生产者和消费者线程修改。生产者产生数据并把它加入到队列。消费者消耗这些数据(例如把它移出)。
queue = []
queue = []
在刚开始,我们不会设置固定大小的条件,而在实际运行时加入(指下述例子)。
一开始带bug的程序:
from threading import Thread, Lock
import time
import random
queue = []
lock = Lock()
class ProducerThread(Thread):
def run(self):
nums = range(5) #Will create the list [0, 1, 2, 3, 4]
global queue
while True:
num = random.choice(nums) #Selects a random number from list [0, 1, 2, 3, 4]
lock.acquire()
queue.append(num)
print "Produced", num
lock.release()
time.sleep(random.random())
class ConsumerThread(Thread):
def run(self):
global queue
while True:
lock.acquire()
if not queue:
print "Nothing in queue, but consumer will try to consume"
num = queue.pop(0)
print "Consumed", num
lock.release()
time.sleep(random.random())
ProducerThread().start()
ConsumerThread().start()
123456789101112131415161718192021222324252627282930313233
from threading import Thread, Lockimport timeimport random&queue = []lock = Lock()&class ProducerThread(Thread):&&&&def run(self):&&&&&&&&nums = range(5) #Will create the list [0, 1, 2, 3, 4]&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&num = random.choice(nums) #Selects a random number from list [0, 1, 2, 3, 4]&&&&&&&&&&&&lock.acquire()&&&&&&&&&&&&queue.append(num)&&&&&&&&&&&&print "Produced", num &&&&&&&&&&&&lock.release()&&&&&&&&&&&&time.sleep(random.random())&class ConsumerThread(Thread):&&&&def run(self):&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&lock.acquire()&&&&&&&&&&&&if not queue:&&&&&&&&&&&&&&&&print "Nothing in queue, but consumer will try to consume"&&&&&&&&&&&&num = queue.pop(0)&&&&&&&&&&&&print "Consumed", num &&&&&&&&&&&&lock.release()&&&&&&&&&&&&time.sleep(random.random())&ProducerThread().start()ConsumerThread().start()
运行几次并留意一下结果。如果程序在IndexError异常后并没有自动结束,用Ctrl+Z结束运行。
样例输出:
Produced 3
Consumed 3
Produced 4
Consumed 4
Produced 1
Consumed 1
Nothing in queue, but consumer will try to consume
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner
self.run()
File "producer_consumer.py", line 31, in run
num = queue.pop(0)
IndexError: pop from empty list
1234567891011121314
Produced 3Consumed 3Produced 4Consumed 4Produced 1Consumed 1Nothing in queue, but consumer will try to consumeException in thread Thread-2:Traceback (most recent call last):&&File "/usr/lib/python2.7/threading.py", line 551, in __bootstrap_inner&&&&self.run()&&File "producer_consumer.py", line 31, in run&&&&num = queue.pop(0)IndexError: pop from empty list
我们开始了一个生产者线程(下称生产者)和一个消费者线程(下称消费者)。
生产者不停地添加(数据)到队列,而消费者不停地消耗。
由于队列是一个共享变量,我们把它放到lock程序块内,以防发生竞态条件。
在某一时间点,消费者把所有东西消耗完毕而生产者还在挂起(sleep)。消费者尝试继续进行消耗,但此时队列为空,出现IndexError异常。
在每次运行过程中,在发生IndexError异常之前,你会看到print语句输出”Nothing in queue, but consumer will try to consume”,这是你出错的原因。
我们把这个实现作为错误行为(wrong behavior)。
什么是正确行为?
当队列中没有任何数据的时候,消费者应该停止运行并等待(wait),而不是继续尝试进行消耗。而当生产者在队列中加入数据之后,应该有一个渠道去告诉(notify)消费者。然后消费者可以再次从队列中进行消耗,而IndexError不再出现。
条件(condition)可以让一个或多个线程进入wait,直到被其他线程notify。参考:?
这就是我们所需要的。我们希望消费者在队列为空的时候wait,只有在被生产者notify后恢复。生产者只有在往队列中加入数据后进行notify。因此在生产者notify后,可以确保队列非空,因此消费者消费时不会出现异常。
condition内含lock。
condition有acquire()和release()方法,用以调用内部的lock的对应方法。
condition的acquire()和release()方法内部调用了lock的acquire()和release()。所以我们可以用condiction实例取代lock实例,但lock的行为不会改变。
生产者和消费者需要使用同一个condition实例, 保证wait和notify正常工作。
重写消费者代码:
from threading import Condition
condition = Condition()
class ConsumerThread(Thread):
def run(self):
global queue
while True:
condition.acquire()
if not queue:
print "Nothing in queue, consumer is waiting"
condition.wait()
print "Producer added something to queue and notified the consumer"
num = queue.pop(0)
print "Consumed", num
condition.release()
time.sleep(random.random())
1234567891011121314151617
from threading import Condition&condition = Condition()&class ConsumerThread(Thread):&&&&def run(self):&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&condition.acquire()&&&&&&&&&&&&if not queue:&&&&&&&&&&&&&&&&print "Nothing in queue, consumer is waiting"&&&&&&&&&&&&&&&&condition.wait()&&&&&&&&&&&&&&&&print "Producer added something to queue and notified the consumer"&&&&&&&&&&&&num = queue.pop(0)&&&&&&&&&&&&print "Consumed", num &&&&&&&&&&&&condition.release()&&&&&&&&&&&&time.sleep(random.random())
重写生产者代码:
class ProducerThread(Thread):
def run(self):
nums = range(5)
global queue
while True:
condition.acquire()
num = random.choice(nums)
queue.append(num)
print "Produced", num
condition.notify()
condition.release()
time.sleep(random.random())
123456789101112
class ProducerThread(Thread):&&&&def run(self):&&&&&&&&nums = range(5)&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&condition.acquire()&&&&&&&&&&&&num = random.choice(nums)&&&&&&&&&&&&queue.append(num)&&&&&&&&&&&&print "Produced", num &&&&&&&&&&&&condition.notify()&&&&&&&&&&&&condition.release()&&&&&&&&&&&&time.sleep(random.random())
样例输出:
Produced 3
Consumed 3
Produced 1
Consumed 1
Produced 4
Consumed 4
Produced 3
Consumed 3
Nothing in queue, consumer is waiting
Produced 2
Producer added something to queue and notified the consumer
Consumed 2
Nothing in queue, consumer is waiting
Produced 2
Producer added something to queue and notified the consumer
Consumed 2
Nothing in queue, consumer is waiting
Produced 3
Producer added something to queue and notified the consumer
Consumed 3
Produced 4
Consumed 4
Produced 1
Consumed 1
123456789101112131415161718192021222324
Produced 3Consumed 3Produced 1Consumed 1Produced 4Consumed 4Produced 3Consumed 3Nothing in queue, consumer is waitingProduced 2Producer added something to queue and notified the consumerConsumed 2Nothing in queue, consumer is waitingProduced 2Producer added something to queue and notified the consumerConsumed 2Nothing in queue, consumer is waitingProduced 3Producer added something to queue and notified the consumerConsumed 3Produced 4Consumed 4Produced 1Consumed 1
对于消费者,在消费前检查队列是否为空。
如果为空,调用condition实例的wait()方法。
消费者进入wait(),同时释放所持有的lock。
除非被notify,否则它不会运行。
生产者可以acquire这个lock,因为它已经被消费者release。
当调用了condition的notify()方法后,消费者被唤醒,但唤醒不意味着它可以开始运行。
notify()并不释放lock,调用notify()后,lock依然被生产者所持有。
生产者通过condition.release()显式释放lock。
消费者再次开始运行,现在它可以得到队列中的数据而不会出现IndexError异常。
为队列增加大小限制
生产者不能向一个满队列继续加入数据。
它可以用以下方式来实现:
在加入数据前,生产者检查队列是否为满。
如果不为满,生产者可以继续正常流程。
如果为满,生产者必须等待,调用condition实例的wait()。
消费者可以运行。消费者消耗队列,并产生一个空余位置。
然后消费者notify生产者。
当消费者释放lock,消费者可以acquire这个lock然后往队列中加入数据。
最终程序如下:
from threading import Thread, Condition
import time
import random
queue = []
MAX_NUM = 10
condition = Condition()
class ProducerThread(Thread):
def run(self):
nums = range(5)
global queue
while True:
condition.acquire()
if len(queue) == MAX_NUM:
print "Queue full, producer is waiting"
condition.wait()
print "Space in queue, Consumer notified the producer"
num = random.choice(nums)
queue.append(num)
print "Produced", num
condition.notify()
condition.release()
time.sleep(random.random())
class ConsumerThread(Thread):
def run(self):
global queue
while True:
condition.acquire()
if not queue:
print "Nothing in queue, consumer is waiting"
condition.wait()
print "Producer added something to queue and notified the consumer"
num = queue.pop(0)
print "Consumed", num
condition.notify()
condition.release()
time.sleep(random.random())
ProducerThread().start()
ConsumerThread().start()
123456789101112131415161718192021222324252627282930313233343536373839404142
from threading import Thread, Conditionimport timeimport random&queue = []MAX_NUM = 10condition = Condition()&class ProducerThread(Thread):&&&&def run(self):&&&&&&&&nums = range(5)&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&condition.acquire()&&&&&&&&&&&&if len(queue) == MAX_NUM:&&&&&&&&&&&&&&&&print "Queue full, producer is waiting"&&&&&&&&&&&&&&&&condition.wait()&&&&&&&&&&&&&&&&print "Space in queue, Consumer notified the producer"&&&&&&&&&&&&num = random.choice(nums)&&&&&&&&&&&&queue.append(num)&&&&&&&&&&&&print "Produced", num&&&&&&&&&&&&condition.notify()&&&&&&&&&&&&condition.release()&&&&&&&&&&&&time.sleep(random.random())&class ConsumerThread(Thread):&&&&def run(self):&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&condition.acquire()&&&&&&&&&&&&if not queue:&&&&&&&&&&&&&&&&print "Nothing in queue, consumer is waiting"&&&&&&&&&&&&&&&&condition.wait()&&&&&&&&&&&&&&&&print "Producer added something to queue and notified the consumer"&&&&&&&&&&&&num = queue.pop(0)&&&&&&&&&&&&print "Consumed", num&&&&&&&&&&&&condition.notify()&&&&&&&&&&&&condition.release()&&&&&&&&&&&&time.sleep(random.random())&ProducerThread().start()ConsumerThread().start()
样例输出:
Produced 0
Consumed 0
Produced 0
Produced 4
Consumed 0
Consumed 4
Nothing in queue, consumer is waiting
Produced 4
Producer added something to queue and notified the consumer
Consumed 4
Produced 3
Produced 2
Consumed 3
12345678910111213
Produced 0Consumed 0Produced 0Produced 4Consumed 0Consumed 4Nothing in queue, consumer is waitingProduced 4Producer added something to queue and notified the consumerConsumed 4Produced 3Produced 2Consumed 3
很多网友建议我在lock和condition下使用Queue来代替使用list。我同意这种做法,但我的目的是展示Condition,wait()和notify()如何工作,所以使用了list。
以下用Queue来更新一下代码。
Queue封装了Condition的行为,如wait(),notify(),acquire()。
现在不失为一个好机会读一下Queue的文档()。
更新程序:
from threading import Thread
import time
import random
from Queue import Queue
queue = Queue(10)
class ProducerThread(Thread):
def run(self):
nums = range(5)
global queue
while True:
num = random.choice(nums)
queue.put(num)
print "Produced", num
time.sleep(random.random())
class ConsumerThread(Thread):
def run(self):
global queue
while True:
num = queue.get()
queue.task_done()
print "Consumed", num
time.sleep(random.random())
ProducerThread().start()
ConsumerThread().start()
12345678910111213141516171819202122232425262728
from threading import Threadimport timeimport randomfrom Queue import Queue&queue = Queue(10)&class ProducerThread(Thread):&&&&def run(self):&&&&&&&&nums = range(5)&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&num = random.choice(nums)&&&&&&&&&&&&queue.put(num)&&&&&&&&&&&&print "Produced", num&&&&&&&&&&&&time.sleep(random.random())&class ConsumerThread(Thread):&&&&def run(self):&&&&&&&&global queue&&&&&&&&while True:&&&&&&&&&&&&num = queue.get()&&&&&&&&&&&&queue.task_done()&&&&&&&&&&&&print "Consumed", num&&&&&&&&&&&&time.sleep(random.random())&ProducerThread().start()ConsumerThread().start()
在原来使用list的位置,改为使用Queue实例(下称队列)。
这个队列有一个condition,它有自己的lock。如果你使用Queue,你不需要为condition和lock而烦恼。
生产者调用队列的put方法来插入数据。
put()在插入数据前有一个获取lock的逻辑。
同时,put()也会检查队列是否已满。如果已满,它会在内部调用wait(),生产者开始等待。
消费者使用get方法。
get()从队列中移出数据前会获取lock。
get()会检查队列是否为空,如果为空,消费者进入等待状态。
get()和put()都有适当的notify()。现在就去看Queue的源码吧。
关于作者:
可能感兴趣的话题
写的通俗易懂,又很有层次感。今天在网上看了很多写python生产者消费者问题的人,连加锁解锁的逻辑都没有就开始啪啪啪一堆烂代码狂轰滥炸,还标题为“^复杂多并发问题”,我也是醉了……
吐槽完毕,感谢博主的翻译以及原作者的耐心翻译
关于伯乐在线博客
在这个信息爆炸的时代,人们已然被大量、快速并且简短的信息所包围。然而,我们相信:过多“快餐”式的阅读只会令人“虚胖”,缺乏实质的内涵。伯乐在线内容团队正试图以我们微薄的力量,把优秀的原创文章和译文分享给读者,为“快餐”添加一些“营养”元素。
新浪微博:
推荐微信号
(加好友请注明来意)
– 好的话题、有启发的回复、值得信赖的圈子
– 分享和发现有价值的内容与观点
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 翻译传播优秀的外文文章
– 国内外的精选文章
– UI,网页,交互和用户体验
– 专注iOS技术分享
– 专注Android技术分享
– JavaScript, HTML5, CSS
– 专注Java技术分享
– 专注Python技术分享
& 2017 伯乐在线python的中文问题一直是困扰新手的头疼问题,Python的发行版至今尚未包括任何中文支持模块。当然,几乎可以确定的是,在将来的版本中,python会彻底解决此问题,不用我们这么麻烦了。 笔者使用的是2.5版本。Python的版本可以通过调用sys模块的sys.version查看。在几个月的学习中,主要遇到以下问题:
1. print打印中文的问题:
在编辑器中输入一段测试代码:
运行结果如下:
Non-ASCII character '\xb2' in file c:\Documents and Settings\Administrator\桌面\2.py on line 1, but
see http://www.python.org/peps/pep-0263.html for details: 2.py, line 1, pos 0
原因是如果文件里有非ASCII字符,需要指定编码声明。把2.py文件的编码重新改为utf-8,并加上编码声明:
# -*- coding: utf-8 -*-
运行后可以正确打印中文。
2.中文路径的问题。
在D盘下保存一个名字为&中文.txt&的文件。运行如下测试代码:
# -*- coding: utf-8 -*-
f=open('D:\\中文.txt', 'r')
print f.read()
运行结果如下:
IOError: [Errno 2] No such file or directory: 'D:\\\xe4\xb8\xad\xe6\x96\x87.txt'
字符串有很多的编码,不同的系统和平台有各自的编码 ,为了实现系统或平台之间的信息交互可能需要编码转换。这里只需要先使用UNICODE编码一下,这样再读取中文路径就不会有问题了:
# -*- coding: utf-8 -*-
path='D:\\中文.txt'
spath=unicode(path , "utf8")
f=open(spath,'r')
print f.read()
然后就可以正确显示文件内容
所有的中文显示问题都可以归结为编码问题,遇到其他类似的问题,那只能仔细看文档,靠你的经验,靠你多做测试。而且根据python所报出来的错误一般也可以判断出来。那么当发现需要编码转换时,剩下的就是如何正确进行码制转换。
为了正确处理多语言文本,Python在2.0版后引入了Unicode字符串。从那时起,Python语言中的字符串就分为两种:一种是2.0版之前就已经使用很久的传统Python字符串,一种则是新的Unicode字符串。在Python语言中,一般的解决办法是使用unicode()内建函数对一个传统Python字符串进行&解码&,得到一个Unicode字符串,然后又通过Unicode字符串的encode()方法对这个Unicode字符串进行&编码&,将其&编码&成为传统Python字符串。
阅读(...) 评论()Author:hit9
注1:以下问题来自Stackoverflow, 但不完全一致
注2:欢迎fork向本文添加内容, 文章在Github上,地址见首页。
&&& obj = object()
&&& obj.name = &whatever&
Traceback (most recent call last):
File &&stdin&&, line 1, in &module&
AttributeError: 'object' object has no attribute 'name'
但是为什么这样就可以呢:
&&& class Object(object):pass
&&& Obj = Object()
&&& Obj.name = &whatever&
&&& Obj.name
'whatever'
答: 现在你给第二个代码块中的Object加上属性 __slots__ 试试:
&&& class Object(object):
__slots__ = {}
&&& Obj = Object()
&&& Obj.name = &whatever&
Traceback (most recent call last):
File &&stdin&&, line 1, in &module&
AttributeError: 'Object' object has no attribute 'name'
会发现抛出了同样的异常。 object 、 list
、 dict 等内置函数都如此。
拥有 __slots__ 属性的类在实例化对象时不会自动分配 __dict__ ,而 obj.attr 即 obj.__dict__['attr'],
所以会引起 AttributeError
对于拥有 __slots__ 属性的类的实例 Obj 来说,只能对 Obj 设置 __slots__ 中有的属性:
&&& class Object(object):
__slots__ = {&a&,&b&}
&&& Obj = Object()
&&& Obj.a = 1
&&& Obj.c = 1
Traceback (most recent call last):
File &&stdin&&, line 1, in &module&
AttributeError: 'Object' object has no attribute 'c'
for property, value in vars(theObject).iteritems():
print property, &: &, value
这个做法其实就是 theObject.__dict__ ,
也就是 vars(obj) 其实就是返回了 o.__dict__
另一个做法: inspect.getmembers(object[, predicate])
&&& import inspect
&&& for attr, value in inspect.getmembers(obj):
print attr, value
两者不同的是, inspect.getmembers
返回的是元组 (attrname, value) 的列表。而且是所有的属性,
包括 __class__ , __doc__ ,
__dict__ ,
__init__ 等特殊命名的属性和方法。而 vars() 只返回 __dict__. 对于一个空的对象来说, __dict__ 会是 {}
而 inspect.getmembers 返回的不是空的。
&&& class O(object):pass
&&& O.__dict__[&a&] = 1
Traceback (most recent call last):
File &&stdin&&, line 1, in &module&
TypeError: 'dictproxy' object does not support item assignment
答: 是的, class的 __dict__ 是只读的:
&&& class O(object):pass
&&& O.__dict__
&dictproxy object at 0xb76d8ac4&
&&& O.__dict__.items()
[('__dict__', &attribute '__dict__' of 'O' objects&), ('__module__', '__main__'), ('__weakref__', &attribute '__weakref__' of 'O' objects&), ('__doc__', None)]
&&& O.func = lambda: 0
&&& O.__dict__.items()
[('__dict__', &attribute '__dict__' of 'O' objects&), ('__module__', '__main__'), ('__weakref__', &attribute '__weakref__' of 'O' objects&), ('__doc__', None), ('func', &function &lambda& at 0xb76de224&)]
&&& O.func
&unbound method O.&lambda&&
可以看到 O.__dict__ 是一个 dictproxy 对象,而不是一个 dict . (你可以 dir(O.__dict__) ,但不会发现有它有属性 __setitem__ )
那我们怎么给类设置属性呢? 用 setattr
&&& setattr(O, &a&, 1)
&&& class C(object):
def foo(self):
&unbound method C.foo&
&&& C().foo
&bound method C.foo of &__main__.C object at 0xb76ddcac&&
为什么 C.foo 是一个 unbound method , C().foo 是一个 bound method ? Python 为什么这样设计?
答:这是问题
来自Armin Ronacher(Flask 作者)的回答:
如果你明白python中描述器(descriptor)是怎么实现的, 方法(method) 是很容易理解的。
上例代码中可以看到,如果你用类 C 去访问 foo 方法,会得到 unbound 方法,然而在class的内部存储中它是个 function, 为什么?
原因就是 C 的类 (注意是类的类) 实现了一个 __getattribute__ 来解析描述器。听起来复杂,但并非如此。上例子中的 C.foo 等价于:
&&& C.__dict__['foo'].__get__(None, C)
&unbound method C.foo&
这是因为方法 foo 有个 __get__ 方法,也就是说, 方法是个描述器。如果你用实例来访问的话也是一模一样的:
&&& c = C()
&&& C.__dict__['foo'].__get__(c, C)
&bound method C.foo of &__main__.C object at 0xb76ddd8c&&
只是那个 None 换成了这个实例。
现在我们来讨论,为什么Python要这么设计?
其实,所谓 bound method ,就是方法对象的第一个函数参数绑定为了这个类的实例(所谓 bind )。这也是那个 self 的由来。
当你不想让类把一个函数作为一个方法,可以使用装饰器 staticmethod
&&& class C(object):
@staticmethod
def foo():
&function foo at 0xb76d056c&
&&& C.__dict__['foo'].__get__(None, C)
&function foo at 0xb76d056c&
staticmethod 装饰器会让 foo 的 __get__ 返回一个函数,而不是一个方法。
一个函数(function)是由 def 语句或者 lambda 创建的。
当一个函数(function)定义在了class语句的块中(或者由 type 来创建的), 它会转成一个 unbound method , 当我们通过一个类的实例来
访问这个函数的时候,它就转成了 bound method , bound method 会自动把这个实例作为函数的地一个参数。
所以, bound method 就是绑定了一个实例的方法, 否则叫做 unbound method .它们都是方法(method), 是出现在 class 中的函数。
这是stackoverflow投票很高的问题:
回答: (最高得分的答案)
Metaclass是创建class的东西。
一个class是用来创建对象的是不是?
但是我们知道,Python中的类也是对象。
Metaclass就是用来创建类这些对象的,它们是类的类,你可以形象化地理解为:
MyClass = MetaClass()
MyObject = MyClass()
你知道, type 函数可以这样使用:
MyClass = type('MyClass', (), {})
这是因为 type 实际上是个 metaclass , Python使用 type 这个元类来创建所有的类。
现在你是不是有疑问了,为什么 type 是小写开头的,而不是 Type 呢?既然它是个元类!
我猜,大概是因为和 str , int 来保持一致吧, str 也是一个类,用来创建字符串。
你可以检查下对象的 __class__ 属性来看看它们的类是谁. Python中万物都是对象:
&&& age = 35
&&& age.__class__
&type 'int'&
&&& name = 'bob'
&&& name.__class__
&type 'str'&
&&& def foo():pass
&&& foo.__class__
&type 'function'&
&&& class Bar(object): pass
&&& b = Bar()
&&& b.__class__
&class '__main__.Bar'&
那么, __class__ 的 __class__ 属性又是谁?
&&& a.__class__.__class__
&type 'type'&
&&& name = 'bob'
&&& name.__class__.__class__
&type 'type'&
所以,元类是用来创建类的。
你可以叫元类为 类工厂
type 是Python使用的内建元类,当然,Python允许大家建立自己的元类.
你可以在写一个类的时候加上这个属性 __metaclass__
class Foo(object):
__metaclass__ = something...
这样的话,Python就会用这个元类(上例中为 something ) 来创建类 Foo
我们首先写的是 class Foo(object) ,但是Python跑到这里看到这一行时,并没有在内存中建立类 Foo
因为Python这么做的:查找它有没有 ``__metaclass__`` 属性,有的话,用指定的类来创建 ``Foo`` ,否则(也就是一般情形下),使用 ``type`` 来创建
最好还是记住上面那句话 :)
当你这么写的时候:
class Foo(Bar):
Python会这么做:
有 __metaclass__ 定义吗? 如果有,在内存中建立一个类的对象。用 __metaclass__ 指定的类来创建。
如果没有找到这个属性,它会继续在父类 Bar 中找
这样一直向父类找,父类的父类。。。直到 module 级别的才停止。
如果在任何的父类中都找不到,那就用 type 创建 Foo
现在一个问题,我们可以给 __metaclass__ 赋值什么呢?
答案当然是,一个可以创建类的东西。
那么,什么才能创建一个类呢?
设计元类的主要目的就是允许我们在类创建的时候动态的修改它,这经常用在API的设计上。
让我们举一个很纯的例子,比如你想要让一个模块中的所有类都共享一些属性,有很多办法可以做到,其中一个就是
在模块中定义一个 __metaclass__ 属性。
这样,模块中所有的类都会被 __metaclass__ 创建。
幸运的是 , __metaclass__ 可以是任何可以被调用的对象。不非要是个class,还可以是个函数。
所以,我们这么做,用一个函数来作为metaclass:
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
Return a class object, with the list of its attribute turned
into uppercase.
# pick up any attribute that doesn't start with '__'
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
# turn them into uppercase
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with &object& though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with &object& children
bar = 'bip'
print hasattr(Foo, 'bar')
# Out: False
print hasattr(Foo, 'BAR')
# Out: True
print f.BAR
# Out: 'bip'
现在我们用一个类来作为一个metaclass:
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return type(future_class_name, future_class_parents, uppercase_attr)
但这样并不是很 OOP , 我们可以直接调用 type 函数,并且不覆盖父亲的 __new__ 方法:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
# reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)
你可能注意到了参数 upperattr_metaclass ,没什么特殊的,一个方法总是拿那个实例来作为第一个参数。就像寻常的 self 参数。
当然,可以这么写,我上面的例子命名不那么好:)
class UpperAttrMetaclass(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return type.__new__(cls, name, bases, uppercase_attr)
我们可以使用 super
函数来让这个例子变得更简洁:
class UpperAttrMetaclass(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)
元类是个简单的魔术,只要:
注入类的创建
返回修改后的类
既然 __metaclass__ 可以是任何可以被调用的对象,那么你为什么用类作为metaclass而不是函数呢?
更能清楚的表达意图
可以使用OOP, metaclass可以继承,重写父类,甚至使用metaclass,可以使用面向对象的特性。
更好的组织代码.
一个典型例子,Django ORM (译者注,peewee也用metaclass):
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
但是你这么做:
guy = Person(name='bob', age='35')
print guy.age
并不返回一个 IntegerField 对象,而是一个 int
Python的世界里,万物都是对象
但是 type 是它自己的元类。
99%的情形下你不需要用这个东西。
众所周知, zip 函数可以把多个序列打包到元组中:
&&& a, b = [1, 2, 3], [4, 5, 6]
&&& c = zip(a, b)
[(1, 4), (2, 5), (3, 6)]
那么为什么没有这样的 unzip 函数来把 [(1, 4), (2, 5), (3, 6)] 还原呢?
答: Python中有个很神奇的操作符 * 来 unpack 参数列表:
&&& zip(*c)
[(1, 2, 3), (4, 5, 6)]
无论你怎么叫吧, 英文来说是 new style class 和 old style class
新式类是继承自 object 或其他新式类的类:
class NewStyleClass(object):
class AnotherNewStyleClass(NewStyleClass):
否则是老式类:
class OldStyleClass():
为什么引入新式类?
& The major motivation for introducing new-style classes is to provide a unified object model with a full meta-model.
Python为了提供一个更完整的元模型。(好吧,译者也不大明白,不过我知道Python中很神奇的描述器只能在新式类里用)
本文Github地址:
欢迎Fork追加问题。

我要回帖

更多关于 python的编码问题 的文章

 

随机推荐