前言:
继承是面向对象语言(比如 C++、python)中非常强大的功能,能大幅减少代码量,接下来,我们来了解一下 PythonPythonPython 中类的继承。(不是 Py 党的可以先去看一下:Python快速入门,特别详细)
前置知识——类
就算你不是个 Py 党,你应该也知道类是个什么。简单概括就是,他是我们定义的,用来定义对象的新类型(有点绕),与此同时,用这个类定义变量的过程叫类的实例化。接下来,为了方便讲述,我们把用这个类定义出来的变量叫这个类的实例
首先看如何定义一个类:
class A:
之后在下面就可以写对这个类的描述了(注意缩进)
接下来看如何定义一个类的属性。
因为类本质上是一个新类型,所以我们在类中的定义如果想作用到它的实例中去,我们定义的每个属性就必须针对那个实例(这或许是废话),但在类中,我们并不知道接下来要用这个类定义的实例叫什么,怎么针对呢?
其实,这就要到函数里去操作…
论 PythonPythonPython 类中的函数
与平常定义没啥区别,只不过当这个参数针对实例时,第一个参数所代表的是实例 ,这样你的函数才能对实例起作用。比如说: (我们为了叙述方便,暂且固定第一个参数名为 self)
class A:
def vall(self,a):
self.val=a
#假设前文定义了个实例d
#则 vall 可以这么用:
d.vall(1) #把实例放在句点前
#不知道句点用法的可以去看开头的链接
# 其实也还可以这么用
A.vall(d,1) #或许这样更好理解
然后, PythonPythonPython 类规定了, init() 为构造函数(两旁各有两个下划线),可以往里传参,如果该函数针对实例,第一个参数必为 self (在类外不用给它传参,类外从函数模板中第二个参数开始传参),像这样:
class A:
def __init__(self,a,c):
self.a=a
self.b="name"
self.c=c
#ta是这样用的
d=A(1,2) #d为一个类的实例,1和2为两个参数,分别被赋给 self.a 和 self.c
之后总结一下再类外怎么用类里的函数:
构造函数: 实例 = 类名(构造函数的各个参数)
普通的,针对对象的变量或函数: 实例.函数+(参数…)/变量名
注:这里的参数不包括指代实例的名字
或 类名.函数(参数…)
注:这里的参数包括实例本身。
(为什么针对实例的变量不能这样写呢?因为变量只有通过句点才能让编译器明白它针对实例)
不针对对象的变量或函数: 类名.函数+(参数…)/变量名
这里举一个类的例子:
class A:
number=0 #定义不针对实例的变量
def __init__(self,name,age): #定义构造函数
self.name=name
self.age=age
A.number+=1 #在类中也要把 'A.' 带上,否则number 会被解释器作为局部变量(这是 python 古老的特性)
def bark(self): #类中方法
print(self.name,self.age)
dog=A("Tom",11) #构造函数在一开始自动被使用,参数在括号里传
A.bark(dog) #调用方法
print(A.number) #不针对实例的变量的调用
#输出
Tom 11
1
接下来讲继承部分
继承,就是在一个类中沿用另一个类中的对象(变量、函数…等等等等,在类里的都是)。被继承的叫“父类”“基类”“超类”,继承的叫“子类”“派生类”。一个类可以有多个父类,父类和子类间必须为从属关系。
先来看一下继承的语法,非常简单:
class 子类名(父类名):
就像刚刚所说的那样,子类沿用了父类的所有对象,如果子类中没有定义构造函数,将自动应用基类中的构造函数。同样,基类中已经实现的非私有(这个概念等会再说)的方法也可以被继承。
For example,
class A: #定义父类
def __init__(self,name): #父类构造函数
self.name=name
self.num=1
class B(A):
def numm(self): #子类独有的方法
print(self.num,self.name)
C=B("name") #沿用父类的构造函数
C.numm()
#输出
1 name
子类中也可以引用父类中的变量:
class A:
def __init__(self,name):
self.name=name
self.age=11
class B(A):
def printf(self):
print(self.name,self.age) #沿用父类中的 name 和 age
c=B("ttt") #沿用父类构造函数
print(c.name) #沿用父类中的 name
#输出
ttt
上面讲的是子类没有构造函数的情况,那如果有构造函数呢?
如果直接定义 init() ,那就只是个简单的重写了,就没法通过父类的构造函数来初始化实例了。 所以,打构造函数时,要先继承,再构造,这样才能获取父类的变量和继承其构造方法。
举个例子:
class A:
def __init__(self,name,age): #父类构造函数
self.name=name
self.age=age
def tell(self):
print(self.name,self.age)
class B(A):
def __init__(self,name,age,val):
A.__init__(self,name,age) #这一步为继承
self.val=val #这是子类独有的构造部分
从上面可以看到,继承父类构造函数的语法是:
父类.init(self,…)
当然,方法不止这一种,想知道其他方法的可以去搜super关键字。
那为什么这里运用上了类似普通类中函数的调用语法呢?因为这里的实例是已经在函数模板指定了名字(即第一个参数的名字),所以可以用上类似普通类中函数调用的语法,而不用担心未定义的问题(用这种语法也可以区别父类和子类的构造函数,让解释器不迷茫)。
子类对父类方法的重写:
在子类中,如果我们想重写父类中的方法,让其在子类中有其他的意义,那该怎么办呢? 直接在子类中写就好。
class A:
def __init__(self,name): #父类构造函数
self.name=name
self.age=11
def tell(self): #等着被重写的方法
print("Hello!My name is %s."%self.name)
class B(A):
def __init__(self,name,age): #构造函数也被重写了
self.name=name
self.age=age
def tell(self): #重写 tell
print("Hello!My name is %s and I am %d years old."%(self.name,self.age))
t1=A("ttt")
t2=B("kkk",17) #两个实例
t1.tell()
t2.tell()
#输出
Hello!My name is ttt.
Hello!My name is kkk and I am 17 years old.
这里重写了构造函数和 tell 方法(当父类的构造函数对子类而言需求不大时可以不继承),所以要重写时直接写就好。
接下来给大家放一个类继承的例子:
class Person: #基类
def __init__(self,name,age,money): #基类构造函数
self.name=name
self.age=age
self.money=money
def tell(self): #基类方法
print("I am %s,and I am %d year old."%(self.name,self.age))
def give_money(self,to,money): #同 tell ,基类方法
self.money-=money
to.money+=money
class teacher(Person):
def __init__(self,name,age,money,stu=[]):
Person.__init__(self,name,age,money) #继承基类的构造函数
self.students=stu #独有构造部分
def tell(self): #重构 tell 方法
print("I am teacher %s,and I am %d year old."%(self.name,self.age))
print("I have %d students."%len(self.students))
print("They are",end=' ')
for i in self.students: #迭代学生
print(i.name+',',end='')
print()
print("And now I have ¥%d"%(self.money)) #沿用基类中的 money
def give(self,money): #子类中独有的函数
t=money//len(self.students)
self.money-=t*len(self.students)
for i in self.students:
i.money+=t
class student(Person):
def __init__(self,name,age,money,tt=None):
Person.__init__(self,name,age,money) #继承构造函数
self.teacher=tt #子类独有构造部分
def get_teacher(self,teacherr): #子类对基类的拓展(即子类独有的方法)
self.teacher=teacherr
teacherr.students.append(self)
def give_xuefei(self,to,money):
self.give_money(to,money) #这里无需再次编写,直接引用父类中的方法
p1=teacher("Wu",34,20000)
p2=student("Z",12,13000)
p3=student("H",13,15000) #三个实例
p2.get_teacher(p1)
p3.get_teacher(p1) #两个学生拜师
p2.give_xuefei(p1,5000) #p2 给学费
p1.tell() 打印 p1 的情况
#输出
I am teacher Wu,and I am 34 year old.
I have 2 students.
They are Z,H,
And now I have ¥25000
类继承例子二:AC自动机
之后来说一下之前留下来的疑问:私有方法
在类中,方法(即函数)分为三种:
1、特殊方法:被 PythonPythonPython 指定,拥有特殊作用的方法,拥有固定的作用,比如 init() 或 new() 为构造函数。重载运算符也靠此类方法(在例子二中有体现)。
2、私有方法:这些方法只能在类中被访问,在类外不能被访问,也不能被继承,在子类中用父类的私有方法会报错,定义方法为: def __函数名(各种参数) (左侧为两个下划线),调用的时候也要有这两个下划线
(但是,函数在 python 中,严格意义上也是对象,对象就能被复制,所以,你可以用一个公有方法去替代这个私有方法的功能,所以,私有方法并不是说完全不能访问,只是不能直呼其名)
(根据他人的提醒和自己的试验,私有方法之所以私有,是因为他的名字在访问时被突然改变了,而这种改变是有规律的,找到规律即可访问私有方法。这进一步地说明,python中没有什么是绝对私有的)
3、普通的公开方法,可以在类外调用,可以被继承。
类中变量的分类与此相似。(私有变量同私有方法一样,可以通过可变类型的引用改变其值,这里不多说)