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,如需轉載請自行聯系原作者