类和对象

类可以父业子承的。定义一个新类时,可以从某个现有的类继承,新类称为子类(subclass),而被继承的类称为基类、父类或超类(Base class、Super class)。子类可以获得父类所有的属性和方法函数。子类的方法如果和父类的方法重名,子类会覆盖掉父类,这样继承多了一个好处“多态”,后面会专门讲这个“多态”。

随着公司规模的扩大,需要对雇员分部门管理,建一个作家的类,来服务和管理员工里的作家:

def Employee(object):		# 没有父类写object
……

def Writer(Employee):		# Writer是Employee的子类
pass		# Writer为空,只继承了父类。留空会出错,所以写pass

类Writer内容虽为空,因为继承了父类,也就继承了Employee所有属性和方法:

empWriter1 = Writer('Mark', 'Twain', 8000)
# 实际上是调用了父类Employee的方法函数__init__()生成的对象

print(empWriter1.info_summary())
# 调用了父类Employee的方法函数info_summary()

输出:
Mark Twain, 8000 
This email address is being protected from spambots. You need JavaScript enabled to view it.

子类当然要有自己的属性和方法函数,比如类Writer生成的对象除了姓名和工资外还希望加上代表作(masterwork),毕竟是作家类嘛。那么Writer只靠父类的__init__就不够了,需要自己的初始化函数。在自己的__init__()内,加上对代表作的处理,其他部分可以直接用父类的初始化函数处理。

在子类的方法函数里调用父类方法函数有两种方式:一种时前面加前缀super();另一种时前面加上父类前缀,如果不止一个父类,那就只能用方式二了。

再有Writer类的方法函数info_summary()也要稍微变一下,要增加输出代表作。作家的工资增长幅度raiseAmount单独做些调整。

最后作家类再加一个类函数,使得可以从一个字符串“J.R.R-Tolkien-8000-Lord of Rings”生成新对象:可以先对字符串进行分离,名、姓、工资和代表作用’-‘连接,故可用字符串方法函数split(‘-‘)分离,分离后直接返回自己所在的类(cls)生成的对象:

class Writer(Employee):		# Writer是Employee的子类

    raiseAmount = 1.1
    # Writer定义了自己的raiseAmount,屏蔽父类同名变量

    def __init__(self, first, surname, salary, masterwork):
    # 初始化方法函数增加一个接收代表作的形参
        super().__init__(first, surname, salary)
        # 加super()调用父类的方法函数进行初始化
        self.masterwork = masterwork
        # 子类自己独有的属性

    def info_summary(self):	# 增加了输出代表作,屏蔽父类同名方法函数
        return '{}, {} \n{}\n{}'.format(self.fullname, self.salary, self.email, self.masterwork)

    @classmethod
    def new_from_string(cls, empstr):
        fname, surname, salary, masterwork = empstr.split('-')
        # 分离用‘-’连接的字符串
        return cls(fname, surname, salary, masterwork)
        # 返回Writer对象,用cls(……)手法生成的

在子类里,无论是属性还是方法函数,只要与父类同名,子类就会屏蔽掉父类同名的属性和方法函数:

empWriter1 = Writer('Mark', 'Twain', 8000, 'The adventure of Tom Sawyer')					# 生成Writer类的实例(对象)
print(empWriter1.info_summary())
输出:
Mark Twain, 8000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
The adventure of Tom Sawyer

empStr = 'J.R.R-Tolkien-8000-Lord of Rings'
empWriter2 = Writer.new_from_string(empStr)
print(empWriter2.info_summary())
输出:
J.R.R Tolkien, 8000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
Lord of Rings

print(empWriter.raiseAmount)
输出:
1.1

再做一个Employee的子类:领导类Leader。相比Employee 类,Writer类初始化对象时多了一个代表作masterwork,Leader初始化对象时要多加一个向其汇报的下属列表。要求生成对象时就算没给下属列表的实参也不能报错,这就要用到参数的缺省值了。

此外Leader对象的信息汇总函数info_summary(),要把下属的姓名汇总进去;增加下属add_sub()和删除下属remove_sub()的两个方法函数要添加:

class Leader(Employee):

    def __init__(self, fname, surname, salary, subordinates=None):
    # 接收下属列表的参数employees缺省值为空列表,被调用时没给实参就用缺省值

        Employee.__init__(self, fname, surname, salary)	(1)

        if subordinates is None:			# 为None说明没传实参
            self.subordinates = []		# 没传实参下属列表设为空
        else:
            self.subordinates = subordinates			(2)		
    def add_sub(self, subordinate):		# emp接收雇员对象

        if sub not in self.subordinates:	# 雇员不在下属列表则添加
            self.subordinate.append(sub)	# 列表的方法函数append()

    def remove_sub(self, subordinate):

	   if sub in self.subordinates:		# 雇员在下属列表里这删除
	       self.employees.remove(sub) 	# 列表的方法函数remove()

    def info_summary(self):			# 自己的汇总信息函数

        subNameList = []				# 下属名字列表初值为空
        if self.subordinates:		# 如果下属列表不为空
            for sub in self.subordinates:	# 遍历列表,下属姓名添加进列表           
                empNameList.append(emp.fullname)

        return '{}, {} \n{}\n{}'.format(self.fullname, self.salary, self.email, empNameList)

(1)    引用父类方法函数有两种方式:在方法函数前加上父类前缀Employee这种方式要求的第一个形参是self;使用前缀super()这种方式则第一形参不需要self,但这种方法不适于多个父类的情况。

(2)    本意是用形参subordinates接收一个Employee对象列表,但如果实参传过来的不是列表,而是一个Employee对象,__init__会接收,但后面方法函数的调用会全部出错(方法函数对下属的添加和删除都以subordinates是列表类型为前提)。所以这里做一个判断,如果传进来的实参类型是Employee、Writer或Leader中的一种,转换成列表予以接收。实际应用上,对传进来的数据应该做更严格的检查:

……
    def __init__(self, fname, surname, salary, subordinates=None):

        Employee.__init__(self, fname, surname, salary)
        if subordinates is None:
            self.subordinates = []
        else:
            if isinstance(subordinates, list):	
            # 传进来的实参是列表类型,实际上应进一步检查列表元素是雇员对象
                self.subordinates = subordinates
            else:
                if isinstance(subordinates, Employee) or isinstance(subordinates, Writer) or isinstance(subordinates, Leader):
		     # 用isinstance()判断是不是雇员对象,是雇员对象就转成列表
                    self.subordinates = [subordinates]
                else:
                    print('下属参数只接收雇员对象或雇员对象列表')
                    self.subordinates = []
……

empLeader1 = Leader('Julius', 'Caesar', 20000)
print(empLeader1.info_summary())
运行结果:
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
[]
empLeader1 = Leader('Julius', 'Caesar', 20000, [employee1, employee2])
print(empLeader1.info_summary())
运行结果:
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
['Harry Potter', 'Bilbo Baggins']
empLeader1 = Leader('Julius', 'Caesar', 20000, 'tom')
print(empLeader1.info_summary())
运行结果:
下属参数只接收雇员对象或雇员对象列表
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
[]

empLeader1 = Leader('Julius', 'Caesar', 20000, empWriter1)
print(empLeader1.info_summary())
运行结果:
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
['Mark Twain']

继续测试Leader的方法函数、isinstance(对象, 类)判断是不是类的实例、issubclass(子类, 父类)判断是不是父类子类的关系、hasattr(类或对象,属性名字符串)判断类或对象是不是具有某个属性:

empLeader1.add_sub(employee1)
print(empLeader1.info_summary())
输出结果:
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
['Mark Twain', 'Harry Potter']

empLeader1.remove_sub(employee1)
print(empLeader1.info_summary())
输出结果:
Julius Caesar, 20000 
This email address is being protected from spambots. You need JavaScript enabled to view it.
['Mark Twain']

print(isinstance(empWriter1, Employee))	输出结果为:True
print(isinstance(empWriter1, Writer))		输出结果为:True
print(isinstance(empWriter1, Leader))		输出结果为:False
print(issubclass(Writer, Employee))		输出结果为:True
print(issubclass(Writer, Leader))		输出结果为:False
print(hasattr(empLeader1, 'subordinates'))	输出结果为:True
print(hasattr(empWriter1, 'subordinates'))	输出结果为:False
print(hasattr(Employee, 'employeeNum'))	输出结果为:True

即将推出的Python ABC教程对PythonABC视频内容进行了梳理,修正了发现的错误、对代码做了些许优化、替换掉视频中的英文注释、替换掉国内不能访问的资源……敬请关注,谢谢