Python快速入门系列(三)

面向对象编程 是最有效的软件编写方法之一。在面向对象编程中,你编写表示现实世界中的事物和情景的类,并基于这些类来创建对象。在本章中,你将学习处理文件,让程序能够快速地分析大 量的数据;你将学习错误处理,避免程序在面对意外情形时崩溃。还有如何使用Python模块unittest 中的工具来测试代码。

创建和使用类

class Cat():
    def __init__(self, name, color):
        self.name = name
        self.color = color

    def eat(self):
        print('cat ' + self.name + ' color ' + self.color + ', now eat')

    def run(self):
        print('cat ' + self.name + ' color ' + self.color + ', now run')


my_cat = Cat('Spring', 'white')
print(my_cat.name)
print(my_cat.color)
my_cat.eat()
my_cat.run()

上面创建了类 Cat ,并实例化了 my_cat,然后调用了类的方法 eat() 和 run()。输出结果:
在这里插入图片描述

类中的函数称为方法。init() 是函数的构造方法,每档创建新实例时 Python 都会自动运行它。注意构造方法名字必须是这个,是规定好的。

上面的例子中__init__(self, name, color) 有三个形参,第一个形参 self 必不可少,还必须位于其他形参的前面。其他的形参可以根据需要调整。self 是一个指向实例本身的引用,让实例能够访问类中的属性和方法。

还可以通过实例直接访问属性:my_cat.name。但在其他语言中并不建议这样做。

在Python 2.7中创建类

在Python 2.7中创建类时,需要做细微的修改——在括号内包含单词object:

class ClassName(object):

类的属性

给属性设置默认值

类中的每个属性都必须有初始值,哪怕这个值是0或空字符串。在有些情况下,如设置默认值时,在方法__init__() 内指定这种初始值是可行的;如果你对某个属性这样做了,就无需包含为它提供初始值的形参。

重新定义 Cat ,在构造方法中给属性 age 设置默认值。

class Cat():
    def __init__(self, name, color):
        self.name = name
        self.color = color
        self.age = 3

    def eat(self):
        print('cat ' + self.name + ' color ' + self.color + ', now eat')

    def run(self):
        print('cat ' + self.name + ' color ' + self.color + ', now run')

    def print_age(self):
        print('cat`s age is ' + str(self.age))

修改属性的值

可以以三种不同的方式修改属性的值:直接通过实例进行修改,通过方法进行设置。

直接修改属性的值

要修改属性的值,最简单的方式是通过实例直接访问它。

class Cat():
    def __init__(self, name, color):
        self.name = name
        self.color = color
        self.age = 3

    def eat(self):
        print('cat ' + self.name + ' color ' + self.color + ', now eat')

    def run(self):
        print('cat ' + self.name + ' color ' + self.color + ', now run')

    def print_age(self):
        print('cat`s age is ' + str(self.age))


my_cat = Cat('Spring', 'white')

my_cat.print_age()
my_cat.age = 4
my_cat.print_age()

在这里插入图片描述

上例直接通过 my_cat.age = 4 修改了 age 属性的值。

通过方法修改属性的值

再来更新代码,加入 update_age() 方法来修改 age 的属性。

class Cat():
    def __init__(self, name, color):
        self.name = name
        self.color = color
        self.age = 3

    def eat(self):
        print('cat ' + self.name + ' color ' + self.color + ', now eat')

    def run(self):
        print('cat ' + self.name + ' color ' + self.color + ', now run')

    def print_age(self):
        print('cat`s age is ' + str(self.age))

    def update_age(self, age):
        self.age = age


my_cat = Cat('Spring', 'white')
my_cat.print_age()
my_cat.update_age(10)
my_cat.print_age()

在这里插入图片描述

继承

编写类时,并非总是要从空白开始。如果你要编写的类是另一个现成类的特殊版本,可使用继继承承 。一个类继继承承 另一个类时,它将自动获得另一个类的所有属性和方法;原有的 类称为父父类类 ,而新类称为子子类类 。子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法。

class Animal():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print('Animal ' + self.name + ' run')


class Cat(Animal):
    def __init__(self, name, age):
        super().__init__(name, age)


cat = Cat('Tony', 2)
cat.run()

运行程序,输出:
在这里插入图片描述

先定义了类 Animal,又定义了 Cat 继承自 Animal。 Animal称为父类, Cat 称为子类。通过输出可以验证,子类继承了父类的方法。

在子类的构造方法中要先实现父类的构造方法:super().init(name, age)。
还可以给子类定义自己的方法,或者重写父类的方法。

class Animal():
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print('Animal ' + self.name + ' run')


class Cat(Animal):
    def __init__(self, name, age):
        super().__init__(name, age)

    def play(self):
        print('Cat ' + self.name + ' play')

    def run(self):
        print('Cat ' + self.name + ' run')


cat = Cat('Tony', 2)
cat.run()
cat.play()

我们来修改下程序,Animal 类不变,Cat 类还是继承了 Animal ,但定义了自己的方法 play() 并重写了父类方法 run() 。运行程序,得到输出:
在这里插入图片描述

Python2.7 中的继承

在Python 2.7中,继承语法稍有不同,ElectricCar类的定义类似于下面这样:

class Car(object):
	def __init__(self, make, model, year):
    	--snip--
    
class ElectricCar(Car):
	def __init__(self, make, model, year):
		super(ElectricCar, self).__init__(make, model, year)
		--snip--

函数super()需要两个实参:子类名和对象self。为帮助Python将父类和子类关联起来,这些实参必不可少。另外,在Python 2.7中使用继承时,务必在定义父类时在括号内指定object。

导入类

当一个文件过长时,可以将其中一部分代码抽离出去,然后导入到主文件中。

导入方式有多种:

导入单个类

假如 car.py 里定义了类 Car

from car import Car

从一个模块中导入多个类

假如 car.py 包含了三个类 Car , Battery 和 ElectricCar 。

只导入一个类:

from car import ElectricCar

导入多个类,中间用逗号隔开:

from car import Car, ElectricCar

导入整个模块

还可以导入整个模块,再使用句点表示法访问需要的类。这种导入方法很简单,代码也易于阅读。由于创建类实例的代码都包含模块名,因此不会与当前文件使用的任何名称发生冲突。

import car
my_car = car.Car()

导入模块中的所有类

要导入模块中的每个类,可使用下面的语法:

from module_name import *

不推荐使用这种导入方式,其原因有二。

首先,如果只要看一下文件开头的import语句,就 能清楚地知道程序使用了哪些类,将大有裨益;但这种导入方式没有明确地指出你使用了模块中 的哪些类。这种导入方式还可能引发名称方面的困惑。如果你不小心导入了一个与程序文件中其 他东西同名的类,将引发难以诊断的错误。这里之所以介绍这种导入方式,是因为虽然不推荐使 用这种方式,但你可能会在别人编写的代码中见到它。
需要从一个模块中导入很多类时,最好导入整个模块,并使用module_name.class_name语法 来访问类。这样做时,虽然文件开头并没有列出用到的所有类,但你清楚地知道在程序的哪些地 方使用了导入的模块;你还避免了导入模块中的每个类可能引发的名称冲突。

文件和异常

从文件中读取数据

要使用文本文件中的信息,首先需要将信息读取到内存中。为此,你可以一次性读取文件的全部内容,也可以以每次一行的方式逐步读取。

读取整个文件

with open('test.txt') as file_obj:
    contents = file_obj.read()
    print(contents)

open() 用于打开一个文件,参数为文件的路径。

关键字 with 在不再需要访问文件后将其关闭。有了 with 你只管打开文件,并在需要时使用它,Python自会在合适的时候自动将其关闭。

相比于原始文件,该输出唯一不同的地方是末尾多了一个空行。为何会多出这个空行呢?因为 read() 到达文件末尾时返回一个空字符串,而将这个空字符串显示出来时就是一个空行。要删除多出来的空行,可在print语句中使用 rstrip()。

文件路径可以是相对路径,也可以是绝对路径。

逐行读取

with open('test.txt') as file_obj:
    for line in file_obj:
        print(line.rstrip())

要以每次一行的方式检查文件,可对文件对象使用for循环。

写入文件

with open('test.txt', 'w') as file_obj:
    file_obj.write("I love python")

在这个示例中,调用open()时提供了两个实参,第一个实参也是要打开的文件的名称;第二个实参(‘w’)告诉Python,我们要以写入模式打开这个文件。

可选模式:

r :只读。
w : 只写。如果文件不存在则创建,如果文件存在则先清空,再写入。
a :附加模式,写入的内容追加到原始文件后面。如果文件不存在则创建。
r+ :可读可写。

如果你省略了模式实参,Python将以默认的只读模式打开文件。

异常

Python使用被称为异常 的特殊对象来管理程序执行期间发生的错误。每当发生让Python不知所措的错误时,它都会创建一个异常对象。如果你编写了处理该异常的代码,程序将继 续运行;如果你未对异常进行处理,程序将停止,并显示一个traceback,其中包含有关异常的报告。

异常是使用try-except 代码块处理的。try-except 代码块让Python执行指定的操作,同时告诉Python发生异常时怎么办。使用了try-except 代码块时,即便出现异常, 程序也将继续运行:显示你编写的友好的错误消息,而不是令用户迷惑的traceback。

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

else 代码块

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")
else:
	print("no exception")

如果 try 中的代码运行成功,没有出现异常,则执行 else 代码块中的代码。

用 json 存储数据

Python 中使用 json.dump() 和 json.load() 来存储和读取 json 文件。

import json

userInfo = {'username': 'jack', 'age': 18}

with open('test.txt', 'w') as obj:
    json.dump(userInfo, obj)


with open('test.txt') as obj:
    content = json.load(obj)
    print(content)

在这里插入图片描述

上例中用 json.dump() 把数据存入到了 test.txt 中,又用 json.load() 把数据从文件中取出并打印。

注意使用前先导入 json 模块。

单元测试

在本章中,你将学习如何使用Python模块unittest 中的工具来测试代码。先定义一个拼接名字的函数 name_function.py

def get_formatted_name(first, last):
    full_name = first + ' ' + last
    return full_name.title()

再写测试类来测试这个函数

import unittest
from name_function import get_formatted_name


class NamesTestCase(unittest.TestCase):

    def test_name_function(self):
        full_name = get_formatted_name('david', 'alex')
        self.assertEqual(full_name, 'David Alex')


unittest.main()

在这里插入图片描述

测试类要继承 unittest.TestCase ,通过 self.assertEqual 断言是否得到的结果和预期相等。

各种断言方法

Python在unittest.TestCase 类中提供了很多断言方法。前面说过,断言方法检查你认为应该满足的条件是否确实满足。如果该条件确实满足,你对程序行为的假设就得到了 确认,你就可以确信其中没有错误。如果你认为应该满足的条件实际上并不满足,Python将引发异常。

方法用途
assertEqual(a, b)核实a == b
assertNotEqual(a, b)核实a != b
assertTrue(x)核实x为True
assertFalse(x)核实x为False
assertIn(item, list)核实item在list中
assertNotIn(item, list)核实item不在list中

setUp() 方法

如果你在TestCase类中包含了方法 setUp() ,Python将先运行它,再运行各个以test_打头的方法。
通常用于做一些初始化操作。这样,在你编写 的每个测试方法中都可使用在方法setUp() 中创建的对象了。

总结

在本章中,你学习了:如何编写类;如何使用属性在类中存储信息,以及如何编写方法,以让类具备所需的行为,如何编写方法__init__() ,以便根据类创建包含所需属性的 实例。

使用文件,这让你能够保存你在程序中所做的工作,以及你让用户做的工作。你还将学习异常 。最后,如何使用模块unittest 中的工具来为函数和类编写测试。

在这里插入图片描述
欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值