英雄联盟感谢有礼450元类, 1350 元类,3150 元类,4800和6300元类哪类英雄好?

【图文】第10章 分布式数据库系统的发展趋势_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
第10章 分布式数据库系统的发展趋势
大小:235.50KB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢【图文】Java_与MVC_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Java_与MVC
大小:272.50KB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢Rational Rose2002从简单使用到全面理解讲义(二)_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
Rational Rose2002从简单使用到全面理解讲义(二)
&&Rational Rose2002从简单使用到全面理解讲义(二)
阅读已结束,下载文档到电脑
想免费下载本文?
定制HR最喜欢的简历
下载文档到电脑,方便使用
还剩8页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢Python 中的元类编程,第 3 部分
简介去年,我参加了 EuroPython 2006 会议。这个会议非常好,组织得很完美,谈话都具有很高的水平,人们也都特别友好。然而,我在这篇文章归属的 Python 社区中注意到了一种令人烦恼的趋势。几乎同时,我的合著者 David Mertz 也在思考一个类似的关于一些提交给 Gnosis Utilities 的补丁程序的问题。这种有争议的趋势就是趋向于耍小聪明。不幸的是,Python 社区的这种聪明以前只局限于 Zope 和 Twisted,现在已变得无处不在。我们在试验项目和学习过程中并不反对这种聪明。我们的烦恼是,在产品框架上必须符合用户的要求。在本文中,我们希望为避免这种聪明做出小小的贡献,至少在我们比较精通的领域避免元类滥用。
对于本文,我们坚持严肃的立场:我们把在不用元类也能解决问题的情况下使用元类都视为元类滥用。当然,作者的过错也很明显:我们的
助长了这种做法的流行。Nostra culpa。 使用元编程最普通的情况就是创建具有动态生成的属性和方法的类。跟流行的观点相反,这是一个在大多数时候都不需要 而且不想要 自定义元类的工作。 本文适用于两类读者:普通程序员和聪明的程序员。前者知道一些元编程技巧,但是并没有在大脑中形成具体的概念;后者很聪明,而且理解得深一些。后者的问题在于变得聪明很容易,要变得不那么聪明就得花不少时间了。例如,花几个月时间就能理解如何使用元类,但是要花几年时间才能明白如何不 使用它们。 关于类初始化 创建类的过程中,类的所有属性和方法只设置一次。而在 Python 中,方法和属性随时都可以更改,但是只有不遵守规则的程序员才会这样做。 在各种条件下,创建类时,也许想用比简单地运行静态编码更加动态的方法。例如,可能想根据从配置文件读取的参数来设置一些默认的类属性;或者想根据数据库表中的字段来设置类特性。利用强制方式动态自定义类行为最简单的方法是:首先创建类,然后添加方法和属性。例如,Anand Pillai(我们熟悉的一个优秀程序员)提出了一个到 Gnosis Utilities 的分包 gnosis.xml.objectify 的路径,该分包就是这么做的。一个专门用来保存 “xml 节点对象” 的叫做 gnosis.xml.objectify._XO_ 的基类就被许多增强的行为 “装饰” 成如下这样:清单 1. 基类的动态增强setattr(_XO_, 'orig_tagname', orig_tagname)
setattr(_XO_, 'findelem', findelem)
setattr(_XO_, 'XPath', XPath)
setattr(_XO_, 'change_pcdata', change_pcdata)
setattr(_XO_,'addChild',addChild) 您可能会非常合理地想到,也可以定义 XO 基类的子类来实现同样的增强。在感觉上这是对的,但 Anand 已经提供了 20 多种可能的增强,并且一些特定的用户可能想要其中的一些增强,但不想要 另外一些增强。有太多的替代方法可以轻易地为每种增强情形创建子类。尽管如此,上面的代码未必是恰到好处的。应该用一个附加到 XO、行为是动态决定的自定义元类来完成上述工作。但是这又让我们回到了希望避免的聪明过度(和不透明性)上。 上述问题的一种干净、漂亮的解决方案可能需要向 Python 添加类装饰器。如果拥有这些装饰器,编写的代码可能就像这样:清单 2. 向 Python 添加类装饰器features = [('XPath',XPath), ('addChild',addChild), ('is_root',is_root)]
@enhance(features)
class _XO_plus(gnosis.xml.objectify._XO_): pass
gnosis.xml.objectify._XO_ = _XO_plus 然而,目前没有这种语法。当元类变复杂时 表面上看本文除了大惊小怪之外,似乎毫无意义。例如,为什么不直接把 XO 元类定义为
Enhance,然后就一切 OK 了呢。
Enhance.__init__() 可以为所讨论的特定用途添加所需的任何功能。可能看起来像这样:清单 3. 将 XO 定义为 Enhance class _XO_plus(gnosis.xml.objectify._XO_):
__metaclass__ = Enhance
features = [('XPath',XPath), ('addChild',addChild)]
gnosis.xml.objectify._XO_ = _XO_plus 不幸的是,当考虑到继承时,问题却没有这么简单。一旦为基类定义了一个自定义元类,所有派生类都将继承此元类,所以初始化代码将魔法般隐式地在所有派生类上运行。这在特定的情形中可能还不错(例如,假设必须将所有类都注册到您自己的框架中:使用元类可以确保不会忘记注册派生类),然而,在许多情况下则可能不喜欢这种行为,因为: 您相信显式比隐式更好。 派生类具有跟基类相同的动态类属性。为每个派生类再次设置这些属性是一种浪费,因为通过继承它们就会拥有这些属性。如果初始化代码很慢或者需要大量的计算,那么这一特性就显得特别重要。也许会在元类代码中添加一个检查,以查看是否在父类中设置了这些属性,但是这样会增加负担,并且不会控制所有的类。 自定义元类将会使类有些不可思议和不标准:您肯定不想增加元类冲突、“ __slots__ ” 问题、跟扩展类( Zope )斗争和其他复杂问题的几率。元类比很多人认识到的更加脆弱。我们甚至在试验代码中用了四年之后还很少在生产代码中使用它们。 您觉得对于类初始化这类简单的工作使用自定义元类是杀鸡用牛刀,所以想要使用一种更为简单的解决方案。 换句话说,只有当想在派生类上运行代码,又不想让用户注意到时,才应该使用自定义元类。如果不属于这种情形,那就跳过元类,使您(和您的用户)的生活更加惬意。classinitializer 装饰器 本文以下部分可能会被谴责为聪明过度。但是聪明不应该加重用户的负担,只应该加重我们作者的负担。读者可以做一些与我们假设的理想类装饰器类似的事情,但是要避免在元类方法中出现的继承及元类冲突问题。我们后面给出的 “不可思议的” 装饰器通常情况下只能增强直观的(但稍微有些难看的)强制方法,并且跟下面的例子在 “精神上相当”:清单 4. 强制方法def Enhance(cls, **kw):
for k, v in kw.iteritems():
setattr(cls, k, v)
class ClassToBeInitialized(object):
Enhance(ClassToBeInitialized, a=1, b=2) 上面的强制增强器并不是很坏。但是也有一些缺馅:它要求重复输入类名称;可读性不够理想,因为类定义和类初始化是分开的 —— 长的类定义可能会漏掉最后一行;并且它会认为首先定义一些内容然后又立即更改是不对的。classinitializer 装饰器提供了一个说明性解决方案。装饰器将 Enhance(cls,**kw) 转换为一个能够用于类定义中的方法:清单 5. 基本操作中神奇的装饰器&&& @classinitializer # add magic to Enhance
... def Enhance(cls, **kw):
for k, v in kw.iteritems():
setattr(cls, k, v)
&&& class ClassToBeInitialized(object):
Enhance(a=1, b=2)
&&& ClassToBeInitialized.a
&&& ClassToBeInitialized.b
2如果使用过 zope 界面,也许见过类初始化器的例子 (zope.interface.implements)。事实上,classinitializer 是使用一个从 Phillip J. Eby 开创的 zope.interface.advice 复制过来的技巧来实现的。此技巧使用 “ __metaclass__ ” 钩子,但是它不使用 自定义类。ClassToBeInitialized 保留了它原始的元类,即新式类的普通内置元类 type: &&& type(ClassToBeInitialized)
&type 'type'& 原则上,此技巧也适用于老式类,并且应该容易编写一个实现来使老式类保持老的样式。然而,由于根据 Guido 所说的 “老式类在精神上是不受赞成的”,当前的实现将老式类转换为新式类: 清单 6. 升级为新式类&&& class WasOldStyle:
Enhance(a=1, b=2)
&&& WasOldStyle.a, WasOldStyle.b
&&& type(WasOldStyle)
&type 'type'&classinitializer 装饰器的一个动机是要隐藏细节,使一般的人们能够用一种容易的方法实现他们自己的类初始化器,而不必知道类创建工作的细节和_metaclass_ 钩子的秘密。另一个动机是,即使对于 Python 奇才来说,每次编写新的类初始化器时都得重写管理 _metaclass_ 钩子的代码也是很不方便的。
最后应该注意,我们指出
Enhance 的已装饰版本当作类范围外的未装饰版本来运行已经足够漂亮了,假设传递给它一个显式类参数:&&& Enhance(WasOldStyle, a=2)
&&& WasOldStyle.a
2极度不可思议 下面是 classinitializer 的代码。使用装饰器不需要理解该代码: 清单 7. classinitializer 装饰器import sys
def classinitializer(proc):
# basic idea stolen from zope.interface.advice, P.J. Eby
def newproc(*args, **kw):
frame = sys._getframe(1)
if '__module__' in frame.f_locals and not \
'__module__' in frame.f_code.co_varnames: # we are in a class
if '__metaclass__' in frame.f_locals:
raise SyntaxError("Don't use two class initializers or\n"
"a class initializer together with a __metaclass__ hook")
def makecls(name, bases, dic):
cls = type(name, bases, dic)
except TypeError, e:
if "can't have only classic bases" in str(e):
cls = type(name, bases + (object,), dic)
# other strange errs, e.g. __slots__ conflicts
proc(cls, *args, **kw)
return cls
frame.f_locals["__metaclass__"] = makecls
proc(*args, **kw)
newproc.__name__ = proc.__name__
newproc.__module__ = proc.__module__
newproc.__doc__ = proc.__doc__
newproc.__dict__ = proc.__dict__
return newproc 从实现上看,类初始化器是如何工作的就变得很清晰了:当在类中调用一个类初始化器时,实际上定义了一个 _metaclass_ 钩子,它将会被这个类的元类(一般是 type) 调用。元类将创建此类(作为一个新式类)并将其传递给类初始化器过程。 技巧和警告 当类初始化器(重新)定义 _metaclass_ 钩子时,它们不能很好地与显式(与隐式继承的相反)定义 _metaclass_ 钩子的类协作。如果 _metaclass_ 钩子在类初始化器之后 定义,它会静静地 覆盖类初始化器。 清单 8.表项目 index.html主页&&& class C:
Enhance(a=1)
def __metaclass__(name, bases, dic):
cls = type(name, bases, dic)
print 'Enhance is silently ignored'
return cls
Enhance is silently ignored
Traceback (most recent call last):
AttributeError: type object 'C' has no attribute 'a' 然而不幸的是,这个问题没有通用的解决方案;我们只是简单地记录。另一方面,如果在 _metaclass_ 钩子之后 调用类初始化器,将会得到异常: 清单 9. 本地元类出现错误&&& class C:
def __metaclass__(name, bases, dic):
cls = type(name, bases, dic)
print 'calling explicit __metaclass__'
return cls
Enhance(a=1)
Traceback (most recent call last):
SyntaxError: Don't use two class initializers or
a class initializer together with a __metaclass__ hook 出现错误比静静地覆盖显式的 _metaclass_ 钩子要好。因此,如果试图同时使用两个类初始化器,或者两次调用同一个类初始化器,将导致错误:清单 10. 双重增强导致了一个问题&&& class C:
Enhance(a=1)
Enhance(b=2)
Traceback (most recent call last):
SyntaxError: Don't use two class initializers or
a class initializer together with a__metaclass__ hook 从好的方面看,继承的 _metaclass_ 钩子和自定义元类的所有问题都被解决了: 清单 11. 有效地增强继承的元类 &&& class B: # a base class with a custom metaclass
class __metaclass__(type):
&&& class C(B): # class with both custom metaclass AND class initializer
Enhance(a=1)
&&& type(C)
&class '_main.__metaclass__'& 类初始化器并没有干扰到 C 的元类,它继承了基类 B,并且继承的元类不但不会影响到类初始化器,而且会很好地运行。相反,如果试图在基类中直接调用 Enhance,则可能会遇到问题。总结 使用所有这些定义的方法,自定义类初始化将变得更加简单和美观。可能就像下面的清单这么简单:清单 12. 最简形式的增强class _XO_plus(gnosis.xml.objectify._XO_):
Enhance(XPath=XPath, addChild=addChild, is_root=is_root)
gnosis.xml.objectify._XO_ = _XO_plus 这个例子仍然使用了“注入”,这对普通情况来说有些多余;也就是说,我们将增强的类放回到模块名称空间中的一个特定名称中。这对特定的模块是必要的,但是大多数时候都不需要。在任何情况下,Enhance() 的参数不需要像上面那样固定在代码中,您可以公平地对完全动态的事情使用 Enhance(**feature_set)。 另一点需要注意的是,Enhance() 函数的功能远不只上面提到的简单版本。装饰器更擅长完成复杂的增强功能。例如,以下是一个将 “记录” 添加到类的 Enhance() 函数:
清单 13. 类增强的变体@classinitializer
def def_properties(cls, schema):
Add properties to cls, according to the schema, which is a list
of pairs (fieldname, typecast). A typecast is a
callable converting the field value into a Python type.
The initializer saves the attribute names in a list cls.fields
and the typecasts in a list cls.types. Instances of cls are expected
to have private attributes with names determined by the field names.
cls.fields = []
cls.types = []
for name, typecast in schema:
if hasattr(cls, name): # avoid accidental overriding
raise AttributeError('You are overriding %s!' % name)
def getter(self, name=name):
return getattr(self, '_' + name)
def setter(self, value, name=name, typecast=typecast):
setattr(self, '_' + name, typecast(value))
setattr(cls, name, property(getter, setter))
cls.fields.append(name)
cls.types.append(typecast) 不同之处在于:(a)什么被增强了;(b)这种方法是如何工作的;(c)基类的工作都保持正交:清单 14. 自定义记录类&&& class Article(object):
# fields and types are dynamically set by the initializer
def_properties([('title', str), ('author', str), ('date', date)])
def __init__(self, values): # add error checking if you like
for field, cast, value in zip(self.fields, self.types, values):
setattr(self, '_' + field, cast(value))
&&& a=Article(['How to use class initializers', 'M. Simionato', ''])
&&& a.title
'How to use class initializers'
&&& a.author
'M. Simionato'
&&& a.date
datetime.date()
相关主题 您可以参阅本文在 developerWorks 全球站点上的 。
“” 的第 1 部分(developerWorks,2003 年 2 月)介绍了元类编程概念和面向对象概念。
“”(developerWorks,2003 年 8 月)更加深入地讨论了 Python 元类的一些细节。
“”(developerWorks,2006 年 12 月)介绍了 Python 最新的元编程功能。
查看实现所有功能的 。
阅读 Michele 的
中,可以找到更多的 Linux 开发人员的资源,浏览 。
在 developerWorks 上查看所有的
从 developerWorks 下载
,构建下一个 Linux 开发项目。
添加或订阅评论,请先或。
有新评论时提醒我
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=Linux, Open sourceArticleID=270762ArticleTitle=Python 中的元类编程,第 3 部分publish-date=《植物天然产物开发》复习题_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
《植物天然产物开发》复习题
阅读已结束,下载文档到电脑
想免费下载更多文档?
定制HR最喜欢的简历
你可能喜欢

我要回帖

更多关于 英雄联盟4800英雄 的文章

 

随机推荐