第七章 更加抽象
类和类型
类
概念:
__metaclass__=type # 确保类是新版的
class Person:
def setName(self, name):
self.name = name
def getname(self):
return self.name
def greet(self):
print("hello,my name is %s" % self.name)
foo = Person()
bar = Person()
foo.setName("luck")
bar.setName("anakin")
foo.greet()
bar.greet()
特性、函数和方法
1.为了让方法和特性变为私有(从外部无法访问),只要在他的名字前面加上双下划线即可:
class secretive:
def __inaccessible(self):
'''在方法和特性变为私有,在它名字前加上双下划綫'''
print ("Bet you cann't see me...")
def accessible(self):
print("the secret message is :")
self.__inaccessible()
s = secretive()
''' s.__inaccessible() # 报错,不能访问私有的特性
File "D:/Users/shirley/PycharmProjects/untitled/hm-class.py", line 26, in <module>
s.__inaccessible()
AttributeError: 'secretive' object has no attribute '__inaccessible'''
s.accessible()
s._secretive__inaccessible()
'''类的内部定义中,所有以双下划线开始的名字被翻译成前面加上单下划线和类名的形式'''
注意:
类的内部定义中,所有以双下划线开始的名字被翻译成前面加上单下划线和类名的形式:
实际上还是能在类外访问这些私有的方法,尽管不应该这么做
s._secretive__inaccessible()
2.类的命名空间
定义类时,所有位于class语句中的代码都在特殊命名空间中执行–类命名空间(class namespace)。
这个命名空间可由类内所有成员访问。类的定义其实就是执行代码块。
3.指定超类
class Filter:
def init(self):
self.blocked =[]
def filter(self, sequence):
return [x for x in sequence if x not in self.blocked]
class SPAMFilter(Filter): #SPAMFilter是Filter的子表
def init(self): # 重写Filter超类中init方法
self.blocked = ['SPAM']
s= SPAMFilter()
s.init()
print(s.filter(['SPAM','SPAM','eggs','SPAM','SPAM','bacon']))
检查继承
1).查看一个类是否是另一个类的子类,内建issubclass 函数
issubclass(SPAMFilter,Filter)
2).如果想知道已知类的基类(们),是一个它的特殊特性 bases:
SPAMFilter.__bases__
3).检查一个对象是否是一个类的实例,使用isinstance方法:
isinstance(s, SPAMFilter)
4 接口和内省
接口:处理多态对象时,只关心它的接口(或称为协议)即可,即公开的方法和特性
下面是关于面向对象的一些思考:
查看对象是否存在特定特性时,try/except也很有用
第九章
构造方法
一个对象被创建之后会立即调用构造方法
# 构造方法
class FooBar:
def __init__(self):
self.somevar =42
f = FooBar()
print(f.somevar)
重写是继承机制中一个重要的内容,对构造方法尤其重要。构造方法用来初始化新创建对象的状态,大多数子类不仅拥有自己的初始化代码,还要拥有超类的初始化代码。
如果一个类的构造方法被重写,就需要调用超类(你所继承的类)的构造方法,否者对象可能不会被正确地初始化。
迭代器
注意:一个实现了__iter__方法的对象时可迭代的,一个实现了next方法的对象则是迭代器。
生成器
一种用普通的函数语法定义的迭代器。任何包含yield语句的函数成为生成器
八皇后问题:
'''八皇后问题'''
def conflict(state,nextX):
'''
1.冲突检查,在定义state时,采用state来标志每个皇后的位置,其中索引用来表示横坐标,基对应的值表示纵坐标,
例如: state[0]=3,表示该皇后位于第1行的第4列上
如果下一个皇后和正在考虑的前一个皇后的水平距离为0(列相同)或者
等于垂直距离(在一条对角线上),返回True,否则False
nextX:代表下一个皇后的水平位置(X坐标或者列)
nextY:代表垂直位置(y坐标或者行)
'''
nextY = len(state)
for i in range(nextY):
# 如果下一个皇后的位置与当前的皇后位置相邻(包括上下,左右)或在同一对角线上,则说明有冲突,需要重新摆放
if abs(state[i]-nextX) in (0,nextY -i):
return True
return False
'''
def queens(num,state):
if len(state) == num-1:
for pos in range(num):
if not conflict(state, pos):
yield pos
print (queens(4,(1,3,0)) )
'''
def queens(num = 8 ,state=()):
'''
2.采用生成器的方式来产生每一个皇后的位置,并用递归来实现下一个皇后的位置。
如果只剩下一个皇后没有位置,那么遍历它的所有可能的位置,并且返回没有冲突发生的位置
:param num: 皇后的总数
:param state: 存放前面皇后的位置信息的元组
:return:
'''
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,) + result
def prettyprint(solution):
"""
3.打印出一个解决方案,为了直观表现棋盘,用X表示每个皇后的位置
:param solution:
:return:
"""
def line(pos, length=len(solution)):
return '. '*(pos)+'X '+'. '*(length-pos-1)
for pos in solution:
print(line(pos))
import random
prettyprint(random.choice(list(queens(8))))
print(list(queens(8)))
进一步理解改问题:
关于皇后冲突的判定
用自然语言很容易描述八个皇后的位置制约关系,即棋盘的每一行,每一列,每一个条正斜线,每一条反斜线,都只能有1个皇后。如果用这个方法,判断新加入的皇后位置是否与已经存在的皇后位置冲突,先求出新皇后在哪一行,列,正反两条斜线上,再依次判断是否冲突。
发现以下规律:
在同一 ‘/’ 斜线上的位置,横纵坐标之 和 相同
在同一 ‘\’ 斜线上的位置,横纵坐标之 差 相同
由此可以很轻松的判断新皇后在正反两条斜线上是否与已经存在的皇后们的坐标冲突。
判定冲突函数
1.参数1: 之前做出的选择构成的序列(列表或元组)
2.参数2: 当前选择
3.函数目的: 判断当前选择是否与之前的选择冲突
关于判断冲突函数的写法,分两类,一种是分析棋盘上所有的棋子是否冲突,而另一种则是默认之前的棋子间不会冲突,而判断当前要检验的新棋子是否与之前的每一个棋子冲突。明显第二种实现更为简洁。
下面是冲突的判定函数
def confict(state, pos):
nextY = len(state)
if pos in state: return True
'''判断斜线'''
for i in range(nextY):
if nextY-pos == i-state[i]: return True
if nextY+pos == i+state[i]: return True
return False
递归“函数”
def queens(num, state=()):
if num-1 == len(state):
for i in range(num):
if not confict(state, i):
yield (i,)
else:
for pos in range(num):
if not confict(state, pos):
for result in queens(num, state+(pos,)):
yield (pos,) + result
# 打印结果
for i in list(queens(8)):
print i
首先看参数
参数num皇后个数,第一个参数num是皇后个数,也是棋盘共num行num列。第二个参数是之前皇后的位置。 首先代码分为两部分,即if 中的内容 和 else 中的内容。
如果当前选择是最后一步, 遍历这一步能做出的所有选择,挑选出那些符合我们定义的选择。我们好像不试图从头解决这个问题,而是直接跳到最后一步来。
如果当前选择不是最后一步,那么依然遍历所有选择,当然因为不是最后一步,当前选择的可取性受制于之前的选择,并且限制了之后的选择。目光短浅的看,我们知道一些选择现在看来是合理的,但是不知道如果我们真的做此选择,会不会让后面的选择举步维艰。但是无论如何,我们只是做出当前来看可以得选择,即使后面推出当前的选择是错的,也无可厚非,因为我们正事通过这样排除所有根本不可能的,剩下的就是结果了。
先看第一个”if”代码块,代码含义显而易见,如果只剩下最后一个皇后要放置了,那么遍历棋盘上最后一行的所有位置,将符合条件的位置输出。
第一个”if”并不难,但大多初学者会被第二个”else”里的嵌套循环弄得略晕。看”else”中第一个”for”语句,没错,还是遍历当前行的所有位置,(因为第一行放皇后,第二行放皇后,第三行放皇后。。。,恰好到不是最后一行的当前行)。好吧,大多初学都被第二个”for”代码块弄晕了,yield的那个又是什么东西?
先大概说下“yield,它类似于return,但和return不同的是return 返回一个值(这个“值”可以是数值,字符串,序列等,但只是一次一个),然后函数就结束了,而yield某个值后函数不会结束,而是继续找出接下来所有符合条件的值,然后才结束。关于yield还有疑问, python基础教程书都会告诉你的。(含有yield的“函数”叫做生成器)
“yield (pos,) + result”, “pos”是当前行不与之前的皇后们冲突的位置,本质是取值0-7的整型,而在小括号里的pos再加上一个“,”表明(pos,)是只有一个元素的元组,而result也是元组,元组无法和数值类型的想家,而这就是它们可以相加的原因。
用yield而不用return的目的是我们想得到八皇后的所有位置的情况。这个”for”代码块的意思就是:如果pos这个位置可以放皇后,那么就把它放上,在此基础上,得到接下来一行行找位置把皇后放下去的所有正确结果。
八皇后问题模板与应用
接下来总结这个通用的模板:
判断冲突函数
conflict(之前的选择序列,当前的选择):
'''根据具体需要进行判定即可'''
……
递归“函数”
foo(之前的选择序列):
若当前是最后一次选择:
遍历选择的所有取值:
此次取值不与之前的选择冲突:
元组(或列表)形式“返回”该值
当前不是最后一次选择:
遍历所有取值:
不与之前的选择序列冲突:
“返回”当前选择取该值的基础上,接下来的选择结果。
适用于解数独,计算某些沙盘游戏的最佳建筑布局等。