python 继承
一、 类的继承
1. 单继承
- 1:在继承中基类的构造(__ init __()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
- 2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别在于类中调用普通函数时并不需要带上self参数
- 3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)
实例1-1:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Parent: # 定义父类
parentAttr = 100
def __init__(self):
print("调用父类构造函数")
def parentMethod(self):
print('调用父类方法')
def setAttr(self, attr):
Parent.parentAttr = attr
def getAttr(self):
print("父类属性 :", Parent.parentAttr)
class Child(Parent): # 定义子类
def __init__(self):
print("调用子类构造方法")
def childMethod(self):
print('调用子类方法')
c = Child()
# 实例化子类
c.childMethod()
# 调用子类的方法
c.parentMethod()
# 调用父类方法
c.setAttr(200)
# 再次调用父类的方法 - 设置属性值
c.getAttr()
# 再次调用父类的方法 - 获取属性值
结果:
调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200
实例1-2
生活中的单继承:
孩子只能有一个亲生父亲或者母亲
程序中的单继承:
子类只能有一个父类–》子类只能继承一个父类
# 定义一个父类
class Cat(object):
def __init__(self, name, color):
self.__name = name
self.color = color
def run(self):
print("%s----在跑" % self.__name)
# 定义一个子类
class Bosi(Cat):
def setNewName(self, newName):
self.__name = newName
def eat(self):
# print("%s---在吃饭"%self.__name)
pass
bs = Bosi("波斯", "白色")
# print(bs.__name)
print(bs.color)
bs.eat()
bs.setNewName("加菲")
bs.eat()
bs.run()
结果:
白色
波斯----在跑
说明:
虽然在子类中没有定义__init__()方法 但是父类有,在子类继承父类的时候这个方法也就被继承过来,
创建Bosi对象,默认执行继承过来的__init__方法。
总结:
子类在继承的时候,在定义类的时候,小括号中为父类的名字 父类的公有属性和公有方法会被子类继承。
实例1-3
print("父类中出现私有的情况")
# 定义一个父类
class Animal(object):
def __init__(self, name="动物", color="白色"):
self.__name = name
self.color = color
def __test(self):
print(self.__name)
print(self.color)
def test(self):
print(self.__name)
print(self.color)
# 定义一个子类
class Dog(Animal):
def Test1(self):
# print(self.__name)#不能访问父类的私有属性
print(self.color)
def dogTest2(self):
# self.__test()#不能访问父类的私有方法
self.test()
a = Animal()
# print(a.__name)#报错,不能通过对象访问私有属性
# a.__test()#报错,不能访问私有方法
a.test()
print("--------")
d = Dog(name="小花狗", color="黄色")
print(d.color)
d.Test1()
d.dogTest2()
结果:
父类中出现私有的情况
动物
白色
--------
黄色
黄色
小花狗
黄色
总结:
私有的属性,不能通过对象直接访问,可以通过方法进行访问
私有方法,不能通过对象直接访问
私有属性,方法都不会被子类继承,也不能被访问
子类继承的是父类公有是属性和方法
2. 多继承
class A: # 定义类 A
.....
class B: # 定义类 B
.....
class C(A, B): # 继承类 A 和 B
.....
使用issubclass()或者isinstance()方法来检测
- issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
- isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。
案例2-1
生活中的例子:
骡子是马和驴的杂交品种
程序中的多继承:
一个子类有多个父类,并且据有他们的特征,这被称为多继承
# 定义一个父类
class A:
def printA(self):
print("----A-----")
# 定义一个父类
class B:
def printB(self):
print("----B-----")
# 多继承
class C(A, B):
def printC(self):
print("----C-----")
# 创建C的对象
c = C()
c.printA()
c.printB()
c.printC()
结果:
----A-----
----B-----
----C-----
说明:
python中可以多继承
父类中的方法和属性,子类会继承
案例2-2:多个父类中出现相同的方法
print("多个父类中出现相同的方法")
# 如果父类A和B,有一个同名的方法,
# 那么子类在调用方法的时候,执行哪个父类中的方法
# 定义一个父类
class A:
def printA(self):
print("----A-----")
# 定义一个父类
class B:
def printA(self):
print("----B-----")
# 多继承
class C(A, B):
pass
c = C()
c.printA()
print(c)
# 案例:
class base(object):
def test(self):
print("base--test")
class D(base):
def test(self):
print("D----test")
class E(base):
def test(self):
print("E----test")
class F(D, E):
pass
f = F()
f.test()
print(F.__mro__) # 可以查看类的对象搜索方法时的先后顺序
练习:
有一个学校,人数为0,入职的老师和学生,人数增加1,
老师要显示姓名和工号,学生要显示姓名和成绩
使用继承
父类 学校
子类 老师 学生
参考代码:
print("练习题")
# 学校类
class School:
# 定义默认的人数为0
schoolNum = 0
# 初始化数据
def __init__(self, name):
self.name = name
School.schoolNum += 1
print("学校新加入的成员:%s" % self.name)
print("学校现有的学生的人数%s" % School.schoolNum)
# 自我介绍,姓名和工号,成绩
def sayHello(self):
print("我叫%s" % self.name)
# 创建老师类
class Teacher(School):
def __init__(self, name, id):
a = School(name)
self.name = name
self.id = id
def sayHello(self):
print("我叫%s,工号为%d" % (self.name, self.id))
# 学生类
class Student(School):
def __init__(self, name, result):
self.name = name
self.result = result
b = School(name)
def sayHello(self):
print("我叫%s,成绩:%d" % (self.name, self.result))
# 创建老师对象
t = Teacher("小明", 1000)
t.sayHello()
s = Student("张三", 99)
s.sayHello()
3. 方法重写—重写父类的方法和调用父类的方法
重写:
就是子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖父类中的方法
实例3-1:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class Parent: # 定义父类
def myMethod(self):
print('调用父类方法')
class Child(Parent): # 定义子类
def myMethod(self):
print('调用子类方法')
c = Child()
# 子类实例
c.myMethod()
# 子类调用重写方法
结果:
调用子类方法
实例3-2
重写父类方法
#定义一个父类
class Cat(object):
def sayHello(self):
print("hello----1")
#定义子类
class Bosi(Cat):
def sayHello(self):
print("hello -------2")
#创建子类的对象
bs = Bosi()
bs.sayHello()
结果:
hello -------2
想一想:要求打印出hello ------1,该如何实现??
实例3-3:
调用父类属性
class Cat(object):
def __init__(self, name):
self.name = name
self.color = "yello"
class Bosi(Cat):
def __init__(self, name):
self.name = name
# 方式1调用父类__init__方法,python2中/python3中
# Cat.__init__(self,name)
# 方式2:super(),可以完成对父类__init__()的调用
# super(Bosi,self).__init__(name)
# 方式3:super()直接调用__init__()函数
super().__init__(name)
# super()#一层一层向上
bosi = Bosi("xiaoming")
print(bosi.name)
print(bosi.color)
往下看:https://blog.youkuaiyun.com/dushine2008/article/details/85136742
4. 基础重载方法
5. 运算符重载
实例三:
#!/usr/bin/python
class Vector:
def __init__(self, a, b):
self.a = a
self.b = b
def __add__(self,other):
return Vector(self.a + other.a, self.b + other.b)
def __str__(self):
return 'Vector (%d, %d)' % (self.a, self.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print(v1 + v2)
结果:
Vector(7,8)
6. 类属性与方法
类的私有属性
__private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
类的方法
在类的内部,使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数
类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用 self.__private_methods
实例四:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
class JustCounter:
__secretCount = 0 # 私有变量
publicCount = 0 # 公开变量
def count(self):
self.__secretCount += 1
self.publicCount += 1
print(self.__secretCount)
counter = JustCounter()
counter.count()
counter.count()
print(counter.publicCount)
print(counter.__secretCount) # 报错,实例不能访问私有变量
结果:
1
2
2
Traceback (most recent call last):
File "test.py", line 17, in <module>
print counter.__secretCount # 报错,实例不能访问私有变量
AttributeError: JustCounter instance has no attribute '__secretCount'
Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName 访问属性,将如下代码替换以上代码的最后一行代码:
.........................
print counter._JustCounter__secretCount
执行以上代码,执行结果如下:
1
2
2
2
7. 单下划线、双下划线、头尾双下划线说明:
- __ foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __ init__() 之类的。
- _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *
- __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
8. 类属性和实例属性
概述:
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所公有。
类属性在内存中只存一个副本对于公有类属性和实例属性在类的外面可以直接被访问。
类属性:
在类中定义的属性(公有和私有)
类属性案例:
# 定义一个类:
class People(object):
name = "xiaoming" # 公有的属性
__age = 12 # 私有属性
# 创建People类的对象
p = People()
print(p.name) # ok
print(People.name) # ok
# print(p.__age) # no ok 不能在类外面通过实例对象访问类的私有属性
print(People.__age) # no ok 不能通过类对象访问私有属性
结果:
xiaoming
Traceback (most recent call last):
xiaoming
File "E:/Python37-32/ceshi.py", line 12, in <module>
print(People.__age) # no ok 不能通过类对象访问私有属性
AttributeError: type object 'People' has no attribute '__age'
实例属性
# 定义类
class People(object):
address = "北京"
def __init__(self):
self.name = "xiaoming" # 实例属性
self.age = 12 # 实例属性
p = People()
p.age = 20
print(p.age)
print(p.name)
print(p.address)
print(People.address)
# print(People.name) #类对象访问实例属性
# print(People.age) #类对象访问实例属性
结果:
20
xiaoming
北京
北京
通过实例对象去修改类属性
print("@" * 20)
class People(object):
country = "china"
def __init__(self):
self.name = "hhaa"
print(People.country)
p = People()
print(p.country)
p.country = "chinese" # 实例属性会屏蔽同名的类属性
print(p.country)
print(People.country)
del p.country
print(p.country)
结果:
@@@@@@@@@@@@@@@@@@@@
china
china
chinese
china
china
总结
如果需要在类外修改类属性,必须通过类对象去引用再进行修改
如果通过实例对象去引用,会产生一个同名的实例属性,
这个方式其实是修改实例属性,不会影响类属性,
并且之后如果需要通过实例对象去引用该同名的属性,
实例属性会强制屏蔽类属性,即引用的是类属性,除非删除该实例属性
9. 静态方法和类方法
类方法:
类对象所拥有的方法,需要使用到修饰器 @classmethod---->类方法
对于类方法,第一个参数必须是类对象,一般以cls表示作为第一个参数
(当然可以用其他的名字,但是不建议修改)
class People(object):
country = "china"
@classmethod
def getCountry(cls):
return cls.country
p = People()
print(p.country)
print(p.getCountry()) #通过实例对象进行访问
print(People.country)
print(People.getCountry()) # 通过类对象去引用
print("#" * 20)
class People(object):
country = "china"
# 类方法
@classmethod
def getCountry(cls):
return cls.country
@classmethod
def setCountry(cls, country):
cls.country = country
p = People()
print(p.getCountry()) # 可以用实例对象引用
print(People.getCountry()) # 通过类对象的的引用
p.setCountry("chinese")
print(p.getCountry())
print(People.getCountry())
结果:
china
china
china
china
####################
china
china
china
china
china
china
chinese
chinese
作用:
类方法可以对类属性进行修改
发现规律:
结果显示在用类方法对类属性进行操作修改之后,通过类对象和实例对象访问都发生了变化
静态方法
定义:
需要通过修饰器@staticmethod来进行修饰,不需要传参数
print("#" * 20)
class People(object):
country = "china"
# def getCountry(self):
# return " "
@staticmethod
def getCountry():
return People.country
print(People.getCountry())
p = People()
print(p.getCountry())
结果:
china
china
china
china
####################
china
china
chinese
chinese
总结:
从类方法和实例方法(普通方法)和静态方法
类方法第一个参数是类对象cls,那么通过cls的引用必定是类对象的属性和方法
实例方法第一个参数self,自身,那么通过self引用的可能是类属性,也可以是实例属性(具体分析)
问题?
如果出现相同名称的类属性和实例属性
实例属性优先级更高
静态方法:静态方法中不需要传递任何参数(额外的定义参数),
在静态方法中的引用是类属性,必须通过类对象来引用
10. 实例方法、类方法、静态方法区别
"""
1、实例对象可以调用实例方法、类方法、静态方法
2、类可以调用类方法、静态方法
3、只有实例方法可以访问实例属性
4、实例方法、类方法、静态方法都可以访问类属性
"""
class getMin():
class_pro = "class_pro"
def __init__(self, pro):
self.instance_pro = pro
# 实例方法
def fun(self, arr, n):
print(arr[n - 1])
print(self.instance_pro) # 实例方法调用实例属性
print(getMin.class_pro) # 实例方法访问类属性
# 类方法
@classmethod
def fun_class(cls, arr, n):
print(arr[n - 1])
# print(getMin.instance_pro) # 类方法不能调用实例属性
print(getMin.class_pro) # 类方法访问类属性
# 静态方法
@staticmethod
def fun_static(arr, n):
print(arr[n - 1])
# print(getMin.instance_pro) # 静态方法也不能使用实例属性
print(getMin.class_pro) # 静态方法访问类属性
if __name__ == '__main__':
arr = input().strip().split(" ")
n = int(input())
p = getMin("ttt")
p.fun(arr, n) # 实例对象可以调用实例方法
p.fun_class(arr, n) # 实例对象可以调用类方法
p.fun_static(arr, n) # 实例对象可以调用静态方法
# getMin.fun(arr, n) # 类对象不能调用实例方法
getMin.fun_class(arr, n) # 类对象能调用类方法
getMin.fun_static(arr, n) # 类方法不能调用静态方法
在说这些之前先说一下什么是实例变量、类变量和全局变量。
实例变量(实例属性)
对象.实例变量名 = 表达式 (“asd”,2) #创建或者修改变量
v = 对象(实例).实例变量名 #属性的取值操作
del 删除实例变量:
del 对象.属性名
预置的实例变量:
dict 属性字典 绑定的是一个字典,存储的方式是 key-value 一对键值对
class 返回创建此实例的类
type 同class
类变量(类属性)
类变量是类的属性,此属性属于类,不属于类的实例
作用:
通常用来存储此类创建的对象的共有的属性
说明:
1、类变量可以通过类直接访问
2、类变量可以通过类的实例直接访问
3、类变量可以通过此类的__class__属性间接去访问
e.g
class Dog:
color = “白色”
def init(self):
pass
d = Dog()
print(Dog.color) #通过类名直接访问
print(d.color) #通过对象去访问类变量
print(d.class.color) #通过__class__间接去访问
类方法:
@classmethod
类方法是用于描述类的行为的方法,类方法属于类,但不属于该类创建的对象
说明:
1、类方法需要用到 @classmethod
2、类方法至少有一个形参,第一个形参用于绑定类,约定写为’cls’
3、类和该类的实例都可以调用类方法
4、类方法不能访问此类创建的对象的实例属性
class Dog:
@classmethod
def fun(cls, s):
print("%s" % s)
d = Dog()
d.fun(“实例调用的”)
Dog.fun(“类调用的”)
静态方法:
静态方法是定义在类内部的函数,此函数的作用域是类的内部
说明:
1、静态方法需要使用 @staticmethod
2、静态方法与普通函数定义相同,不需要传入self实例参数和 cls 参数
3、静态方法只能凭借该类或者该类创建的对象来调用
4、静态方法不可以访问类变量和实例变量
class Dog:
“”“这是一个狗类,这里面全是小白狗”""
@staticmethod
def fun(name):
print(“这是一个静态方法”)
d = Dog()
d.color = “red”
d.fun()
Dog.fun()
关于三种方法的使用,有一些要注意的地方:
实例方法、类方法、静态方法、函数
1、实例方法能访问实例变量,类变量和全局变量
2、类方法不能访问实例变量,但是能访问类变量和全局变量
3、静态方法不能放问实例变量,类变量,但是能访问全局变量
4、函数只能访问全局变量
下面举个例子:
class A():
global sex
name="小明"
age=16
def fun(self,age,name,sex):
print("这是一个实例方法%s 姓名%s 性别%s" % (age,self.name,sex))
@classmethod
def fun1(cls,name,sex):
print("这是一个类方法%s 性别%s" % (cls.name,sex))
@staticmethod
def fun2(name,sex):
print("这是一个静态方法%s 性别%s" % (name,sex))
sex=2
a=A()
a.age=18 #修改了这个实例对应的变量的值,修改的值只是这个对象对应的值,而不会改变类变量本身的值
a.fun(a.age,123,sex) #这里age传入的是一个数值,name传的是类变量,所以无论你写什么传入的仍是类本身定义的变量,如果fun函数把self给去掉,则传入的就是数值了
a.fun(81,a.name,sex) #这里age传入的是实例变量,sex是全局变量
b=A()
b.fun1(123,sex) #这里可以传入类变量和全局变量,如果传入的是实例变量则运行不了
c=A()
c.fun2(123,sex) #这里name只能传入数值,但是能传入全局变量
结果:
这是一个实例方法18 姓名小明 性别2
这是一个实例方法81 姓名小明 性别2
这是一个类方法小明 性别2
这是一个静态方法123 性别2