python魔方程序算法_《Python基础教程》第9章 魔方方法、属性和迭代器
本章会(1)讨论一些重要的魔法方法,(2)然后会讨论两个相关的主题:属性和迭代器,(3)最后介绍一个相关的示例来处理一些稍微复杂的问题。
魔法方法或者又称为特殊方法(个人挺不习惯魔法方法的翻译,叫法搞的跟拍科幻片似的,因此后面统一叫做特殊方法),是指被包含下划线的方法或者所能调用到的方法的统称,这些通常会在特殊的情况下调用,并且基本没有手动调用他们的必要。
9.1 注备工作
Python的老版本和新版本在定义类的时候会有些差别,为了统一以新式类的方式来定义类,可以在文件首行添加一行代码来来解决这个问题。个人在2.7的版本上进行学习暂时不存在这个问题,跳过不深究。
9.2 构造方法
构造方法就是类似于init的初始化方法,差别在于一个对象被构建好以后会自动调用构造方法。在Python中创建一个构造方法如下(使用_init_):
class FooBar:
def __init__(self):
self.value = 42
9.2.1 重写一般方法和特殊的构造方法
如果类B继承自类A,类B的某个方法被调用的时候会首先去B中寻找该方法,如果找不到则会到父类中去寻找:
class A():
def hello(self):
print "Hello, I am A!"
class B(A):
pass
a = A()
a.hello()
b = B()
b.hello()
$ python test.py
Hello, I am A!
Hello, I am A!
因为B类没有hello方法,所以当hello被调用时,原始信息就被打印出来。现在类B重写了该方法,再进行调用就能产生不一样的效果:
class A():
def hello(self):
print "Hello, I am A!"
class B(A):
def hello(self):
print "Hello, I am !"
a = A()
a.hello()
b = B()
b.hello()
$ python test.py
Hello, I am A!
Hello, I am B!
重写方法对于类很重要,尤其对构造方法,但重写父类的构造方法需要调用超类的构造方法,否则对象可能不能被正确的初始化。看如下的例子:
class Bird():
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Aaaah...'
self.hungry = False
else:
print 'No, thanks!'
b = Bird()
b.eat()
b.eat()
$ python test.py
Aaaah...
No, thanks!
鸟类在吃过东西以后,就不再饥饿。现在考虑添加SingBird,它添加了唱歌的行为:
class SingBird():
def __init__(self):
self.sound = 'Squawk'
def sing(self):
print self.sound
sb = SingBird()
sb.sing()
$ python test.py
Squawk
但是如果直接调用eat就会产生问题:
Traceback (most recent call last):
File "test.py", line 20, in
sb.eat()
AttributeError: SingBird instance has no attribute 'eat'
为了解决该问题,必须调用超类的构造方法保证正常的初始化,后面两节分别介绍了两种做法。
9.2.2 调用未绑定的超类构造方法
如下为调用未绑定的超类构造方法,属于历史遗留写法:
class SingBird(Bird):
def __init__(self):
Bird.__init__(self)
self.sound = 'Squawk'
def sing(self):
print self.sound
9.2.3 使用super函数
使用super函数的写法:
__metaclass__ = type#super类只能在新式类中使用
class SingBird(Bird):
def __init__(self):
super(SingBird, self).__init__()
self.sound = 'Squawk'
def sing(self):
print self.sound
9.3 成员访问
序列和映射是对象的集合,为了实现它们的基本行为,我们要实现一些魔法方法。如果集合中的对象是不可变的那么实现两个魔方方法即可,否则要实现四个魔法方法。
__len__(self) 如果返回0,该对象被当做布尔变量使用时是假
__getitem__(self, key) 如果是序列,key是0~n-1的整数,如果是映射,key可能会是任何类型。
__setitem__(self, key, value)
__delitem__(self, key)
9.3.1 基本的序列和映射规则
让我们实践一把,创建一个无穷序列:
def checkKey(key):
if not isinstance(key, (int, long)):
raise TypeError
elif key < 0:
raise IndexError
class InfinitSequence():
def __init__(self, start=0, step=1):
self.start = 0
self.step = 1
self.changed = {}
def __getitem__(self, key):
checkKey(key)
try:
return self.changed[key]
except KeyError:
return self.start + key * self.step
def __setitem__(self, key, value):
checkKey(key)
self.changed[key] = value
sequence = InfinitSequence(1, 2)
print sequence[4] #4
sequence[4] = 2
print sequence[4] #2
print sequence[5] #5
因为序列是无穷的,所以没有实现_len_方法,因此之前上面所说的实现两个或者四个魔法方法是可选的,甚至有些方法对于你实现数据结构根本不需要,因此不需要实现。
9.3.2 子类化列表、字典和字符串
要模拟基本的序列/映射要实现其他的特殊方法和普通方法,这是一项繁琐的工作,好在我们有继承。如下实现一个和内建列表行为相似的序列,可以使用子类内建类型list:
__metaclass__=type
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)
c1 = CounterList(range(10))
print c1 #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
c1.reverse()
print c1 #[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
del c1[3:6]
print c1 #[9, 8, 7, 3, 2, 1, 0]
print c1.counter #0
print c1[4] + c1[2] #9
print c1.counter #2
9.4 更多魔力
还有很多特殊方法不详述,参考Python参考手册。
9.5 属性
访问器方法是一个简单的方法,能够使用类似于getHeight何setHeight这样的名字绑定一些属性,看如下例子中的Recantagle类:
__metaclass__=type
class Recantagle(list):
def __init__(self):
self.height = 0
self.width = 0
def getSize(self):
return self.width, self.height
def setSize(self, size):
self.width, self.height = size
r = Recantagle() #(0, 0)
print r.getSize()
r.setSize((200,400))
print r.getSize() #(200, 400)
我们知道方法的实现是在getSize里面,更好的做法是隐藏访问方法。Python中有两种机制,后面主要讨论新式类中使用的property函数,以及类方法和静态方法,然后简单介绍如何使用特殊方法实现属性。
9.5.1 property属性
property函数的使用很简单,只需要将访问器函数用作参数将结果赋值给一个名为size的属性。可以用同样的方式处理width和height:
__metaclass__=type
class Recantagle(list):
def __init__(self):
self.height = 0
self.width = 0
def getSize(self):
return self.width, self.height
def setSize(self, size):
self.width, self.height = size
size = property(getSize, setSize)
r = Recantagle()
print size
property的参数可以用0-4个(此处只提及,使用查询文档),在新式类中应该使用property函数而不是访问器方法。
9.5.2 静态方法和类成员方法
静态方法是创建时被装入Staticmethod类型的特征,静态方法没有self属性,能够被类本身直接调用。
类成员方法是创建时被装入Classmethod类型的特征,类方法在定义时需要cls(类似于self的参数),类成员方法可以直接用类的对象调用。
请看下面的例子:
__metaclass__=type
class MyClass():
def smeth():
print 'This is static method'
smeth = staticmethod(smeth)
def cmeth(cls):
print 'This is class method of ', cls
cmeth = classmethod(cmeth)
MyClass.cmeth() #This is class method of
c = MyClass()
c.cmeth() #This is class method of
MyClass.smeth() #This is static method
也可以用装饰器来定义静态方法和类成员方法:
__metaclass__=type
class MyClass():
@staticmethod
def smeth():
print 'This is static method'
@classmethod
def cmeth(cls):
print 'This is class method of ', cls
MyClass.cmeth() #This is class method of
c = MyClass()
c.cmeth() #This is class method of
MyClass.smeth() #This is static method
静态方法和类成员方法虽然目前还没有大量运功过,以后可以在工厂函数中用到。
9.5.3 __getattr__、 __setattr__和它的朋友们
在不使用property属性之前,如下四个方法提供了拦截对对象特性的功能:
__getattribute__(self, name) 当name被访问时自动被调用,只能在新式类中使用
__getattr__(self, name) 当name被访问时且对象没有相应的可直接访问的特性时候调用
__setattr__(self, name, value) 当试图给特性赋值时候会自动调用
__delattr__(self, name) 但试图删除特性name时自动调用
尽管相对property使用起来稍微复杂,但是还是很强大的:
__metaclass__=type
class Recantagle(list):
def __init__(self):
self.height = 0
self.width = 0
def __getattr__(self, name):
if name == 'size':
return self.width, self.height
else:
raise AttributeError
def __setattr__(self, name, value):
if name == 'size':
self.width, self.height = value
else:
self.__dict__[name] = value
r = Recantagle()
print r.size #(0, 0)
r.size = (100, 200)
print r.size #(100, 200)
9.6 迭代器
此处只讨论一个特殊的迭代器方法__iter__,这个方法是迭代器规则的基础。
9.6.1 迭代器的规则
迭代意思是重复做一件事情能够很多次,到目前为止我们使用了for循环对序列和字典进行迭代,本章介绍的是对实现了__iter__方法的对象进行迭代。
__iter__方法会返回一个迭代器,迭代器是一个有next方法的对象,调用迭代器的next方法时会返回它的下一个值,如果没有值返回就会引发一个StopIteration异常。
使用迭代器的好处是什么? 相比较使用列表一次性获得所有的元素的值占用大量内存而言,迭代显得更加简单优雅,如下的例子“斐波拉契数列”是一个使用列表的例子,否则列表长度必须无限:
如下是一个使用迭代去的例子:
__metaclass__=type
class Fibs():
def __init__(self):
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
fibs = Fibs()
for f in fibs:
if f > 1000:
print f
break
内建函数可以从可迭代对象中直接获得迭代器,比如:
>>> it = iter([1,2,3])
>>> it.next()
1
>>> it.next()
2
9.6.2 从迭代器得到序列
可以将迭代器和可迭代对象转化序列,如下是使用list方法显式将迭代器转化为列表的例子:
class TestIterator():
value = 0
def next(self):
self.value += 1
if self.value > 10:
raise StopIteration
return self.value
def __iter__(self):
return self
ti = TestIterator()
print list(ti) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
9.7 生成器
生成器是一种普通的函数语法定义的迭代器,可以帮助我们写出很优雅的代码,当然编程时不使用生成器也是可以的。下面先简单介绍如何创建和使用生成器,然后再理解下它的机制。
9.7.1 创建生成器
我们要创建展开嵌套列表并按照顺序打印的函数,不适用生成器的时候代码大概是这样的:
nested = [[1,2], [3, 4], [5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
print element
flatten(nested)
现在要把它改写成生成器的方式:
nested = [[1,2], [3, 4], [5]]
def flatten(nested):
for sublist in nested:
for element in sublist:
yield element
for number in flatten(nested):
print number
唯一的区别在于yield语句,任何包含yield的语句被称作生成器,它和普通函数的差别在于普通函数直接返回值,而生成器每次生成一个值后函数会被冻结,等到再次被调用的时候又会被激活,被激活后函数会从之前被冻结的点开始执行。
9.7.2 递归生成器
上面的例子中嵌套有两层,因此使用两个for循环。通过求助于递归,可以遍历多层级的嵌套结构(树):
def flatten(nested):
try:
for sublist in nested:
for element in flatten(sublist):
yield element
except:
yield nested
print list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8])) # [1, 2, 3, 4, 5, 6, 7, 8]
flatten被调用时,两种情况:nested为一个元素,进而引发TypeError异常,生成器继而生成一个元素;nested为一个列表,那么就会进入到第二个for循环,遍历flatten(sublist)从而实现递归(这里面运用到了深度遍历的思想)。
有一种情况是需要特别进行处理的,如果遍历的元素中包含了字符串,我们当然不希望对字符串进行遍历。对一个元素是否是字符串的最好的检测办法是直接将元素和字符串拼接看是否发生错误。因此改写了的代码如下所示(颇有点巧妙):
def flatten(nested):
try:
try:
nested + ''
except TypeError:
pass
else:
raise TypeError
for sublist in nested:
for element in flatten(sublist):
yield element
except:
yield nested
print list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8])) # [1, 2, 3, 4, 5, 6, 7, 8]
假定nested是字符串,那么在内层try检测中一定不会导致TypeError,因此在else中手动诱发TypeError好交给外层处理;如果不是字符串且不是普通的单个元素(不然进入不到第一个try子句里),那么会触发内层的TypeError,不需要进行处理而是直接跳过好让后面的for中递归内容执行。
9.7.3 通用生成器
生成器是由两部分组成的:生成器的函数和生成器的迭代器。用于def语句定义的包含yield部分的就是生成器的函数,生成器的迭代器则是这个函数的返回部分。两个实体经常被当做一个,合作生成器。如下一个简单例子方便理解这两个概念:
def simple_generator():
yield 1
print simple_generator #
print simple_generator() #
9.7.4 生成器方法
从2.5开始Python中引入了属性能够为生成器提供值,这样生成器就能与外部进行交互:
外部作用域访问生成器的send方法,就像使用next一样,只不过前者需要一个参数
当生成器运行时候,返回通过send发送的值,如果next方法被使用则放回None
如下是一个简单例子:
def repeater(value):
while True:
new = (yield value)
if new is not None:
value = new
r = repeater(42)
print r.next()
9.7.5 模拟生成器
生成器在比较旧的版本中是不可用的,可以使用代码来模拟生成器。考虑到目前还没有使用到生成器的需求,如何模拟也暂且先跳过,有需要再回来翻看。
9.8 八皇后问题
该节会使用生成器来解决经典的编程问题。
9.8.1 生成器和回溯
略。
9.8.2 问题
简述:8*8的棋盘中,放置八个皇后使得他们相互之间不会相互威胁(即不在同一条横线竖线斜线上)。
9.8.3 状态表示
八个皇后的状态可以用一个长度为8的元组表示,比如state[0]=3表示第1行的皇后放在第4列。
9.8.4 寻找冲突
可以把冲突的判定定义为一个函数:
def conflict(state, nextX):
nextY = len(state) #2
for i in range(nextY): #2
if abs(nextX - state[i]) in (0, nextY - i):
return True
return False
nextY为下一个皇后的竖直位置(nextX和nextY的含义理解了就容易了),然后遍历所有已经放置好的皇后,如果新的位置和放置好的皇后位置在同一个竖直位置上或者斜线上,那么就返回True,否则返回False。
9.8.5 基本情况
这里先把问题简化为n皇后,假定前面n-1个皇后的位置都已经找到了,你只需要处理最后一步,你现在需要做的是根据前面n-1个皇后的位置找到第n个皇后所能放置的所有位置。如下用代码进行表示:
def queens(nums, state):
if len(state) == nums - 1:
for pos in range(nums):
if not conflict(state, pos):
yield pos
list(queens(4, [1, 3, 0])) # [2]
/Users/appledev072/Desktop/Screen Shot 2015-01-30 at 2.50.19 PM.png
9.8.6 需要递归的情况
def conflict(state, nextX):
nextY = len(state) #2
for i in range(nextY): #2
if abs(nextX - state[i]) in (0, nextY - i):
return True
return False
def queens(nums = 8, state = ()):
for pos in range(nums):
if not conflict(state, pos):
if len(state) == nums - 1:
yield (pos, )
else:
for result in queens(nums, state + (pos,)):
yield (pos,) + result
print list(queens(3)) #[]
print list(queens(4)) #[(1, 3, 0, 2), (2, 0, 3, 1)]
print len(list(queens(8))) #92
内层部分相当于我们之前所处理的情况,区别在于现在变成了元组,即该部分会获得所有最后一个皇后的位置并返回给父级调用(内层for循环中的queens),并跟后面7皇后的位置结果进行合并。以此类推。
9.8.7 打包
可以将八皇后的结果输出的稍微好理解一点,这样也有有助于发现错误:
import random
def prettyprint(solution):
def line(pos, length = len(solution)):
return '. ' * (pos) + 'X ' + '. ' * (length - pos - 1)
for pos in solution:
print line(pos)
prettyprint(random.choice(list(queens(8))))
9.9 小结
旧式类和新式类
魔法方法
构造方法
重写
序列和映射
迭代器
生成器
八皇后问题
9.9.1 本章的新函数
iter(obj)
property(fget, fset, fdel, doc)
super(class, obj)
9.9.2 接下来学习什么
测试、扩展、打包和一些项目的具体实现。
python魔方程序算法_《Python基础教程》第9章 魔方方法、属性和迭代器相关推荐
- python 打开程序 最大化_@Python 程序员,如何最大化提升编码效率?
作者 | Enoch CK 译者 | 刘畅 整理 | Jane 出品 | Python大本营 [导语]无论你是一位高级的AI工程师还是学生,你都会在工作或学习过程中需要用到 Python.自 1991 ...
- python魔方程序算法_魔方机器人(一)还原算法
魔方机器人(一)还原算法 还原算法简介 我使用的是Thislethwaite还原算法.其简介如下:一般的魔方还原的解法是通过逐块(piece or block or layer)还原来减少下一步剩余块 ...
- Python基础教程-第9章-魔法方法、特性和迭代器
在Python中,有些名称很特别,开头和结尾都是两个下划线.你在本书前面已经见过一些, 如__future__. 在这样的名称中,很大一部分都是魔法(特殊)方法的名称.如果你的对象实现了这些方法,它们 ...
- python 广告分析算法_[Python]研究广告渠道的特征数据与结果数据的相关性, 并对渠道作出评分模型...
官方描述 公司近三个月(30天)大力投放广告,累计投放的渠道有889,每个渠道的客户性质也可能不同,比如在优酷视频投广告和今日头条投放广告,效果可能会有差异.现在需要对广告效果分析实现有针对性的广告效 ...
- python算法程序_浅谈python常用程序算法
一.冒泡排序: 1.冒泡排序是将无序的数字排列成从小到大的有序组合: 过程:对相邻的两个元素进行比较,对不符合要求的数据进行交换,最后达到数据有序的过程. 规律: 1.冒泡排序的趟数时固定的:n-1 ...
- python类class定义_python基础教程之类class定义使用方法
面对对象(oop)中的对象,是一个非常重要的知识点,我们可以把它简单看做是数据以及由存取.操作这些数据的方法所组成的一个集合.我们在学习函数(function)之后,知道了如果重用代码,那为什么还要用 ...
- iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序
iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序 程序是为了实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合.本章将以编写第一个Sprite Kit程序为 ...
- 数据结构和算法_零基础入门01
数据结构和算法_零基础入门01 一.数据结构是什么? 逻辑结构.物理结构 二.算法 算法的五个基本特征 算法设计的要求 b站学习小甲鱼的数据结构与算法,自留笔记. 程序设计=数据结构+算法 一.数据结 ...
- Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍
Unity4.x 2D游戏开发基础教程第1章Unity及其组成的介绍 本书主要讲解的是,如何使用Unity开发2D游戏.但在开始讲解之前,最好先熟悉一下Unity这个工具.本章会首先介绍Unity的下 ...
- 计算机应用基础李健,计算机应用基础教程 李健苹 计算机应用基础教程 第4章...
计算机应用基础教程 李健苹 计算机应用基础教程 第4章 (58页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.90 积分 主讲人:李健苹 2011.2 ...
最新文章
- Windows10远程访问Jupyter notebook
- 『玩具装箱TOY 斜率优化DP』
- P1629邮递员送信与P1342请柬与P1821银牛派队研制联合胜利
- 技术思维解决“现金贷”危机——如何让互联网金融更加“互联网”?
- mongodb笔记(三)
- WebView三个方法区别(解决乱码问题)
- WebSocket + Spring消息推送服务的快速实现
- STM32之ADC单通道连续例程
- php会话控制区别和流程,PHP会话控制:cookie和session区别与用法深入理解_后端开发...
- Powershell About Active Directory Group Membership of a domain user
- 传智播客Lucene视频教程
- Ubuntu-mv,cp 命令
- uniapp windows 真机调试 ios iphone 踩坑指南 itunes 64位历史版本
- 基于SpringBoot开源框架的MES生产制造执行系统源码
- github上传代码全部流程
- c语言验证费马大定理,数论概论 第四章 高次幂之和与费马大定理 习题解答(宋二娃的BLOG)...
- 关于百度的若干问题和百度员工的回答
- 中国合成革行业应用发展分析与销售前景研究报告2022版
- 初级会计报名-IE浏览器攻略
- SpringBoot整合Thymeleaf模板引擎以及静态资源的访问
热门文章
- 大富豪5.3全网首发,真正的5.3正版破解授权,不是高防端
- TNS-12555报错的解决方案
- 中南大学计算机复试分数线,2019年中南大学考研复试分数线
- SpringMVC进阶
- SATA 3.3协议 Error handing机制
- ArcGIS山体坡度、坡向分析
- PKI/CA/电子签名等相关名词解释
- MySQL报Out of sort memory, consider increasing server sort buffer size的两种情况
- 酒店管理系统用什么服务器,用勤哲Excel服务器实现酒店管理系统
- 汇总并对比几个数据库存储相关的知识