# 《数据结构与算法：Python语言描述》一2.5类定义实例：学校人事管理系统中的类

### 2.5.2人事记录类的实现

class PersonTypeError(TypeError):
pass

class PersonValueError(ValueError):
pass


import datetime


class Person:
_num = 0

def __init__(self, name, sex, birthday, ident):
if not (isinstance(name, str) and
sex in ("女", "男")):
raise PersonValueError(name, sex)
try:
birth = datetime.date(*birthday)      # 生成一个日期对象
except:
raise PersonValueError("Wrong date:", birthday)
self._name = name
self._sex = sex
self._birthday = birth
self._id = ident
Person._num += 1                           # 实例计数


__init__方法的主要工作是检查参数合法性，设置对象的数据属性。这些检查非常重要，只有通过细致检查，才能保证建立起的人员对象都是合法对象，使用这些对象的程序可以依赖于它们的合法性。对人员的名字，这里只要求它是一个字符串。对于性别，要求实参是两个汉字字符串之一，用运算符in检查。

Person类的其他方法都非常简单：

def id(self): return self._id
def name(self): return self._name
def sex(self): return self._sex
def birthday(self): return self._birthday
def age(self): return (datetime.date.today().year -
self._birthday.year)

def set_name(self, name):  # 修改名字
if not isinstance(name, str):
raise PersonValueError("set_name", name)
self._name = name

def __lt__(self, another):
if not isinstance(another, Person):
raise PersonTypeError(another)
return self._id < another._id


@classmethod
def num(cls): return Person._num

def __str__(self):
return " ".join((self._id, self._name,
self._sex, str(self._birthday)))

def details(self):
return ", ".join(("编号: " + self._id,
"姓名: " + self._name,
"性别: " + self._sex,
"出生日期: " + str(self._birthday)))


p1 = Person("谢雨洁", "女", (1995, 7, 30), "1201510111")
p2 = Person("汪力强", "男", (1990, 2, 17), "1201380324")
p3 = Person("张子玉", "女", (1974, 10, 16), "0197401032")
p4 = Person("李国栋", "男", (1962, 5, 24), "0196212018")

plist2 = [p1, p2, p3, p4]
for p in plist2:
print(p)

print("\nAfter sorting:")
plist2.sort()
for p in plist2:
print(p.details())

print("People created:", Person.num(), "\n")


class Student(Person):
_id_num = 0

@classmethod
def _id_gen(cls):     # 实现学号生成规则
cls._id_num += 1
year = datetime.date.today().year
return "1{:04}{:05}".format(year, cls._id_num)

def __init__(self, name, sex, birthday, department):
Person.__init__(self, name, sex, birthday,
Student._id_gen())
self._department = department
self._enroll_date = datetime.date.today()
self._courses = {}  # 一个空字典


def set_course(self, course_name):
self._courses[course_name] = None

def set_score(self, course_name, score):
if course_name not in self._courses:
raise PersonValueError("No this course selected:",
course_name)
self._courses[course_name] = score

def scores(self): return [(cname, self._courses[cname])
for cname in self._courses]


def details(self):
return ", ".join((Person.details(self),
"入学日期: " + str(self._enroll_date),
"院系: " + self._department,
"课程记录: " + str(self.scores())))


class Staff(Person):
_id_num = 0
@classmethod
def _id_gen(cls, birthday):   # 实现职工号生成规则
cls._id_num += 1
birth_year = datetime.date(*birthday).year
return "0{:04}{:05}".format(birth_year, cls._id_num)

def __init__(self, name, sex, birthday, entry_date=None):
super().__init__(name, sex, birthday,
Staff._id_gen(birthday))
if entry_date:
try:
self._entry_date = datetime.date(*entry_date)
except:
raise PersonValueError("Wrong date:",
entry_date)
else:
self._entry_date = datetime.date.today()
self._salary = 1720       # 默认设为最低工资, 可修改
self._department = "未定"  # 需要另行设定
self._position = "未定"    # 需要另行设定

def set_salary(self, amount):
if not type(amount) is int:
raise TypeError
self._salary = amount

def set_position(self, position):
self._position = position
def set_department(self, department):
self._department = department

def details(self):
return ", ".join((super().details(),
"入职日期: " + str(self._entry_date),
"院系: " + self._department,
"职位: " + self._position,
"工资: " + str(self._salary)))


p1 = Staff("张子玉", "女", (1974, 10, 16))
p2 = Staff("李国栋", "男", (1962, 5, 24))

print(p1)
print(p2)

p1.set_department("数学")
p1.set_position("副教授")
p1.set_salary(8400)

print(p1.details())
print(p2.details())


### 2.5.3讨论

1）定义基类对象中的一类特殊个体，它们具有与基类对象类似的行为，可以作为基类对象使用（替换原理），但通常还有一些自己的特殊功能。为满足这种需要，从基类派生将能直接共享基类定义的操作，通过调用基类的初始化方法，建立派生类对象中与基类对象相同的部分。派生类对象继承基类的方法属性，可以用重新定义的方式覆盖原有方法，也可以定义新方法。在上面实例中，Student和Staff类都是公共人事类Person的派生类，其对象都是特殊的Person对象。
2）只是为了重用基类已有的功能，而将一个类定义为派生类。实际中有时也有这种需要，主要是为了代码的重用，这也是面向对象中继承机制的一类用途。

### 本章总结

Python语言里专门用于支持数据抽象的机制是类（class）及其相关结构。解释器处理完一个类定义后生成一个类对象。类对象也是一种复合对象，具有类定义里描述的所有数据属性和函数属性，约束到给定的类名，就像函数定义将生成的函数对象约束于函数名一样。类对象的最重要功能就是可以通过调用的形式生成该类的实例对象。如果类中定义了名字为__init__的初始化函数，生成实例对象时就会自动调用它；如果没定义这个函数，生成的将是一个空对象。人们通常用初始化函数为实例对象建立数据属性，设置实例对象的初始状态。这样生成的实例对象可以通过方法调用的形式，使用其所属类中定义的各个实例函数。在类里还可以定义静态方法和类方法。

Python的异常处理机制是完全基于类和对象的概念构造起来的。系统定义了一组异常类，形成了一套标准的异常类层次结构。引发一个异常就是生成相应异常类的一个对象。Python解释器的异常查找机制设法找到与异常匹配的处理器，匹配条件就是发生的异常对象属于处理器描述的异常类。在这里应该注意，派生类的对象也是基类的对象。因此，捕捉基类异常的处理器也能捕捉属于派生类的异常。如果用户需要定义自己的异常，只需要选择一个系统异常类，基于它定义一个派生类。

1. 复习下面概念：抽象数据类型，接口，实现，过程抽象和数据抽象，类型，内置类型和用户定义类型，表示，不变类型和可变类型，不变对象和可变对象，类，类定义和类对象，类对象名字空间，类的属性（数据属性和函数属性），类的实例（实例对象，对象），方法，实例方法，self参数，方法和函数，函数isinstance，初始化方法，实例的属性和属性赋值，静态方法，类方法，实例变量和私有变量，Python的特殊方法名，继承，基类，派生类，方法覆盖，替换原理，类层次结构，类object，函数issubclass，类方法查找，静态约束和动态约束，函数super，Python标准异常，异常类层次结构，Exception异常，Python异常的传播和捕捉。
2. 请列举出数据类型的三类操作，说明它们的意义和作用。
3.为什么需要初始化函数？其重要意义和作用是什么？
3. 设法说明在实际中某些类型应该定义为不变类型，另一些类型应该定义为可变类型。请各举出两个例子。
4. 请简要说明在定义一个数据类型时应该考虑哪些问题？
5. 请检查本章给出的Date抽象数据类型，讨论其中操作的语义说明里有哪些不精确之处，设法做些修改，消除描述中的歧义性。
6. 请解释并比较类定义中的三类方法：实例方法、静态方法和类方法。
7. 列出Python编程中有关类属性命名的约定。
8. 请通过实例比较类作用域与函数作用域的差异。
9. 试比较本章采用元组实现有理数和采用类实现有理数的技术，讨论这两种不同方式各自的优点和缺点。
编程练习
10. 定义一个表示时间的类Time，它提供下面操作：
a）Time(hours、minutes、seconds)创建一个时间对象；

b）t.hours()、t.minutes()、t.seconds()分别返回时间对象t的小时、分钟和秒值；
c）为Time对象定义加法和减法操作（用运算符+和-）；
d）定义时间对象的等于和小于关系运算（用运算符==和<）。

1. 请定义一个类，实现本章描述的Date抽象数据类型。
2. 扩充本章给出的有理数类，加入一些功能：
a）其他运算符的定义；

b）各种比较和判断运算符的定义；
c）转换到整数（取整）和浮点数的方法；
d）给初始化函数加入从浮点数构造有理数的功能（Python标准库浮点数类型的as_integer_ratio()函数可以用在这里）。

1. 本章2.2.2节中有理数类的实现有一个缺点：每次调__init__都会对两个参数做一遍彻底检查。但是，在有理数运算函数中构造结果时，其中一些检查并不必要，浪费了时间。请查阅Python手册中与类有关的机制，特别是名字为__new_的特殊方法等，修改有关设计，使得到的实现能完成工作但又能避免不必要的检查。
2. 请基于2.5节的工作继续扩充，为该学校人事系统定义研究生类、教师类和职员类。

|
18天前
|

23 0
|
21天前
|

【Python小技巧】通过实例说明推导式，条件表达式和Lambda函数
【Python小技巧】通过实例说明推导式，条件表达式和Lambda函数
23 2
|
21天前
|
XML 机器学习/深度学习 算法

27 1
|
21天前
|
Python
Python类(class)中self的理解
Python类(class)中self的理解
16 0
|
21天前
|

27 3
|
21天前
|
Python
Python类与对象：深入解析与应用

|
28天前
|

python类用法（四）
python类用法（四）
17 0
|
28天前
|
Python
python类用法（三）
python类用法（三）
16 0
|
28天前
|
Python
python类用法（二）
python类用法（二）
16 0
|
1月前
|

【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手？一文通！！！
【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手？一文通！！！
51 1