亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

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

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

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


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

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

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

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

聲明接口和適配器


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

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

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

test_lispy.py 對象序列化

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')

運行時,我們得到:

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)

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

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

適配協(xié)議


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

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

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

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

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

adapt() 的假想實現(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

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

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

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

test_lispy2.py 對象序列化

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è)計不能滿足等冪的目標(biāo)。改進(jìn)這一設(shè)計可能是個不錯的練習(xí)。不過,像 ILisp 這樣的描述肯定會損耗原始對象中的信息(這是沒關(guān)系的)。

結(jié)束語


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

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

相關(guān)文章

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

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

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

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

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

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

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

    Python錯誤處理操作示例

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

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

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

    Django Channel實時推送與聊天的示例代碼

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

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

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

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

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

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

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

    Python字符串三種格式化輸出

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

最新評論