天天看點

python之特殊方法、屬性和疊代器

9.1 準備工作

class NewStyle(object)

more_code_here

class OldStyle:

在這兩個類中,NewStyle是新式的類,OldStyle是舊式的類。如果檔案以__metaclass__=type開始,那麼兩個類都是新式類。

除此之外,還可以在自己的類的作用域中對__metaclass__變量指派。這樣隻會為這個類設定元類。元類是其他類的類-----這是個更進階的主題。

9.2 構造方法

當一個對象被建立後,會立即調用構造方法。

>>>f = FooBar()

>>>f.init()

構造方法能讓它簡化成如下形式:

在python中建立一個構造方法很容易。隻要把init方法的名字從簡單的init修改為魔法版本__init__即可:

class FooBar:

def __init__(self):

self.somevar = 42

>>>f.somevar

42

class FooBar():

def __init__(self,value=42)

self.somevar = value

>>>f = FooBar('This is a constructor argument')

'This is a constructor argument'

python中有一個魔法方法叫做__del__,就是析構方法。它在對象就要被垃圾回收之前調用,但發生調用的具體時間是不可知的,是以建議讀者盡力避免使用__del__函數。

9.2.1 重寫一般方法和特殊的構造方法

如果一個方法在B類的一個執行個體中被調用,但在B類中沒有找到該方法,那麼就會去它的超類A裡面找。

class A:

def hello(self):

print "hello,I'm A."

class B(A):

pass

A類 定義了一個叫做hello的方法,被B類繼承。下面是一個說明類是如何工作的例子:

>>>a = A()

>>>b = B()

>>>a.hello()

hello,I'm A.

>>>b.hello()

hello,I'm A

因為B類沒有定義自己的hello方法,是以當hello被調用時,原始的資訊就被列印出來。

在子類中增加功能的最基本的方式就是增加方法。但是也可以重寫一些超類的方法來自定義繼承的行為。B類也能重寫這個方法。比如下面的例子中B類的定義被修改了。

print "hello,I'm B."

使用這個定義,b.hello()能産生一個不同的結果。

hello,I'm B.

雖然重寫的機制對于所有方法來說都是一樣的,但是當處理構造方法比重寫普通方法時,更可能遇到特别的問題:如果一個類的構造方法被重寫,那麼就需要調用超類的構造方法,否則對象可能不會被正确地初始化。

class Bird:

self.hungry = True

def eat(self):

if self.hungry:

print 'Aaaah...'

self.hungry = False

else:

print 'No,thanks!'

這個類定義所有的鳥都具有的一些最基本的能力:吃,用法示例;

>>>b = Bird()

>>>b.eat()

Aaaah...

No,thanks!

就像能在這個例子中看到的,鳥吃過了以後,它就不再饑餓。為子類SongBird,它添加了唱歌的行為。

class SongBird(Bird):

self.sound = 'Squawk!'

def sing(self):

print self.sound

SongBird類和Bird類一樣容易使用:

>>>sb = SongBird()

>>>sb.sing()

Squawk!

因為SongBird是Bird的一個子類,它繼承了eat方法,但如果調用eat方法,就會産生一個問題:

>>>sb.eat()

報錯

異常很清楚地說明了錯誤:SongBird沒有hungry特性。原因是這樣的:在SongBird中,構造方法被重寫,但新的構造方法沒有任何關于初始化hungry特性的代碼。為了達到預期的效果,SongBird的構造方法必須調用其超類Bird的構造方法來確定進行基本的初始化。有兩種方法能達到這個目的:調用超類構造方法的未綁定版本,或者使用super函數。

9.2.2 調用未綁定的超類構造方法

9.2.3 使用super函數

隻能在新式類使用super函數。目前的類和對象可以作為super函數的參數使用,調用函數傳回的對象的任何方法都是調用超類的方法,而不是目前類的方法。那麼就可以不用再SongBird的構造方法中使用Bird,而直接使用super(SongBird,self)。除此之外__init__方法能以一個普通的綁定方式被調用。

在python3.0 super函數可以不用任何參數進行調用。

下面的例子是對bird例子的更新。

__metaclass__ = type

super(SongBird,self).__init__()

No,thanks

9.3 成員通路

規則:它是管理某種形式的行為的規則

9.3.1 基本的序列和映射規則

序列和映射是對象的集合。為了實作它們基本的行為(規則),如果對象是不可變的,那麼就需要使用兩個魔法方法,如果是可變的則需要使用4個。

1.__len__(self):這個方法應該傳回集合中所含項目的數量。對于序列來說,這就是元素的個數;對于映射來說,則是鍵-值對得數量。如果__len__傳回0(并且沒有實作重寫該行為的__nonzero__),對象會被當作一個布爾變量中的假值(空的清單,元組,字元串和字典一樣)進行處理。

2.__getitem__(self,key):這個方法傳回與所給鍵對應的值。對于一個序列,鍵應該是一個0~n-1的整數(或者像後面所說的負數),n是序列的長度;對于映射來說,可以使用任何種類的鍵。

3.__setitem__(self,key,value):這個方法應該按一定的方式存儲和key相關的value,該值随可使用__getitem__來擷取。當然,隻能為可以修改的對象定義這個方法。

4.__delitem__(self,key):這個方法對一部分對象使用del語句時被調用,同時必須删除和元素相關的建。這個方法也是為可修改的對象定義的(并不是删除全部的對象,而隻删除一些需要移除的元素)。

對這些方法的附加要求:

1.對于一個序列來說,如果鍵是負整數,那麼要從末尾開始計數。換句話說就是x[-n]和x[len(x)-n]是一樣的;

2.如果鍵是不适合的類型(例如,對序列使用字元串作為鍵),會引發一個TypeError異常;

3.如果序列的索引是正确的類型,但超出了範圍,應該引發一個IndexError異常。

建立一個無窮序列:

def checkIndex(key):

if not isinstance(key),(int,long): raise TypeError

if key<0: raise IndexError

#鍵應該是一個非負整數

class ArithmeticSequence:

def __init__(self,start=0,step=1):

self.start = start   #序列中第一個值

self.step = step     #步長----兩個相鄰值之間的差别

self.changed = {}    #改變----使用者修改的值的字典

#初始化算術序列

def __getitem__(self,key):

Get an item from the arithmetic sequence.

checkIndex(key)

try: return self.changed[key]       #修改了麼

except KeyError:                    #否則。。。。

return self.start + key*self.step  #。。。。。計算值

def __setitem__(self,key,value):

#修改算術序列中的一個項

self.changed[key] = value  #儲存更改後的值

這裡實作的是一個算術序列,該序列中的每個元素都比它前面的元素大一個常數。第一個值是由構造方法參數start(預設為0)給出的,而值與值之間的步長是由step設定的(預設為1)。使用者能通過名為changed的方法将特例規則儲存在字典中,進而修改一些元素的值,如果元素沒有被修改,那就計算self start+key* step的值。

>>>s = ArithmeticSequence(1,2)

>>>s[4]

9

>>>s[4] = 2

2

>>>s[5]

11

注意,沒有實作__del__方法的原因是我希望删除元素是非法的:

>>>del s[4]

這個類沒有__len__方法,因為它是無限長的。

如果使用了一個非法類型的索引,就會引發TypeError異常,如果索引的類型是正确的但超出了範圍,則會引發IndexError異常:

>>>s["four"]

>>>s[-42]

索引檢查是通過使用者自定義的checkIndex函數實作的。

9.3.2 子類化清單,字典和字元串

如果希望實作一個和内建清單行為相似的序列,可以使用子類list。

當子類化一個内建類型----比如list的時候,也就間接的将object子類化了,是以類就自動成為新式類,這就意味着可以使用像super函數這樣的特性了。

帶有通路計數的清單

class CounterList(list):

def __init__(self,*args):

super(CounterList,self).__init__(*args)

self.counter = 0

def __getitem__(self,index):

self.counter += 1

return super(CounterList,self).__getitem__(index)

CounterList類嚴重依賴它的子類化超類的行為。CounterList類沒有重寫任何的方法(和append,extend,index一樣)都能被直接使用。在兩個被重寫的方法中,super方法被用來調用相應的超類的方法,隻在__init__中添加了所需的初始化counter特性的行為,并在__getitem__中更新了counter特性。

CounterList如何使用的例子

>>>c1 = CounterList(range(10))

>>>c1

[0,1,2,3,4,5,6,7,8,9]

>>>c1.reverse()

[9,8,7,6,5,4,3,2,1,0]

>>>del c1[3:6]

[9,8,7,3,2,1,0]

>>>c1.counter

>>>c1[4] + c1[2]

正如看到的,CounterList在很多方面和清單的作用一樣,但它有一個counter特性,每次清單元素被通路時,它都會自增,是以在執行家法c1[4] + c1[2],這個值自增兩次,變為2.

9.5 屬性

通路器是一個簡單的方法,它能夠使用getHeight、setHeight這樣的名字來得到或者重綁定一些特性。

class Rectangle:

self.width = 0

self.height = 0

def setSize(self,size):

self.width,self.height = size

def getSize(self):

return self.width,self.height

>>>r = Rectang()

>>>r.width = 10

>>>r.height = 5

>>>r.getSize()

(10,5)

>>>r.setSize((150,100))

>>>r.width

150

python能隐藏通路器方法,讓所有特性看起來一樣。這些通過通路器定義的特性被稱為屬性。

9.5.1 property函數

property函數的使用很簡單。如果已經編寫了一個像上節的Rectangle那樣的類,那麼隻要增加一行代碼(子類化object,或者使用__metaclass__=type語句):

__metaclass__=type

self.width =0 

size = property(getSize,setSize)

在這個新版的Rectangle中,property函數建立了一個屬性,其中通路器函數被用作參數(先是取值,然後是指派),這個屬性命為size。這樣一來就不再需要擔心是怎麼實作的了,可以用同樣的方式處理width、height和size。

>>>r = Rectangle()

>>>r.size

>>>r.size = 150,100

很明顯,size特性仍然取決于getSize和setSize中的計算。但它們看起來就像普通的屬性一樣。

如果屬性的行為很奇怪,那麼要確定你所使用的類為新式類;如果不是的話,雖然屬性的取值部分還是可以工作,但指派部分就不一定了。

實際上,property函數可以用0,1,2,3或者4個參數來調用。如果沒有參數,産生的屬性既不可讀,也不可寫。如果隻使用一個參數調用(一個取值方法),産生的屬性是隻讀的,第三個參數是可選是一個用于删除特性的方法。第四個參數可選是一個文檔字元串。property的4個參數分别被叫做fget、fset、fdel和doc--,如果想要一個屬性是隻寫的,并且有一個文檔字元串,能使用它們作為關鍵字參數。

9.5.2 靜态方法和類成員方法

靜态方法和類成員方法分别在建立時分别被裝入Staticmethod類型和Classmethod類型的對象中。靜态方法的定義沒有self參數,且能夠被類本身直接調用。類方法在定義時需要名為cls的類似于self的參數,類成員方法可以直接用類的具體對象調用。但cls參數是自動被綁定到類的:

class MyClass:

def smeth():

print 'This is a static method'

smeth = staticmethod(smeth)

def cmeth(cls):

print 'This is a class method of',cls

cmeth = classmethod(cmeth)

    手動包裝和替換方法的技術看起來有點單調。python 2.4中,為這樣的包裝方法引入了一個叫做裝飾器的新文法。使用@操作符,在方法的上方将裝飾器列出,進而指定一個或者更多的裝飾器。

class Myclass:

@staticmethod

@classmethod

定義了這些方法後,就可以像下面的例子那樣使用:

>>>MyClass.smeth()

This is a static method

>>>MyClass.cmeth()

This is a class method of <class '__main__.MyClass'>

靜态方法和類成員方法在python中并不是向來很重要,主要的原因時大部分情況不可以使用函數或者綁定方法代替。在早期的版本中沒有得到支援也是一個原因。但即使看不到兩者在目前代碼中的大量應用,也不要忽視靜态方法和類成員方法的應用。

9.5.3 __getattr__ setattr__和它的朋友們

攔截對象的所有特性通路時可能的,這樣可以用舊式類實作屬性。為了在通路特性的時候可以執行代碼,必須使用一些魔法方法。

1.__getattribute__(self,name):當特性name被通路時自動調用。

2.__getattr__(self,name):當特性name被通路且對象沒有相應的特性時被自動調用。

3.__setattr__(self,name,value):當試圖給特性name指派時會被自動調用。

4.__delattr__(self,name):當試圖删除特性name時被自動調用。

盡管和使用property函數相比有點複雜(而且在某些方面效率更低),但這些方法是很強大的,因為可以對處理很多屬性的方法進行再編碼。

下面還是Rectangle的例子,但這次使用的是特殊方法:

self.heigth = 0

def __setattr__(self,name,value):

if name == 'size':

self.width , self.heigth = value

self.__dict_[name] = value

def __getattr__(self,name)

return self.width , self.heigth

raise AttributeError

這個版本的類需要注意增加的管理細節。當思考這個例子時,下面的亮點應該引起讀者的重視:

1.__setattr__方法在所涉及到的特性不是size時也會調用。因為,這個方法必須把兩方面都考慮進去:如果屬性是size,那麼就像前面那樣執行操作,否則就要使用特殊方法__dict__,該特殊方法包含一個字典,字典裡面所有執行個體的屬性。為了避免__setarrt__方法被再次使用,__dict__方法被用來代替普通的特性指派操作。

2.__getattr__方法隻在普通的特性沒有被找到的時候調用,這就是說如果給定的名字不是size,這個特性不存在,這個方法會引發一個AttributeError異常。如果希望類和hasattr或者是getattr這樣的内建函數一起正确地工作,__getattr__方法就很重要。如果使用的時size屬性,那麼就會使用在前面的實作中找到的表達式。

就像死循環陷阱和__setattr__有關系一樣,還有一個陷阱和__getattribute__有關系。因為__getattribute__攔截所有特性的通路,也攔截對__dict__的通路!通路__getattribute__中與Self相關的特性時,使用超類的__getattribute__方法是唯一安全的途徑。

9.6 疊代器

9.6.1 疊代器規則

疊代的意思是重複做一些事很多次------就像在循環中做得那樣。到現在為止隻是在for循環中對序列和字典進行疊代,但實際上也能對其他的對象進行疊代:實作__iter__方法的對象。

__iter__方法傳回一個疊代器,所謂的疊代器就是具有next方法(這個方法在調用時不需要任何參數)的對象。在調用next方法時,疊代器會傳回它的下一個值。如果next方法被調用,但疊代器沒有值可以傳回,就會引發一個StopIteration異常。

疊代器規則在python 3.0 中有一些變化,在新的規則中,疊代器對象應該實作__next__方法,而不是next,而新的内建函數next可以用于通路這個方法。換句話說,next(it)等同于3.0之前版本中的it.next()

這裡的清單是一個斐波那契數列。使用疊代器如下:

class Fibs:

self.a = 0

self.b = 1

def next(self):

self.a , self.b = self.b , self.a+self.b

return self.a

def __iter__(self):

return self

注意,疊代器實作了__iter__方法,這個方法實際上傳回疊代器本身。在很多情況下,__iter__會放到其他的會在for循環中使用的對象中。這樣一來,程式就能傳回所需的疊代器。此外,推薦使疊代器實作它自己的__iter__方法,然後就能直接在for循環中使用疊代其本身。

正式的說法是,一個實作了__iter__方法的對象是可疊代的,一個實作了next方法的對象則是疊代器。

首先 産生一個Fibs對象:

>>>fibs = Fibs()

可在for循環中使用該對象----比如去查找在斐波那契數列中比1000大的數中的最小的數:

>>>for f in fibs:

if f > 1000:

print f

break

...

159

因為設定了break,是以循環在這裡停止了,否則循環會一直繼續下去。

内建函數iter可以從可疊代的對象中獲得疊代器。

>>>it = iter([1,2,3])

>>>it.next()

1

9.6.2 從疊代器得到序列

除了在疊代器和可疊代對象上進行疊代外,還能把它們轉換成序列。在大部分能使用序列的情況下,能使用疊代器替換。關于這個的一個很有用的例子是使用list構造方法顯式的将疊代器轉化為清單。

>>>class TestIterator

value = 0 

self.value += 1

if self.value > 10: raise StopIteration

return self.value

def __iter__(self)

>>>ti = TestIterator()

>>>list(ti)

[1,2,3,4,5,6,7,8,9,10]

9.7 生成器

生成器是一種用普通的函數文法定義的疊代器.

nested = [[1,2],[3,4],[5]]

def flatten(nested):

for sublist in nested:

for element in sublist:

yield element

首先疊代提供的嵌套清單中的所有子清單,然後按順序疊代子清單中的元素.如果最後一行是print element的話,那麼就容易了解力.

任何包含yield語句的函數稱為生成器.除了名字不同以外,它的行為和普通的函數也有很大的差别,這就在于它不是像return那樣傳回值,而是每次産生多個值.每次産生一個值,函數就會被當機:即函數停在那點等待被激活.函數被激活後就從停止的那點開始執行.

通過在生成器上疊代來使用所有的值.

>>>nested = [[1,2],[3,4],[5]]

>>>for num in flatten(nested):

print num

3

4

5

or

>>>list(flatten(nested))

[1,2,3,4,5]

循環生成器

生成器推導式和清單推導式的工作方式類似,隻不過傳回的不是清單而是生成器,所傳回的生成器允許你像下面這樣一步一步地進行計算:

>>>g = ((i+2)**2 for i in range(2,27))

>>>g.next()

16

和清單推導式不同的就是普通圓括号的使用方式,在這樣簡單的例子中,還是推薦大家使用清單推導式.

生成器推導式可以在目前的圓括号直接使用,例如在函數調用中,不用增加另外一對圓括号,換句話說,可以像下面這樣編寫代碼:

sum(i**2 for i in range(10))

9.7.2 遞歸生成器

def flatten(nested)

try:

for element in flatten(sublist:)

except TypeError:

yield nested

當flatten被調用時,有兩種可能性:基本情況和需要遞歸的情況。在基本的情況中,函數被告知展開一個元素,這種情況下,for循環會引發一個TypeError異常,生成器會産生一個元素。

如果展開的是一個清單,那麼就要進行特殊處理。程式必須周遊所有的子清單,并對它們調用flatten。然後使用另一個for循環來産生被展開的子清單中的所有元素。

>>>list(flatten([[[1],2],3,4,[5,[6,7]],8]))

[1,2,3,4,5,6,7,8]

這麼做隻有一個問題:如果nested是一個類似于字元串的對象,那麼它就是一個序列,不會引發TypeError,但是你不想對這樣的對象進行疊代。

不應該在flatten函數中對類似于字元串的對象進行疊代,出于兩個主要的原因。首先,需要實作的是将類似于字元串的對象當成原子值,而不是當成應被展開的序列。其次,對它們進行疊代實際上會導緻無窮遞歸,因為一個字元串的第一個元素是另一個長度為1的字元串,而長度為1的字元串的第一個元素就是字元串本身。

為了處理這種情況,則必須在生成器的開始處添加一個檢查語句。試着将傳入的對象和一個字元串拼接,看看會不會出現TypeError,這是檢查一個對象是不是似類于字元串的最簡單、最快速的方法。下面是加入了檢查語句的生成器:

try: nested+ ''

except TypeError:pass

else:raise TypeError

for element in flatten(sublist):

如果表達式nested+ 引發了一個TypeError,它就會被忽略。然而如果沒有引發TypeError,那麼内層try語句中的else子句就會引發一個它自己的TypeError異常。這就會按照原來的樣子生成類似于字元串的對象。

>>>list(flatten({'foo',['bar',['baz']]]))

['foo','bar','baz']

上面的代碼沒有執行類型檢查.這裡沒有測試nested是否是一個字元串,而隻是檢查nested的行為是不是像一個字元串.

9.7.3 通用生成器

生成器是一個包含yield關鍵字的函數.當它被調用時,在函數體中的代碼不會被執行,而會傳回一個疊代器.每次請求一個值,就會執行生成器中德代碼,直到遇到一個yield或者return語句.yield語句意味着應該生成一個值.return語句意味着生成器要停止執行.換句話說,生成器是兩部分組成:生成器的函數和生成器的疊代器.生成器的函數是def語句定義的,包含yield的部分,生成器的疊代器是這個函數傳回的部分.按一種不是很準确的說法,兩個實體經常被當作一個,合起來叫做生成器.

>>>def simple_generator():

yield 1

>>>simple_generator

<function simple_generator at 153b44>

>>>simple_generator()

<generator object at 1510b0>

生成器的函數傳回的疊代器可以像其他的疊代器那樣使用.

9.7.4 生成器方法

生成器的新屬性是在開始運作後衛生成器提供值得能力.表現為生成器和"外部世界"進行交流的管道,要注意下面兩點.

1.外部作用域通路生成器的send方法,就像通路next方法一樣,隻不過前者使用一個參數

2.在内部則刮起生成器,yield現在作為表達式而不是語句使用,換句話說,當生成器重新運作的時候,yield方法傳回一個值,也就是外部通過send方法發送的值.如果next方法被使用,那麼yield方法傳回None.

注意,使用send方法隻有在生成器挂起之後才有意義,如果在此之前需要給生成器提供更多資訊,那麼隻需使用生成器函數的參數.

def repeater(value):

while True:

new = (yield value)

if new is not None: value = new

使用方法如下:

r = repeater(42)

r.next()

r.send("Hello,world!")

"Hello,world!"

注意看yield表達式周圍的括号的使用.雖然并未嚴格要求,但在使用傳回值得時候,安全起見還是要閉合yield表達式.

生成器還有其他兩種方法:

1.throw方法用于在生成器内引發一個異常

2.close方法用于停止生成器

close方法也是建立在異常的基礎上的.他在yield運作處引發一個GeneratorExit異常,是以如果需要在生成器内進行代碼清理的話,則可以将yield語句放在try/finally語句中.如果需要的話,還可以捕捉GeneratorExit異常,但随後必需将其重新引發,引發另外一個異常或者直接傳回.試着在生成器的close方法被調用後再通過生成器生成一個值則會導緻RuntimeError異常.

9.7.5 模拟生成器

如何使用普通的函數模拟生成器。

先從生成器的代碼開始。首先将下面語句放在函數體的開始處:

result = []

如果代碼已經使用了result這個名字,那麼應該用其他名字代替,然後将下面這種形式的代碼

yield some_expression

用下面的語句替換:

result.append(some_expression)

最後,在函數的末尾,添加下面這條語句:

return  result

盡管這個版本可能不适用于所有生成器,但對大多數生成器來說是可行的。

flatten生成器用普通的函數重寫的版本

try: nested + ''

result.append(element)

result.append(nested)

return result

9.8 八皇後問題

9.8.1 生成器和回溯

生成器是逐漸産生結果的複雜遞歸算法的理想實作工具。

def conflict(state,nextX):

nextY = len(state)

for i in range(nextY):

if abs(state[i]-nextX) in (0,nextY-i):

return True

return False

參數nextX代表下一個皇後的水準位置(x坐标或列),nextY代表垂直位置(y坐标或行)。這個函數對前面的每個皇後的位置做一個簡單的檢查,如果下一個皇後和前面的皇後有同樣的水準位置,或者是在一條對角線上,就會發生沖突,接着傳回True。如果沒有這樣的沖突發生,那麼傳回False,不太容易了解的是下面的表達式:

abs(state[i]-nextX) in (0,nextY-i):

如果下一個皇後和正在考慮的前一個皇後的水準距離為0或者等于垂直距離就傳回True,否則就傳回False。

9.8.5 基本情況

從基本的情況開始:最後一個皇後。你想讓它做什麼?假設你想找出所有可能的解決方案;

這樣一來,它能根據其他的皇後位置生成它自己能占據的所有位置。能把這樣的情況直接描繪出。

def queens(num,state):

if len(state) == num-1:

for pos in range(num):

if not conflict(state,pos):

yield pos

用人類的語言來描述,它的意思是:如果隻剩一個皇後沒有位置,那麼周遊它所有的可能的位置,并且傳回沒有沖突發生的位置。num參數是皇後的總數。state參數是存放前面皇後的位置資訊的元組。假設有4個皇後,前3個分别被放置在1,3,0号位置。

>>>list(queens(4,(1,3,0)))

[2]

9.8.6  需要遞歸的情況

for result in queens(num,state + (pos))

yield(pos)+result

for pos和if not conflice部分和前面的代碼相同。添加一些預設的參數:

def queens(num=8,state()):

yield(pos,)

for result in queens(num,state+ (pos,)):

yiled(pos) + result

生成器queens能給出所有的解決方案

>>>list(queens(3))

[]

>>>list(queens(4))

[(1,3,0,2),(2,0,3,1)]

>>>for solution in queens(8):

print solution

(0,4,7,5,2,6,1,3)

(0,5,7,2,6,3,1,4)

如果用8個皇後做參數來運作queens。

>>>len(list(queens(8)))

92

9.8.7 打包

def prettyprint(solution):

def line(pos,length=len(solution));

return '. ' * pos + 'X ' + '. ' * (length-pos-1)

for pos in solution:

print line(pos)

注意prettyprint中建立了一個小的助手函數。之是以将其放在prettyprint内,是因為我們假設在外面的任何地方都不會用到它。下面列印出一個令我滿意的随機解決方案。可以看到該方案是正确的。

>>>import random

>>>prettyprint(random.choice(list(queens(8))))

      本文轉自潘闊 51CTO部落格,原文連結:http://blog.51cto.com/pankuo/1661443,如需轉載請自行聯系原作者