一般网站的宽度是多少像素,wordpress主题使用帮助,小微企业所得税优惠政策,南通做网站公司哪家好第九章 魔法方法、特性和迭代器
构造函数
构造函数#xff08;constructor#xff09;#xff0c;它其实就是初始化方法#xff0c;只是命名为__init__。 构造函数不同于普通方法的地方在于#xff0c;将在对象创建后自动调用它们。
在Python中#xff0c;创建构造函数…第九章 魔法方法、特性和迭代器
构造函数
构造函数constructor它其实就是初始化方法只是命名为__init__。 构造函数不同于普通方法的地方在于将在对象创建后自动调用它们。
在Python中创建构造函数很容易只需将方法init的名称从普通的init改为魔法版__init__即可。
class Beyond:def __init__(self):self.band 1999sq Beyond()
sq.band#结果为1999给构造函数添加几个参数
class Beyond:def __init__(self,value 1999):self.band valueyy Beyond(This is a band)
yy.band#结果为This is a bandPython提供了魔法方法__del__也称作析构函数destructor。这个方法在对象被销毁作为垃圾被收集前被调用但鉴于你无法知道准确的调用时间建议尽可能不要使用__del__。
重写普通方法和特殊的构造函数
每个类都有一个或多个超类并从它们那里继承行为。
对类B的实例调用方法或访问其属性时如果找不到该方法或属性将在其超类A中查找。 类A定义了一个名为hello的方法并被类B继承。 由于类B自己没有定义方法hello因此对其调用方法hello时打印的是消息Hello, I am A.。
class A:def hello(self):print(Hello,I am A)class B(A):passa A()
b B()a.hello()#结果为Hello,I am A
b.hello()#结果为Hello,I am A当然类B可以重写方法hello 重写是继承机制的一个重要方面对构造函数来说尤其重要。
class A:def hello(self):print(Hello,I am A)class B(A):def hello(self):print(Hello,I am B)b B()
b.hello()#结果为Hello,I am B构造函数用于初始化新建对象的状态而对大多数子类来说除超类的初始化代码外还需要有自己的初始化代码。
重写构造函数时必须调用超类继承的类的构造函数否则可能无法正确地初始化对象。 Bird类定义了所有鸟都具备的一种基本能力进食。鸟进食后就不再饥饿。
class Bird:def __init__(self):self.hungry Truedef eat(self):#进食if self.hungry:print(Yes,hungry)self.hungry False#进食完就不饿了else:print(No,thanks!)b Bird()
b.eat()#结果为Yes,hungry
b.eat()#结果为No,thanks!子类SongBird它新增了鸣叫功能。 SongBird是Bird的子类继承了方法eat但是一旦调用该方法会报异常SongBird没有属性hungry
class SongBird(Bird):def __init__(self):self.sound beyonddef sing(self):print(self.sound)sb SongBird()
sb.sing()#结果为beyondsb.eat()#报错AttributeError Traceback (most recent call last)
ipython-input-34-05f67b3cf162 in module()
---- 1 sb.eat()ipython-input-28-ede0d63683b8 in eat(self)3 self.hungry True4 def eat(self):
---- 5 if self.hungry:6 print(Yes,hungry)7 self.hungry FalseAttributeError: SongBird object has no attribute hungrySongBird中重写了构造函数但新的构造函数没有包含任何初始化属性hungry的代码。要消除这种错误SongBird的构造函数必须调用其超类Bird的构造函数以确保基本的初始化得以执行。
有两种方法可以解决调用未关联的超类构造函数以及使用函数super。 下面开始介绍这两种解决方法
调用未关联的超类构造函数
在SongBird类中只添加了一行其中包含代码Bird.__init__(self)。 通过将这个未关联方法的self参数设置为当前实例将使用超类的构造函数来初始化SongBird对象。这意味着将设置其属性hungry。
class Bird:def __init__(self):self.hungry Truedef eat(self):if self.hungry:print(Yes,hungry)self.hungry Falseelse:print(No,thanks!)class SongBird(Bird):def __init__(self):Bird.__init__(self)self.sound beyonddef sing(self):print(self.sound)sb SongBird()
sb.eat()#结果为Yes,hungry
sb.eat()#结果为No,thanks!对实例调用方法时方法的参数self将自动关联到实例称为关联的方法
通过类调用方法如Bird.init就没有实例与其相关联可随便设置参数self这样的方法称为未关联的。
使用函数 super
调用这个函数时将当前类和当前实例作为参数。对其返回的对象调用方法时调用的将是超类而不是当前类的方法。 在SongBird的构造函数中可不使用Bird而是使用super(SongBird, self)。 在Python 3中调用函数super时可不提供任何参数通常也应该这样做。
class Bird:def __init__(self):self.hungry Truedef eat(self):if self.hungry:print(Yes,hungry)self.hungry Falseelse:print(No,thanks!)class SongBird(Bird):def __init__(self):super().__init__(self)self.sound beyonddef sing(self):print(self.sound)sb SongBird()
sb.eat()#结果为Yes,hungry
sb.eat()#结果为No,thanks!相比于直接对超类调用未关联方法使用函数super更直观 即便有多个超类也只需调用函数super一次条件是所有超类的构造函数也使用函数super。 使用函数super比调用超类的未关联构造函数或其他方法要好得多。
函数super返回的到底是什么呢 通常你无需关心这个问题只管假定它返回你所需的超类即可。 实际上它返回的是一个super对象这个对象将负责为你执行方法解析。 当你访问它的属性时它将在所有的超类以及超类的超类等等中查找直到找到指定的属性或引发AttributeError异常。
元素访问
在Python中协议通常指的是规范行为的规则 在Python中多态仅仅基于对象的行为而不基于祖先如属于哪个类或其超类等 其他的语言可能要求对象属于特定的类或实现了特定的接口而Python通常只要求对象遵循特定的协议。
基本的序列和映射协议
序列和映射基本上是元素item的集合要实现它们的基本行为协议不可变对象需要实现2个方法而可变对象需要实现4个。
方法名称解释len(self)这个方法应返回集合包含的项数对序列来说为元素个数对映射来说为键-值对数。如果__len__返回零且没有实现覆盖这种行为的__nonzero__对象在布尔上下文中将被视为假就像空的列表、元组、字符串和字典一样getitem(self, key)这个方法应返回与指定键相关联的值。对序列来说键应该是0~n-1的整数也可以是负数其中n为序列的长度。对映射来说键可以是任何类型。setitem(self, key, value)这个方法应以与键相关联的方式存储值以便以后能够使用__getitem__来获取。当然仅当对象可变时才需要实现这个方法。delitem(self, key)这个方法在对对象的组成部分使用__del__语句时被调用应删除与key相关联的值。同样仅当对象可变且允许其项被删除时才需要实现这个方法。
对于这些方法还有一些额外的要求对于序列如果键为负整数应从末尾往前数。换而言之x[-n]应与x[len(x)-n]等效。如果键的类型不合适如对序列使用字符串键可能引发TypeError异常。对于序列如果索引的类型是正确的但不在允许的范围内应引发IndexError异常。
模块collections的文档有更复杂的接口和使用的抽象基类Sequence
实现一个算术序列其中任何两个相邻数字的差都相同。 第一个值是由构造函数的参数start默认为0指定的而相邻值之间的差是由参数step默认为1指定的。 允许用户修改某些元素这是通过将不符合规则的值保存在字典changed中实现的。 如果元素未被修改就使用公式self.start key * self.step来计算它的值。 函数check_index(key)指定的键是否是可接受的索引
键必须是非负整数才是可接受的。如果不是整数
将引发TypeError异常如果是负数将引发Index
Error异常因为这个序列的长度是无穷的def check_index(key):#索引检查if not isinstance(key,int):raise TypeErrorif key 0 :raise IndexErrorclass ArithmeticSequence:
#start -序列中的第一个值
#step -两个相邻值的差
#changed -一个字典包含用户修改后的值def __init__(self,start0,step1):#初始化这个算术序列self.start start#存储起始值self.step step#存储步长值self.changed {}#没有任何元素被修改def __getitem__(self,key):#从算术序列中获取一个元素check_index(key)try:return self.changed[key]#修改过except KeyError:# 如果没有修改过就计算元素的值return self.start key * self.stepdef __setitem__(self,key,value):#修改算术序列中的元素check_index(key)self.changed[key] value#存储修改后的值s ArithmeticSequence(1,2)
s[4]#结果为9s[4] 2
s[4]#结果为2s[5]#结果为11#由于没有实现__del__故禁止删除元素
del s[4]#报异常AttributeError Traceback (most recent call last)
ipython-input-70-9cf88d1604ce in module()
---- 1 del s[4]AttributeError: __delitem__
#这个类没有方法__len__因为其长度是无穷的。
#如果所使用索引的类型非法将引发TypeError异常
#如果索引的类型正确但不在允许的范围内即为负数将引发IndexError异常。
s[beyond]#结果为报异常TypeError Traceback (most recent call last)
ipython-input-73-f5e041f04983 in module()
---- 1 s[beyond]ipython-input-66-2648d9225498 in __getitem__(self, key)6 7 def __getitem__(self,key):
---- 8 check_index(key)9 try:return self.changed[key]10 except KeyError:ipython-input-55-f34d4d9f3aac in check_index(key)1 def check_index(key):
---- 2 if not isinstance(key,int):raise TypeError3 if key 0 :raise IndexErrorTypeError:
s[-99]#结果为报异常IndexError Traceback (most recent call last)
ipython-input-74-8aa71bcb4b31 in module()
---- 1 s[-99]ipython-input-66-2648d9225498 in __getitem__(self, key)6 7 def __getitem__(self,key):
---- 8 check_index(key)9 try:return self.changed[key]10 except KeyError:ipython-input-55-f34d4d9f3aac in check_index(key)1 def check_index(key):2 if not isinstance(key,int):raise TypeError
---- 3 if key 0 :raise IndexErrorIndexError: 从 list、dict 和 str 派生
一个带访问计数器的列表。 CounterList类深深地依赖于其超类list的行为。
CounterList没有重写的方法如append、extend、index等都可直接使用。
在两个被重写的方法中使用super来调用超类的相应方法并添加了必要的行为初始化属性counter在__init__中和更新属性counter在__getitem__中。
重写__getitem__并不能保证一定会捕捉用户的访问操作因为还有其他访问列表内容的方式如通过方法pop。
CounterList的行为在大多数方面都类似于列表但它有一个counter属性其初始值为0。
每当你访问列表元素时这个属性的值都加1。
执行加法运算cl[4] cl[2]后counter的值递增两次变成了2。
class CounterList(list):def __init__(self,*args):super().__init__(*args)self.counter 0def __getitem__(self,index):self.counter 1return super(CounterList,self).__getitem__(index)c1 CounterList(range(10))
c1#结果为[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]c1.reverse()
c1#结果为[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]del c1[3:6]
c1#结果为[9, 8, 7, 3, 2, 1, 0]c1.counter#结果为0c1[4] c1[2]#结果为9c1.counter#结果为2特性
通过存取方法定义的属性通常称为特性property。 在Python中实际上有两种创建特定的机制重点解释较新的那种——函数property它只能用于新式类。
class Rectangle:def __init__(self):self.width 0self.height 0def set_size(self,size):self.width,self.height sizedef get_size(self):return self.width,self.heightr Rectangle()
r.width 10
r.height 5
r.get_size()#结果为(10, 5)r.set_size((150,100))
r.width#结果为150get_size和set_size是假想属性size的存取方法这个属性是一个由width和height组成的元组。可随便将这个属性替换为更有趣的属性如矩形的面积或其对角线长度。
函数 property
通过调用函数property并将存取方法作为参数获取方法在前设置方法在后创建了一个特性然后将名称size关联到这个特性。
class Rectangle:def __init__(self):self.width 0self.height 0def set_size(self,size):self.width,self.height sizedef get_size(self):return self.width,self.heightsize property(get_size,set_size)r Rectangle()
r.width 10
r.height 5
r.get_size()#结果为(10, 5)r.set_size((150,100))
r.width#结果为150实际上调用函数property时还可不指定参数、指定一个参数、指定三个参数或指定四个参数。 如果没有指定任何参数创建的特性将既不可读也不可写。 如果只指定一个参数获取方法创建的特性将是只读的。 第三个参数是可选的指定用于删除属性的方法这个方法不接受任何参数。 第四个参数也是可选的指定一个文档字符串。 这些参数分别名为fget、fset、fdel和doc。 如果你要创建一个只可写且带文档字符串的特性可使用它们作为关键字参数来实现。
函数property的工作原理 property其实并不是函数而是一个类。 它的实例包含一些魔法方法而所有的魔法都是由这些方法完成的。 这些魔法方法为__get__、__set__和__delete__它们一道定义了所谓的描述符协议。 只要对象实现了这些方法中的任何一个它就是一个描述符。描述符的独特之处在于其访问方式。
读取属性具体来说是在实例中访问类中定义的属性时如果它关联的是一个实现了__get__的对象将不会返回这个对象而是调用方法__get__并将其结果返回。
静态方法和类方法
静态方法和类方法是这样创建的将它们分别包装在staticmethod和classmethod类的对象中。 静态方法的定义中没有参数self可直接通过类来调用。 类方法的定义中包含类似于self的参数通常被命名为cls。 对于类方法也可通过对象直接调用但参数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)MyClass.smeth()#结果为This is a static method
MyClass.cmeth()#结果为This is a class method of class __main__.MyClass在Python 2.4中引入了一种名为装饰器的新语法可用于像这样包装方法。实际上装饰器可用于包装任何可调用的对象并且可用于方法和函数。可指定一个或多个装饰器为此可在方法或函数前面使用运算符列出这些装饰器指定了多个装饰器时应用的顺序与列出的顺序相反。 装饰器语法也可用于特性详情请参阅有关函数property的文档。
class MyClass:staticmethoddef smeth():print(This is a static method1)classmethoddef cmeth(cls):print(This is a class method of1 ,cls)MyClass.smeth()#结果为This is a static method
MyClass.cmeth()#结果为This is a class method of class __main__.MyClassgetattr、__setattr__等方法
方法解释__getattribute__(self, name)在属性被访问时自动调用只适用于新式类__getattr__(self, name)在属性被访问而对象没有这样的属性时自动调用。__setattr__(self, name, value)试图给属性赋值时自动调用。__delattr__(self, name)试图删除属性时自动调用。即便涉及的属性不是size也将调用方法__setattr__。
因此这个方法必须考虑如下两种情形如果涉及的属性为size就执行与以前一样的操作否则就使用魔法属性__dict__。
__dict__属性是一个字典其中包含所有的实例属性。
之所以使用它而不是执行常规属性赋值是因为旨在避免再次调用__setattr__进而导致无限循环。
仅当没有找到指定的属性时才会调用方法__getattr__。
这意味着如果指定的名称不是size这个方法将引发AttributeError异常。
这在要让类能够正确地支持hasattr和getattr等内置函数时很重要。
如果指定的名称为size就使用前一个实现中的表达式。class Rectangle:def __init__(self):self.width 0self.height 0def __setattr__(self,name,value):if name size:self.width,self.height valueelse:self.__dict__[name] valuedef __getattr__(self,name):if name size:return self.width,self.heightelse:raise AttributeError()迭代器
__iter__它是迭代器协议的基础。
迭代器协议
迭代iterate意味着重复多次就像循环那样。 方法__iter__返回一个迭代器它是包含方法__next__的对象而调用这个方法时可不提供任何参数。 调用方法__next__时迭代器应返回其下一个值。 如果迭代器没有可供返回的值应引发StopIteration异常。 使用内置的便利函数next在这种情况下next(it)与it.next()等效。
斐波那契数列
class Fibs:def __init__(self):self.a 0self.b 1def __next__(self):self.a , self.b self.b , self.a self.breturn self.adef __iter__(self):return selffibs Fibs()
for f in fibs:if f 1000:print(f)#结果为1597break通过对可迭代对象调用内置函数iter可获得一个迭代器。
it iter([1,2,3])
next(it)#结果为1
next(it)#结果为2
next(it)#结果为3从迭代器创建序列
使用构造函数list显式地将迭代器转换为列表。
class TestIterator:value 0def __next__(self):self.value 1if self.value 10:raise StopIterationreturn self.valuedef __iter__(self):return selfti TestIterator()
list(ti)#结果为[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]生成器
生成器它也被称为简单生成器simple generator。 生成器是一种使用普通函数语法定义的迭代器。
创建生成器
包含yield语句的函数都被称为生成器。
生成器不是使用return返回一个值而是可以生成多个值每次一个。
每次使用yield生成一个值后函数都将冻结即在此停止执行等待被重新唤醒。 被重新唤醒后函数将从停止的地方开始继续执行。
def flatten(nested):for sublist in nested:for element in sublist:yield elementnested [[1,2],[3,4],[5]]
for num in flatten(nested):print(num)#结果为1
2
3
4
5
list(flatten(nested))#结果为[1, 2, 3, 4, 5]生成器推导也叫生成器表达式。其工作原理与列表推导相同但不是创建一个列表即不立即执行循环而是返回一个生成器让你能够逐步执行计算。不同于列表推导这里使用的是圆括号。
g ((i 2) ** 2 for i in range(2, 27))
next(g)#结果为16直接在一对既有的圆括号内如在函数调用中使用生成器推导时无需再添加一对圆括号。
sum(i ** 2 for i in range(10))#结果为285递归式生成器
上述设计的生成器只能处理两层的嵌套列表这是使用两个for循环来实现的。 如果要处理任意层嵌套的列表该如何办呢 该求助于递归了。
调用flatten时有两种可能性处理递归时都如此基线条件和递归条件。 在基线条件下要求这个函数展开单个元素如一个数。 在这种情况下for循环将引发TypeError异常因为你试图迭代一个数而这个生成器只生成一个元素。
如果要展开的是一个列表或其他任何可迭代对象遍历所有的子列表其中有些可能并不是列表并对它们调用flatten然后使用另一个for循环生成展开后的子列表中的所有元素。 如果nested是字符串或类似于字符串的对象它就属于序列因此不会引发TypeError异常但并不想对其进行迭代。
def flatten(nested):try:for sublist in nested:for element in flatten(sublist):yield elementexcept TypeError:yield nestedlist(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8]))#结果为[1, 2, 3, 4, 5, 6, 7, 8]在函数flatten中不应该对类似于字符串的对象进行迭代主要原因有两个。 首先你想将类似于字符串的对象视为原子值而不是应该展开的序列。 其次对这样的对象进行迭代会导致无穷递归因为字符串的第一个元素是一个长度为1的字符串而长度为1的字符串的第一个元素是字符串本身
要处理这种问题必须在生成器开头进行检查。 要检查对象是否类似于字符串最简单、最快捷的方式是尝试将对象与一个字符串拼接起来并检查这是否会引发TypeError异常。 如果表达式nested 引发了TypeError异常就忽略这种异常 如果没有引发TypeError异常内部try语句中的else子句将引发TypeError异常这样将在外部的excpet子句中原封不动地生成类似于字符串的对象。
def flatten(nested):try:# 不迭代类似于字符串的对象try:nested except TypeError:passelse:raise TypeErrorfor sublist in nested:for element in flatten(sublist):yield elementexcept TypeError:yield nestedlist(flatten([foo, [bar, [baz]]]))#[foo, bar, baz]这里没有执行类型检查没有检查nested是否是字符串而只是检查其行为是否类似于字符串即能否与字符串拼接。
通用生成器
生成器是包含关键字yield的函数但被调用时不会执行函数体内的代码而是返回一个迭代器。 每次请求值时都将执行生成器的代码直到遇到yield或return。yield意味着应生成一个值而return意味着生成器应停止执行即不再生成值仅当在生成器调用return时才能不提供任何参数。
换而言之生成器由两个单独的部分组成生成器的函数和生成器的迭代器。 生成器的函数是由def语句定义的其中包含yield。生成器的迭代器是这个函数返回的结果。 用不太准确的话说这两个实体通常被视为一个通称为生成器。
def simple_generator():yield isimple_generator#结果为function __main__.simple_generator
simple_generator()#结果为generator object simple_generator at 0x000001C5C9CF2B48生成器的方法
在生成器开始运行后可使用生成器和外部之间的通信渠道向它提供值。这个通信渠道包含如下两个端点。
端点名称解释外部世界外部世界可访问生成器的方法send这个方法类似于next但接受一个参数要发送的“消息”可以是任何对象。生成器在挂起的生成器内部yield可能用作表达式而不是语句。换而言之当生成器重新运行时yield返回一个值——通过send从外部世界发送的值。如果使用的是nextyield将返回None。
仅当生成器被挂起即遇到第一个yield后使用send而不是next才有意义。
def repeater(value):while True:new (yield value)if new is not None:value newr repeater(1999)
next(r)#结果为1999r.send(Hello,beyond band!)#结果为Hello,beyond band!生成器还包含另外两个方法。
方法解释throw用于在生成器中yield表达式处引发异常调用时可提供一个异常类型、一个可选值和一个traceback对象。close用于停止生成器调用时无需提供任何参数。
模拟生成器
使用普通函数重写生成器flatten
def flatten(nested): result [] try: # 不迭代类似于字符串的对象try: nested except TypeError: pass else: raise TypeError for sublist in nested: for element in flatten(sublist): result.append(element) except TypeError: result.append(nested) return result八皇后问题
生成器的回溯
回溯的简单理解 你要去参加一个很重要的会议。你不知道会议在哪里召开但前面有两扇门而会议室就在其中一扇门的后面。你选择进入左边那扇门后又看到两扇门。你再次选择进入左边那扇门但发现走错了。因此你往回走并进入右边那扇门但发现也走错了。因此你继续往回走到起点现在可以尝试进入右边那扇门。
问题
将8个皇后放在棋盘上条件是任何一个皇后都不能威胁其他皇后即任何两个皇后都不能吃掉对方。任意两个皇后都不能处于同一行、同一列或同一斜线上八个方向。 怎样才能做到这一点呢应将这些皇后放在什么地方呢
这是一个典型的回溯问题在棋盘的第一行尝试为第一个皇后选择一个位置再在第二行尝试为第二个皇后选择一个位置依次类推。在发现无法为一个皇后选择合适的位置后回溯到前一个皇后并尝试为它选择另一个位置。最后要么尝试完所有的可能性要么找到了答案。
状态表示
可简单地使用元组或列表来表示可能的解或其一部分其中每个元素表示相应行中皇后所在的位置即列。 如果state[0] 3就说明第1行的皇后放在第4列从0开始计数
完全可以使用列表而不是元组来表示状态如果序列较小且是静态的使用元组可能是不错的选择。
检测冲突
定义冲突 函数conflict接受用状态元组表示的既有皇后的位置并确定下一个皇后的位置是否会导致冲突。
参数nextX表示下一个皇后的水平位置x坐标即列而nextY为下一个皇后的垂直位置y坐标即行。
这个函数对既有的每个皇后执行简单的检查 如果下一个皇后与当前皇后的x坐标相同或在同一条对角线上将发生冲突因此返回True 如果没有发生冲突就返回False。
abs(state[i] - nextX) in (0, nextY - i)如果下一个皇后和当前皇后的水平距离为0在同一列或与它们的垂直距离相等位于一条对角线上这个表达式就为真否则为假。
def conflict(state, nextX):nextY len(state)for i in range(nextY):if abs(state[i] - nextX) in (0, nextY - i):return Truereturn False基线条件
如果只剩下最后一个皇后没有放好就遍历所有可能的位置并返回那些不会引发冲突的位置。参数num为皇后总数而参数state是一个元组包含已放好的皇后的位置。
def queens(num, state):if len(state) num-1:for pos in range(num):if not conflict(state, pos):yield pos
#假设总共有4个皇后而前3个皇后的位置分别为1、3和0故还有位置2可以放置
list(queens(4, (1, 3, 0)))#结果为[2]递归条件
理想状态递归调用返回当前行下面所有皇后的位置 对于递归调用向它提供的是由当前行上面的皇后位置组成的元组。
def queens(num8, state()):for pos in range(num):if not conflict(state, pos):if len(state) num-1:yield(pos,)else:for result in queens(num,state (pos,)):yield(pos,) resultlist(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)
(0, 6, 3, 5, 7, 1, 4, 2)
(0, 6, 4, 7, 1, 3, 5, 2)
(1, 3, 5, 7, 2, 0, 6, 4)
(1, 4, 6, 0, 2, 7, 5, 3)
(1, 4, 6, 3, 0, 7, 5, 2)
(1, 5, 0, 6, 3, 7, 2, 4)
(1, 5, 7, 2, 0, 3, 6, 4)
(1, 6, 2, 5, 7, 4, 0, 3)
(1, 6, 4, 7, 0, 3, 5, 2)
(1, 7, 5, 0, 2, 4, 6, 3)
(2, 0, 6, 4, 7, 1, 3, 5)
(2, 4, 1, 7, 0, 6, 3, 5)
(2, 4, 1, 7, 5, 3, 6, 0)
(2, 4, 6, 0, 3, 1, 7, 5)
(2, 4, 7, 3, 0, 6, 1, 5)
(2, 5, 1, 4, 7, 0, 6, 3)
(2, 5, 1, 6, 0, 3, 7, 4)
(2, 5, 1, 6, 4, 0, 7, 3)
(2, 5, 3, 0, 7, 4, 6, 1)
(2, 5, 3, 1, 7, 4, 6, 0)
(2, 5, 7, 0, 3, 6, 4, 1)
(2, 5, 7, 0, 4, 6, 1, 3)
(2, 5, 7, 1, 3, 0, 6, 4)
(2, 6, 1, 7, 4, 0, 3, 5)
(2, 6, 1, 7, 5, 3, 0, 4)
(2, 7, 3, 6, 0, 5, 1, 4)
(3, 0, 4, 7, 1, 6, 2, 5)
(3, 0, 4, 7, 5, 2, 6, 1)
(3, 1, 4, 7, 5, 0, 2, 6)
(3, 1, 6, 2, 5, 7, 0, 4)
(3, 1, 6, 2, 5, 7, 4, 0)
(3, 1, 6, 4, 0, 7, 5, 2)
(3, 1, 7, 4, 6, 0, 2, 5)
(3, 1, 7, 5, 0, 2, 4, 6)
(3, 5, 0, 4, 1, 7, 2, 6)
(3, 5, 7, 1, 6, 0, 2, 4)
(3, 5, 7, 2, 0, 6, 4, 1)
(3, 6, 0, 7, 4, 1, 5, 2)
(3, 6, 2, 7, 1, 4, 0, 5)
(3, 6, 4, 1, 5, 0, 2, 7)
(3, 6, 4, 2, 0, 5, 7, 1)
(3, 7, 0, 2, 5, 1, 6, 4)
(3, 7, 0, 4, 6, 1, 5, 2)
(3, 7, 4, 2, 0, 6, 1, 5)
(4, 0, 3, 5, 7, 1, 6, 2)
(4, 0, 7, 3, 1, 6, 2, 5)
(4, 0, 7, 5, 2, 6, 1, 3)
(4, 1, 3, 5, 7, 2, 0, 6)
(4, 1, 3, 6, 2, 7, 5, 0)
(4, 1, 5, 0, 6, 3, 7, 2)
(4, 1, 7, 0, 3, 6, 2, 5)
(4, 2, 0, 5, 7, 1, 3, 6)
(4, 2, 0, 6, 1, 7, 5, 3)
(4, 2, 7, 3, 6, 0, 5, 1)
(4, 6, 0, 2, 7, 5, 3, 1)
(4, 6, 0, 3, 1, 7, 5, 2)
(4, 6, 1, 3, 7, 0, 2, 5)
(4, 6, 1, 5, 2, 0, 3, 7)
(4, 6, 1, 5, 2, 0, 7, 3)
(4, 6, 3, 0, 2, 7, 5, 1)
(4, 7, 3, 0, 2, 5, 1, 6)
(4, 7, 3, 0, 6, 1, 5, 2)
(5, 0, 4, 1, 7, 2, 6, 3)
(5, 1, 6, 0, 2, 4, 7, 3)
(5, 1, 6, 0, 3, 7, 4, 2)
(5, 2, 0, 6, 4, 7, 1, 3)
(5, 2, 0, 7, 3, 1, 6, 4)
(5, 2, 0, 7, 4, 1, 3, 6)
(5, 2, 4, 6, 0, 3, 1, 7)
(5, 2, 4, 7, 0, 3, 1, 6)
(5, 2, 6, 1, 3, 7, 0, 4)
(5, 2, 6, 1, 7, 4, 0, 3)
(5, 2, 6, 3, 0, 7, 1, 4)
(5, 3, 0, 4, 7, 1, 6, 2)
(5, 3, 1, 7, 4, 6, 0, 2)
(5, 3, 6, 0, 2, 4, 1, 7)
(5, 3, 6, 0, 7, 1, 4, 2)
(5, 7, 1, 3, 0, 6, 4, 2)
(6, 0, 2, 7, 5, 3, 1, 4)
(6, 1, 3, 0, 7, 4, 2, 5)
(6, 1, 5, 2, 0, 3, 7, 4)
(6, 2, 0, 5, 7, 4, 1, 3)
(6, 2, 7, 1, 4, 0, 5, 3)
(6, 3, 1, 4, 7, 0, 2, 5)
(6, 3, 1, 7, 5, 0, 2, 4)
(6, 4, 2, 0, 5, 7, 1, 3)
(7, 1, 3, 0, 6, 4, 2, 5)
(7, 1, 4, 2, 0, 6, 3, 5)
(7, 2, 0, 5, 1, 4, 6, 3)
(7, 3, 0, 2, 5, 1, 6, 4)
len(list(queens(8)))#结果为92扫尾工作
有可能在其他地方都用不到它故在prettyprint中创建了一个简单的辅助函数。
def prettyprint(solution):def line(pos, lengthlen(solution)):return . * (pos) X . * (length-pos-1)for pos in solution:print(line(pos))import random
prettyprint(random.choice(list(queens(8))))#结果为. X . . . . . .
. . . . . . X .
. . X . . . . .
. . . . . X . .
. . . . . . . X
. . . . X . . .
X . . . . . . .
. . . X . . . . 小结
概念解释新式类和旧式类Python类的工作方式在不断变化。较新的Python 2版本有两种类其中旧式类正在快速退出舞台。新式类是Python 2.2引入的提供了一些额外的功能如支持旧式类正在快速退出舞台。新式类是Python 2.2引入的提供了一些额外的功能如支持或设置__metaclass__。魔法方法Python中有很多特殊方法其名称以两个下划线开头和结尾。这些方法的功能Python中有很多特殊方法其名称以两个下划线开头和结尾。这些方法的功能构造函数很多面向对象语言中都有构造函数对于你自己编写的每个类都可能需要很多面向对象语言中都有构造函数对于你自己编写的每个类都可能需要重写类可重写其超类中定义的方法以及其他任何属性为此只需实现这些方法即可。要调用被重写的版本可直接通过超类调用未关联版本旧式类也可使用函数super来调用新式类。序列和映射要创建自定义的序列或映射必须实现序列和映射协议指定的所有方法其中包括__getitem__和__setitem__等魔法方法。通过从list或UserList和dict或UserDict派生可减少很多工作量。迭代器简单地说迭代器是包含方法__next__的对象可用于迭代一组值。没有更多的值可供迭代时方法__next__应引发StopIteration异常。可迭代对象包含方法__iter__它返回一个像序列一样可用于for循环中的迭代器。通常迭代器也是可迭代的即包含返回迭代器本身的方法__iter__。生成器生成器的函数是包含关键字yield的函数它在被调用时返回一个生成器即一种特殊的迭代器。要与活动的生成器交互可使用方法send、throw和close。八皇后问题八皇后问题是个著名的计算机科学问题使用生成器可轻松地解决它。这个问题要求在棋盘上放置8个皇后并确保任何两个皇后都不能相互攻击一个皇后的八个方向都不能有另一个皇后的存在。
本章介绍的新函数
函数描述iter(obj)从可迭代对象创建一个迭代器next(it)让迭代器前进一步并返回下一个元素property(fget, fset, fdel, doc)返回一个特性所有参数都是可选的super(class, obj)返回一个超类的关联实例