Exercise44 继承和包含
当你写下class Foo(Bar)的时候,就发生了继承,这句代码的意思是“创建一个叫做Foo的类,并继承Bar” 。当你执行这句的时候,编程语言使得Foo的实例所有的行为都跟Bar的实例一样。
父子类之间有3种交互方法:
1.子类的方法隐性继承父类方法
2.子类重写父类的方法
3.对子类的操作改变父类
隐性继承
class Parent(object):
def implicit(self):
print "PARENT implicit()"
class Child(Parent):
pass
dad = Parent()
son = Child()
dad.implicit()
son.implicit()
执行结果:
父类中定义了一个函数,而在子类中虽然没有定义同样名称的函数,但在子类的实例上也可以用此函数。
重写函数
class Parent(object):
def override(self):
print "PARENT override()"
class Child(Parent):
def override(self):
print "CHILD override()"
dad = Parent()
son = Child()
dad.override()
son.override()
执行结果:
如果父类和子类对某个函数有不同的定义,当它们的实例执行这个函数时,分别执行相应的定义。
改变之前&之后
class Parent(object):
def altered(self):
print "PARENT altered()"
class Child(Parent):
def altered(self):
print "CHILD, BEFORE PARENT altered()"
super(Child, self).altered()
print "CHILD, AFTER PARENT altered()"
dad = Parent()
son = Child()
dad.altered()
son.altered()
执行结果:
这里的第九行获得了父类的之前行为,所以可以看到结果中给出了父类版本的方法执行行为前后的提示。
上述三个的结合体
class Parent(object):
def override(self):
print "PARENT override()"
def implicit(self):
print "PARENT implicit()"
def altered(self):
print "PARENT altered()"
class Child(Parent):
def override(self):
print "CHILD override()"
def altered(self):
print "CHILD, BEFORE PARENT altered()"
super(Child, self).altered()
print "CHILD, AFTER PARENT altered()"
dad = Parent()
son = Child()
dad.implicit()
son.implicit()
dad.override()
son.override()
dad.altered()
son.altered()
包含
lass Other(object):
def override(self):
print "OTHER override()"
def implicit(self):
print "OTHER implicit()"
def altered(self):
print "OTHER altered()"
class Child(object):
def __init__(self):
self.other = Other()
def implicit(self):
self.other.implicit()
def override(self):
print "CHILD override()"
def altered(self):
print "CHILD, BEFORE OTHER altered()"
self.other.altered()
print "CHILD, AFTER OTHER altered()"
son = Child()
son.implicit()
son.override()
son.altered()
执行结果:
不使用继承的方式,实现前三个例子同样的操作。
什么时候用继承,什么时候用包含
三个指导准则:
1.不惜一切代价避免多重继承,因为它太复杂太不可靠。如果你必须要使用它,那么一定要知道类的层次结构,并花时间找到每一个类是从哪里来的。
2.将代码封装为模块,这样就可以在许多不同的地方或情况使用。
3.只有当有明显相关的可重用的代码,且在一个共同概念下时,可以使用继承。
作业:阅读网页https://www.python.org/dev/peps/pep-0008/,python代码规范
Exercise 45 制作你的游戏
(写得很垃圾,不放了)
Exercise 46 项目骨架
安装python的包:参考的http://www.jb51.net/article/70331.htm,两种方法都可行。
在ex46文件夹创建projects目录,setup.py、NAME_tests.py代码:
#setup.py
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
config = {
'description': 'My Project',
'author': 'Chankillo',
'url': 'URL to get it at.',
'download_url': 'Where to download it.',
'author_email': 'chankillo7@gmail.com',
'version': '0.1',
'install_requires': ['nose'],
'packages': ['NAME'],
'scripts': [],
'name': 'projectname'
}
setup(**config)
#NAME_tests.py
from nose.tools import *
import NAME
def setup():
print "SETUP!"
def teardown():
print "TEAR DOWN!"
def test_basic():
print "I RAN!"
最后整个项目的结构如下:
Exercise 47 自动测试
为了确认游戏的功能是否正常,你需要一遍一遍地在你的游戏中输入命令。这个过程是很枯燥无味的。如果能写一小段代码用来测试你的代码岂不是更好?然后只要你对程序做了任何修改,或者添加了什么新东西,你只要“跑一下你的测试”,而这些测试能确认程序依然能正确运行。这些自动测试不会抓到所有的 bug,但可以让你无需重复输入命令运行你的代码,从而为你节约很多时间。
复制ex46的整个项目骨架skeleton文件夹到ex47,把所有的NAME改成ex47,删掉.pyc文件。
新建一个文件 ex47/game.py(用来被测试,这里的ex47文件夹指的是skeleton内部的ex47文件夹,不是最外层的ex47):
#game.py
class Room(object):
def __init__(self, name, description):
self.name = name
self.description = description
self.paths = {}
def go(self, direction):
return self.paths.get(direction, None)
def add_paths(self, paths):
self.paths.update(paths)
把测试骨架改成这个:
#ex47_tests.py
from nose.tools import *
from ex47.game import Room
def test_room():
gold = Room("GoldRoom",
"""This room has gold in it you can grab. There's a
door to the north.""")
assert_equal(gold.name, "GoldRoom")
assert_equal(gold.paths, {})
def test_room_paths():
center = Room("Center", "Test room in the center.")
north = Room("North", "Test room in the north.")
south = Room("South", "Test room in the south.")
center.add_paths({'north': north, 'south': south})
assert_equal(center.go('north'), north)
assert_equal(center.go('south'), south)
def test_map():
start = Room("Start", "You can go west and down a hole.")
west = Room("Trees", "There are trees here, you can go east.")
down = Room("Dungeon", "It's a dark down here, you can go up.")
start.add_paths({'west': west, 'down': down})
west.add_paths({'east': start})
down.add_paths({'up': start})
assert_equal(start.go('west'), west)
assert_equal(start.go('west').go('east'), start)
assert_equal(start.go('down').go('up'), start)
这个部分的两个程序改了很久才通过,其中书上的[]都需要改成{},执行结果:
Exercise 48 进阶输入
这部分书上先给了两个提示:
1.怎么断句
2.元组结构
然后让我们实现一个scanner,功能是扫描用户输入,输出的是输入句子中每个单词的词性和它本身。写出来的程序需要能通过测试lexicon_test.py。
##lexicon_test.py
from nose.tools import *
from ex48 import lexicon
def test_direction():
assert_equal(lexicon.scan("north"), [('direction','north')])
result = lexicon.scan("north south east")
assert_equal(result, [('direction', 'north'),
('direction', 'south'),
('direction', 'east')])
def test_verbs():
assert_equal(lexicon.scan("go"), [('verb', 'go')])
result = lexicon.scan("go kill eat")
assert_equal(result, [('verb', 'go'),
('verb', 'kill'),
('verb', 'eat')])
def test_stop():
assert_equal(lexicon.scan("the"), [('stop', 'the')])
result = lexicon.scan("the in of")
assert_equal(result, [('stop', 'the'),
('stop', 'in'),
('stop', 'of')])
def test_nouns():
assert_equal(lexicon.scan("bear"), [('noun', 'bear')])
result = lexicon.scan("bear princess")
assert_equal(result, [('noun', 'bear'),
('noun', 'princess')])
def test_numbers():
assert_equal(lexicon.scan("1234"), [('number', 1234)])
result = lexicon.scan("3 91234")
assert_equal(result, [('number', 3),
('number', 91234)])
def test_errors():
assert_equal(lexicon.scan("ASDFABC"), [('error', 'ASDFABC')])
result = lexicon.scan("bear IAS princess")
assert_equal(result, [('noun', 'bear'),
('error', 'IAS'),
('noun', 'princess')])
我写的程序:
#lexicon.py
direction = ['north', 'south', 'east', 'west', 'down', 'up', 'left', 'right', 'back']
verb = ['go', 'stop', 'kill', 'eat']
stop = ['the', 'in', 'of', 'from', 'at', 'it']
noun = ['door', 'bear', 'princess', 'cabinet']
def scan(stuff):
words = stuff.split()
t = list()
for word in words:
try:
word = int(word)
t.append(('number', word))
except:
if word in direction:
t.append(('direction', word))
elif word in verb:
t.append(('verb', word))
elif word in stop:
t.append(('stop', word))
elif word in noun:
t.append(('noun', word))
else:
t.append(('error', word))
return t
用命令行运行所有的测试结果都正确,但是一开始运行nosetests总是出现问题,测试卡住。睡了一觉起来就好了= =,调程序是门玄学。