"Learn Python the Hard Way"学习笔记8

2017-11-20  本文已影响0人  los_pollos

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()

执行结果:


output44a.png

父类中定义了一个函数,而在子类中虽然没有定义同样名称的函数,但在子类的实例上也可以用此函数。

重写函数
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()

执行结果:

output44b.png

如果父类和子类对某个函数有不同的定义,当它们的实例执行这个函数时,分别执行相应的定义。

改变之前&之后
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()

执行结果:

output44c.png

这里的第九行获得了父类的之前行为,所以可以看到结果中给出了父类版本的方法执行行为前后的提示。

上述三个的结合体
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()

执行结果:

output44e.png

不使用继承的方式,实现前三个例子同样的操作。

什么时候用继承,什么时候用包含

三个指导准则:

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!"

最后整个项目的结构如下:


output46.png

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)

这个部分的两个程序改了很久才通过,其中书上的[]都需要改成{},执行结果:


output47.png

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总是出现问题,测试卡住。睡了一觉起来就好了= =,调程序是门玄学。


result48.png output48.png
上一篇下一篇

猜你喜欢

热点阅读