欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Python的PEAK來(lái)適配協(xié)議的教程

 更新時(shí)間:2015年04月14日 11:13:49   投稿:goldensun  
這篇文章主要介紹了使用Python的PEAK來(lái)適配協(xié)議的教程,來(lái)自于IBM官方網(wǎng)站技術(shù)文檔,需要的朋友可以參考下

如果您正嘗試去處理元類(lèi),或者正受困于 Twisted 中的異步編程,或者正在研究由于使用了多分派而使您精疲力盡的面向?qū)ο缶幊蹋敲茨耆e(cuò)了!PEAK 將所有這些中的一些要素組合到了一個(gè)組件編程框架中。PEAK 還存在一些小問(wèn)題。類(lèi)似于 Twisted,PEAK 的文檔 -- 盡量數(shù)量巨大 -- 難以看懂。但是盡管如此,關(guān)于 Python 領(lǐng)袖 Phillip J. Eby 領(lǐng)導(dǎo)的這一項(xiàng)目還是有一些東西非常值得關(guān)注;而且,我覺(jué)得,有機(jī)會(huì)進(jìn)行極具生產(chǎn)價(jià)值的并且層次特別高的應(yīng)用程序開(kāi)發(fā)。

PEAK 包由許多不同用途的子包組成。一些重要的子包是 peak.api、 peak.binding、 peak.config、 peak.naming 和 peak.storage 。那些名字大部分是自我解釋性的。子包 peak.binding 用于組件間的靈活連接; peak.config 讓您可以存儲(chǔ)“很少改變的(lazily immutable)”數(shù)據(jù),這些數(shù)據(jù)與聲明性應(yīng)用程序(declarative application )編程有關(guān); peak.naming 讓您可以為(網(wǎng)絡(luò)的)資源創(chuàng)建全局惟一的標(biāo)識(shí)符; peak.storage 顧名思義讓您可以管理數(shù)據(jù)庫(kù)和持久內(nèi)容。

不過(guò),對(duì)本文來(lái)說(shuō),我們將關(guān)注的是 peak.api 。特別是 PyProtocols 包,它可以單獨(dú)獲得并為其他 PEAK 子包提供一個(gè)基礎(chǔ)設(shè)施。在 peak.api.protocols 中包括了 PyProtocols 包的一個(gè)版本。不過(guò)現(xiàn)在我所感興趣的是研究一個(gè)獨(dú)立的 protocols 包。在以后的部分,我將返回來(lái)討論 PEAK 其他部分的話題。
什么是協(xié)議?


抽象地說(shuō),協(xié)議只是對(duì)象同意遵循的一組行為。強(qiáng)類(lèi)型(Strongly-typed)編程語(yǔ)言 -- 包括 Python -- 都有一個(gè)基本類(lèi)型的集合,每個(gè)基本類(lèi)型都有一組得到保證的行為:Integer 知道如何去求它們自己的乘積;list 知道如何去遍歷它們的內(nèi)容;dictionary 知道如何根據(jù)一個(gè)關(guān)鍵字找到相應(yīng)的值;file 知道如何去讀和寫(xiě)字節(jié);諸如此類(lèi)。您可以預(yù)期的內(nèi)置類(lèi)型的行為集合構(gòu)成了它們實(shí)現(xiàn)的一個(gè) 協(xié)議。對(duì)協(xié)議進(jìn)行系統(tǒng)化的對(duì)象被稱(chēng)為 接口(interface)。

對(duì)標(biāo)準(zhǔn)的類(lèi)型而言,將實(shí)現(xiàn)的所有行為全部列出并不太困難(盡管不同的 Python 版本之間會(huì)稍有不同;或者,不同的編程語(yǔ)言之間當(dāng)然會(huì)有差別)。但是,在邊界 -- 對(duì)于屬于自定義類(lèi)的對(duì)象來(lái)說(shuō) -- 難以聲明最終是什么構(gòu)成了“類(lèi)-dictionary”或“類(lèi)-file”的行為。大部分情況下,只實(shí)現(xiàn)了比如內(nèi)置的 dict 類(lèi)型的方法的一個(gè)子集 -- 甚至是相當(dāng)小的子集 -- 的自定義對(duì)象,就足夠“類(lèi)-dictionary”而可以滿足當(dāng)前的要求。不過(guò),能顯式地整理出一個(gè)對(duì)象要用到的函數(shù)、模塊、類(lèi)或者框架中需要能夠做哪些事情,將是很吸引人的。那就是 PyProtocols 包所做到的(一部分)。

在具有靜態(tài)類(lèi)型聲明的編程語(yǔ)言中,為了在新的上下文中使用數(shù)據(jù),您通常需要將其自一個(gè)類(lèi)型 強(qiáng)制類(lèi)型轉(zhuǎn)換(cast)或者 轉(zhuǎn)換(convert)到另一個(gè)類(lèi)型。在其他語(yǔ)言中,轉(zhuǎn)換根據(jù)上下文的需要隱式地進(jìn)行,這些被稱(chēng)為 強(qiáng)迫同型(coercions)。Python 中既有強(qiáng)制類(lèi)型轉(zhuǎn)換也有強(qiáng)迫同型,通常使用更多的是前者(“顯式優(yōu)于隱式”)。您可以將向一個(gè)浮點(diǎn)數(shù)加到一個(gè)整型數(shù),結(jié)果得到一個(gè)更為通用的浮點(diǎn)數(shù);但是如果您希望將字符串 "3.14" 轉(zhuǎn)換為一個(gè)數(shù)字,那么您需要使用顯式的構(gòu)造函數(shù) float("3.14") 。

PyProtocols 具有一個(gè)稱(chēng)為“適配(adaptation)”的功能,類(lèi)似于“部分類(lèi)型(partial typing)”這一非正統(tǒng)計(jì)算機(jī)科學(xué)概念。適配還可能被認(rèn)為是“加速的強(qiáng)制同型”。如果一個(gè)接口定義了所需要的一組能力 (也就是對(duì)象方法),那么要去做“所需要的一切”的對(duì)象就要求適配 -- 通過(guò) protocols.adapt() 函數(shù)實(shí)現(xiàn) -- 以提供所需要的能力。顯然,如果您有一個(gè)顯式的轉(zhuǎn)換函數(shù)可以將類(lèi)型 X 的對(duì)象轉(zhuǎn)換為類(lèi)型 Y 的對(duì)象(在這里 Y 實(shí)現(xiàn)了某個(gè) IY 接口),那么那個(gè)函數(shù)要能夠讓 X 適配協(xié)議 IY 。不過(guò),PyProtocols 中的適配可以做比這多得多的事情。例如,甚至如果您從來(lái)沒(méi)有顯式地編寫(xiě)過(guò)從類(lèi)型 X 到類(lèi)型 Y 的轉(zhuǎn)換程序, adapt() 通??梢酝蒲莩鲆粭l讓 X 提供 IY 所要求的能力的途徑(也就是說(shuō),找到從 X 到接口 IZ ,從 IZ 到 IW ,然后再?gòu)?IW 到 IY 的中間轉(zhuǎn)換)。

聲明接口和適配器


在 PyProtocols 中有很多不同的方法可以創(chuàng)建接口和適配器。PyProtocols 文檔非常詳細(xì)地介紹了這些技術(shù) -- 很多不會(huì)在本文中涉及。接下來(lái)我們將進(jìn)入一些細(xì)節(jié),不過(guò),我覺(jué)得,在這里給出實(shí)際的 PyProtocols 代碼的一個(gè)最簡(jiǎn)化實(shí)例是個(gè)有用的方法。

例如,我決定創(chuàng)建一個(gè) Python 對(duì)象的類(lèi)-Lisp 序列化。其描述并不是準(zhǔn)確的 Lisp 語(yǔ)法,我也并不在意這種格式確切的優(yōu)點(diǎn)和缺點(diǎn)。在這里,我的想法只是創(chuàng)建一個(gè)功能,使之可以執(zhí)行類(lèi)似 repr() 函數(shù)或 pprint 模塊的工作,不過(guò)結(jié)果是既與以前串行器(serializers)有明顯的不同,又要能更容易地?cái)U(kuò)展/定制。出于舉例說(shuō)明的目的做出了一個(gè)非常不像 Lisp 的選擇:映射(mappings)是一個(gè)比列表(list)更為基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)(Python 的元組(tuple)或列表被作為以連續(xù)整數(shù)為鍵的映射來(lái)處理)。下面是代碼:

lispy.py PyProtocol 定義

from protocols import *
from cStringIO import StringIO
# Like unicode, & even support objects that don't explicitly support ILisp
ILisp = protocolForType(unicode, ['__repr__'], implicit=True)
# Class for interface, but no methods specifically required
class ISeq(Interface): pass
# Class for interface, extremely simple mapping interface
class IMap(Interface):
  def items():
    "A requirement for a map is to have an .items() method"
# Define function to create an Lisp like representation of a mapping
def map2Lisp(map_, prot):
  out = StringIO()
  for k,v in map_.items():
    out.write("(%s %s) " % (adapt(k,prot), adapt(v,prot)))
  return "(MAP %s)" % out.getvalue()
# Use this func to convert an IMap-supporting obj to ILisp-supporting obj
declareAdapter(map2Lisp, provides=[ILisp], forProtocols=[IMap])
# Note that a dict implements an IMap interface with no conversion needed
declareAdapter(NO_ADAPTER_NEEDED, provides=[IMap], forTypes=[dict])
# Define and use func to adapt an InstanceType obj to the ILisp interface
from types import InstanceType
def inst2Lisp(o, p):
  return "(CLASS '(%s) %s)" % (o.__class__.__name__, adapt(o.__dict__,p))
declareAdapter(inst2Lisp, provides=[ILisp], forTypes=[InstanceType])
# Define a class to adapt an ISeq-supporting obj to an IMap-supporting obj
class SeqAsMap(object):
  advise(instancesProvide=[IMap],
      asAdapterForProtocols=[ISeq] )
  def __init__(self, seq, prot):
    self.seq = seq
    self.prot = prot
  def items(self):  # Implement the IMap required .items() method
    return enumerate(self.seq)
# Note that list, tuple implement an ISeq interface w/o conversion needed
declareAdapter(NO_ADAPTER_NEEDED, provides=[ISeq], forTypes=[list, tuple])
# Define a lambda func to adapt str, unicode to ILisp interface
declareAdapter(lambda s,p: "'(%s)" % s,
        provides=[ILisp], forTypes=[str,unicode])
# Define a class to adapt several numeric types to ILisp interface
# Return a string (ILisp-supporting) directly from instance constructor
class NumberAsLisp(object):
  advise(instancesProvide=[ILisp],
      asAdapterForTypes=[long, float, complex, bool] )
  def __new__(klass, val, proto):
    return "(%s %s)" % (val.__class__.__name__.upper(), val)

在上面的代碼中,我已經(jīng)用一些不同的方法聲明了許多適配器。在一些情況中,代碼將一個(gè)接口轉(zhuǎn)換到另一個(gè)接口;在其他情況中,類(lèi)型本身直接適配到另一個(gè)接口。我希望您能注意到關(guān)于代碼的一些方面:(1)沒(méi)有創(chuàng)建任何從 list 或 tuple 到 ILisp 接口的適配器;(2)沒(méi)有為 int 數(shù)字類(lèi)型顯式聲明適配器;(3)就此而言,沒(méi)有聲明直接由 dict 到 ILisp 的適配器。下面是代碼將如何適配( adapt() )各種 Python 對(duì)象:

test_lispy.py 對(duì)象序列化

from lispy import *
from sys import stdout, stderr
toLisp = lambda o: adapt(o, ILisp)
class Foo:
  def __init__(self):
    self.a, self.b, self.c = 'a','b','c'
tests = [
 "foo bar",
 {17:2, 33:4, 'biz':'baz'},
 ["bar", ('f','o','o')],
 1.23,
 (1L, 2, 3, 4+4j),
 Foo(),
 True,
]
for test in tests:
  stdout.write(toLisp(test)+'\n')

運(yùn)行時(shí),我們得到:

test_lispy.py 序列化結(jié)果

$ python2.3 test_lispy.py
'(foo bar)
(MAP (17 2) ('(biz) '(baz)) (33 4) )
(MAP (0 '(bar)) (1 (MAP (0 '(f)) (1 '(o)) (2 '(o)) )) )
(FLOAT 1.23)
(MAP (0 (LONG 1)) (1 2) (2 3) (3 (COMPLEX (4+4j))) )
(CLASS '(Foo) (MAP ('(a) '(a)) ('(c) '(c)) ('(b) '(b)) ))
(BOOL True)

對(duì)我們的輸出進(jìn)行一些解釋將會(huì)有所幫助。第一行比較簡(jiǎn)單,我們定義了一個(gè)直接從字符串到 ILisp 的適配器,對(duì) adapt("foo bar", ILisp) 的調(diào)用只是返回了 lambda 函數(shù)的結(jié)果。下一行只是有一點(diǎn)復(fù)雜。沒(méi)有直接從 dict 到 ILisp 的適配器;但我們不必使用任何適配器就可以讓 dict 去適配 IMap (我們聲明了足夠多),而且我們有從 IMap 到 ILisp 的適配器。類(lèi)似的,對(duì)于后面的列表和元組,我們可以使 ILisp 適配 ISeq ,使 ISeq 適配 IMap ,并使 IMap 適配 ILisp 。PyProtocols 會(huì)指出要采取的適配路徑,所有這些不可思議的過(guò)程都在幕后完成。一個(gè)舊風(fēng)格的實(shí)例所經(jīng)歷的過(guò)程與字符串或者支持 IMap 的對(duì)象相同,我們有一個(gè)直接到 ILisp 的適配。

不過(guò),等一下。在我們的 dict 和 tuple 對(duì)象中用到的所有的整數(shù)是怎么處理的呢? long 、 complex、float 和 bool 類(lèi)型的數(shù)字有顯式的適配器,不過(guò) int 一個(gè)都沒(méi)有。這里的技巧在于, int 對(duì)象已經(jīng)擁有一個(gè) .__repr__() 方法;通過(guò)將隱式支持聲明為 ILisp 接口的一部分,我們可以巧妙地使用對(duì)象已有的 .__repr__() 方法作為對(duì) ILisp 接口的支持。實(shí)際上,作為一個(gè)內(nèi)置的類(lèi)型,整數(shù)用不加任何修飾的阿拉伯?dāng)?shù)字表示,而不使用大寫(xiě)的類(lèi)型初始器(比如 LONG )。

適配協(xié)議


讓我們來(lái)更明確地看一下 protocol.adapt() 函數(shù)都做了什么事情。在我們的例子中,我們使用“聲明 API(declaration API)”來(lái)隱式地為適配設(shè)置了一組“工廠(factories)”。這個(gè) API 有幾個(gè)層次。聲明 API 的“基本層次(primitives)”是函數(shù): declareAdaptorForType() 、 declareAdaptorForObject() 和 declareAdaptorForProtocol() 。前面的例子中沒(méi)有用到這些,而是用到了一些高層次的 API,如 declareImplementation() 、 declareAdaptor() 、 adviceObject() 和 protocolForType() 。在一種情況下,我們看到在一個(gè)類(lèi)體中有“奇妙”的 advise() 聲明。 advise() 函數(shù)支持用于配置那些建議的類(lèi)的目的和角色的大量關(guān)鍵字參數(shù)。您還可以建議 (advise()) 一個(gè)模塊對(duì)象。

您不需要使用聲明 API 來(lái)創(chuàng)建知道如何使對(duì)象適配( adapt() )自己的可適配的對(duì)象或者接口。讓我們來(lái)看 adapt() 的調(diào)用標(biāo)記,然后解釋它隨后的過(guò)程。對(duì) adapt() 的調(diào)用類(lèi)似這樣:

adapt() 的調(diào)用標(biāo)記

adapt(component, protocol, [, default [, factory]])

這就表示您希望讓對(duì)象 component 去適配接口 protocol 。如果指定了 default ,它可以返回為一個(gè)包裝對(duì)象(wrapper object)或者對(duì) component 的修改。如果 factory 被指定為一個(gè)關(guān)鍵字參數(shù),那么會(huì)使用一個(gè)轉(zhuǎn)換工廠來(lái)生成包裝或者修改。不過(guò)讓我們先退回一點(diǎn),來(lái)看一下 adapt() 嘗試的完整的動(dòng)作次序(簡(jiǎn)化的代碼):

adapt() 的假想實(shí)現(xiàn)

if isinstance(component, protocol):
  return component
elif hasattr(component,'__conform__'):
  return component.__conform__(protocol)
elif hasattr(protocol,'__adapt__'):
  return protocol.__adapt__(component)
elif default is not None:
  return default
elif factory is not None:
  return factory(component, protocol)
else:
  NotImplementedError

對(duì) adapt()的調(diào)用 應(yīng)該保持一些特性(不過(guò)這是對(duì)程序員的建議,而不是庫(kù)的一般強(qiáng)制要求)。對(duì) adapt() 的調(diào)用應(yīng)該是等冪的。也就是說(shuō),對(duì)于一個(gè)對(duì)象 x 和一個(gè)協(xié)議 P ,我們希望: adapt(x,P)==adapt(adapt(x,P),P) 。高級(jí)地,這樣做的目的類(lèi)似于從 .__iter__() 方法返回自身( self )的迭代器(iterator)類(lèi)的目的。您基本上不會(huì)希望去重新適配到您已經(jīng)適配到的相同類(lèi)型以產(chǎn)生波動(dòng)的結(jié)果。

還值得注意的是,適配可能是有損耗的。為了讓一個(gè)對(duì)象去順應(yīng)一個(gè)接口,可能不方便或者不可能保持重新初始化這個(gè)對(duì)象所需要的所有信息。也就是說(shuō),通常情況下,對(duì)對(duì)象 x 及協(xié)議 P1 和 P2 而言: adapt(x,P1)!=adapt(adapt(adapt(x,P1),P2),P1) 。

在結(jié)束之前,讓我們來(lái)看另一個(gè)利用了 adapt() 的低層次行為的測(cè)試腳本:

test_lispy2.py 對(duì)象序列化

from lispy import *
class Bar(object):
  pass
class Baz(Bar):
  def __repr__(self):
    return "Represent a "+self.__class__.__name__+" object!"
class Bat(Baz):
  def __conform__(self, prot):
    return "Adapt "+self.__class__.__name__+" to "+repr(prot)+"!"
print adapt(Bar(), ILisp)
print adapt(Baz(), ILisp)
print adapt(Bat(), ILisp)
print adapt(adapt(Bat(), ILisp), ILisp)
$ python2.3 test_lispy2.py
<__main__.Bar object at 0x65250>
Represent a Baz object!
Adapt Bat to WeakSubset(<type 'unicode'>,('__repr__',))!
'(Adapt Bat to WeakSubset(<type 'unicode'>,('__repr__',))!)

結(jié)果證明 lispy.py 的設(shè)計(jì)不能滿足等冪的目標(biāo)。改進(jìn)這一設(shè)計(jì)可能是個(gè)不錯(cuò)的練習(xí)。不過(guò),像 ILisp 這樣的描述肯定會(huì)損耗原始對(duì)象中的信息(這是沒(méi)關(guān)系的)。

結(jié)束語(yǔ)


感覺(jué)上,PyProtocols 與本專(zhuān)欄提及的其他“外來(lái)”話題有一些共同之處。首先,聲明 API 是聲明性的(相對(duì)于解釋性)。聲明性編程并不給出執(zhí)行一個(gè)動(dòng)作所需要的步驟和開(kāi)關(guān),而是聲明處理特定的內(nèi)容,由庫(kù)或編譯器來(lái)具體指出如何執(zhí)行。名稱(chēng)“declare*()”和“advice*()”正在來(lái)自于這一觀點(diǎn)。

不過(guò),我也發(fā)現(xiàn) PyProtocols 編程有些類(lèi)似于使用多分派進(jìn)行編程,具體說(shuō)就是使用我在另一期文章提到的 gnosis.magic.multimethods 模塊。與 PyProtocols 的確定適配路徑形成對(duì)照,我自己的模塊執(zhí)行了一個(gè)相對(duì)簡(jiǎn)單的推演,確定要分派的相關(guān)祖先類(lèi)。不過(guò)兩個(gè)庫(kù)都傾向于在編程中鼓勵(lì)使用類(lèi)似的模塊化思想 -- 由大量的小函數(shù)或類(lèi)來(lái)執(zhí)行“可插入的”任務(wù),不需要受死板的類(lèi)層級(jí)結(jié)構(gòu)所困。在我看來(lái),這種風(fēng)格有其優(yōu)越之處。

相關(guān)文章

  • python訪問(wèn)抓取網(wǎng)頁(yè)常用命令總結(jié)

    python訪問(wèn)抓取網(wǎng)頁(yè)常用命令總結(jié)

    這篇文章主要介紹了python訪問(wèn)抓取網(wǎng)頁(yè)常用命令的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • Python列表切片操作實(shí)例總結(jié)

    Python列表切片操作實(shí)例總結(jié)

    這篇文章主要介紹了Python列表切片操作,結(jié)合實(shí)例形式總結(jié)分析了Python列表切片常見(jiàn)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-02-02
  • Python中cv2.Canny() 函數(shù)使用方法

    Python中cv2.Canny() 函數(shù)使用方法

    cv2.Canny() 函數(shù)是 OpenCV 中的邊緣檢測(cè)函數(shù)之一,用于檢測(cè)圖像的邊緣,它的基本原理是通過(guò)計(jì)算圖像中每個(gè)像素點(diǎn)的梯度值來(lái)檢測(cè)邊緣,本文通過(guò)示例代碼介紹Python中cv2.Canny() 函數(shù)用法,需要的朋友參考下吧
    2023-07-07
  • Python錯(cuò)誤處理操作示例

    Python錯(cuò)誤處理操作示例

    這篇文章主要介紹了Python錯(cuò)誤處理操作,結(jié)合實(shí)例形式分析了Python使用try...except...finaly語(yǔ)句進(jìn)行錯(cuò)誤處理的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-07-07
  • 深入理解Python虛擬機(jī)中魔術(shù)方法的使用

    深入理解Python虛擬機(jī)中魔術(shù)方法的使用

    這篇文章主要給大家介紹在?cpython?當(dāng)中一些比較花里胡哨的魔術(shù)方法,以幫助我們自己實(shí)現(xiàn)比較花哨的功能,當(dāng)然這其中也包含一些也非常實(shí)用的魔術(shù)方法,需要的可以參考下
    2023-05-05
  • Django Channel實(shí)時(shí)推送與聊天的示例代碼

    Django Channel實(shí)時(shí)推送與聊天的示例代碼

    這篇文章主要介紹了Django Channel實(shí)時(shí)推送與聊天的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Python繪制并保存指定大小圖像的方法

    Python繪制并保存指定大小圖像的方法

    今天小編就為大家分享一篇Python繪制并保存指定大小圖像的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Python 16進(jìn)制與中文相互轉(zhuǎn)換的實(shí)現(xiàn)方法

    Python 16進(jìn)制與中文相互轉(zhuǎn)換的實(shí)現(xiàn)方法

    今天小編就為大家分享一篇Python 16進(jìn)制與中文相互轉(zhuǎn)換的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • python使用phoenixdb操作hbase的方法示例

    python使用phoenixdb操作hbase的方法示例

    這篇文章主要介紹了python使用phoenixdb操作hbase的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • Python字符串三種格式化輸出

    Python字符串三種格式化輸出

    這篇文章主要介紹了Python字符串三種格式化輸出,需要的朋友可以參考下
    2020-09-09

最新評(píng)論