目录
一、继承
1.1、子类重写父类同名属性和方法
class Master(object):
def __init__(self):
self.kongfu = "古法"
def make_cake(self):
print("按照%s制作一份" % self.kongfu)
class School(object):
def __init__(self):
self.kongfu = "现代"
def make_cake(self):
print("按照%s制作一份" % self.kongfu)
class Prentice(School, Master):
def __init__(self):
self.kongfu = "自创"
def make_cake(self):
print("按照%s制作一份" % self.kongfu)
cat = Prentice()
print(cat.kongfu)
cat.make_cake()
print(Prentice.__mro__)
"""
运行结果为:
自创
按照自创制作一份
(<class '__main__.Prentice'>, <class '__main__.School'>, <class '__main__.Master'>, <class 'object'>)
"""
所以,子类和父类有同名属性,则默认使用子类。子类的魔法属性__mro__决定了属性和方法的查找顺序。
1.2、子类调用父类同名属性和方法
调用父类方法格式一:父类类名.父类方法(self)
调用父类方法格式二:super(前一个类名, self).父类方法()
这些方式是在子类中的类定义时,创建函数,而函数中调用父类函数,当使用时,调用子类中定义的函数。具体写法如下:
class A(object):
def show(self):
...
class B(A):
def show(self):
...
def show_old(self):
A.show(self)
super(C, self).show()
a = A()
a.show() # 调用子类的show
a.show_old() # 调用父类的show
这种用法与C++中的方式不同:C++可以在使用是直接调用父类函数,而不同通过调用子类的函数间接调用父类的同名函数。
class A{
void func(void){
}
};
class B:public A{
void func(void){
}
};
int main(void)
{
B b;
b.func();//调用类B中的func
b.A::func();//调用类A中的func
}
1.2.1、方式一
class Master(object):
def __init__(self):
self.kongfu = "古法"
def make_cake(self):
print("Masrer 按照<%s>制作一份" % self.kongfu)
class School(object):
def __init__(self):
self.kongfu = "现代"
def make_cake(self):
print("School 按照<%s>制作一份" % self.kongfu)
class Prentice(School, Master):
def __init__(self):
self.kongfu = "自创"
def make_cake(self):
print("Prentice 按照<%s>制作一份" % self.kongfu)
def make_old_cake(self):
Master.make_cake(self) # self必须要加
cat = Prentice()
print(cat.kongfu)
cat.make_cake()
cat.make_old_cake()
从上述代码中可以发现:
- 调用父类函数(方法)时,必须要加self。
- 父类的同名属性还是保持子类的同名属性。
1.2.2、方式二
针对方式一的第二个问题,能不能将子类的同名属性也同步到父类?
通过执行父类的__init__方法,来修改self的属性值。(其实不一定是__init__方法,可以自己定义修改的属性值的函数)
class Master(object):
def __init__(self):
self.kongfu = "古法"
def make_cake(self):
print("Masrer 按照<%s>制作一份" % self.kongfu)
class School(object):
def __init__(self):
self.kongfu = "现代"
def make_cake(self):
print("School 按照<%s>制作一份" % self.kongfu)
class Prentice(School, Master):
def __init__(self):
self.kongfu = "自创"
def make_cake(self):
print("Prentice 按照<%s>制作一份" % self.kongfu)
def test(self):
print("test")
def make_old_cake(self):
Master.__init__(self)
Master.make_cake(self)
cat = Prentice()
print(cat.kongfu)
cat.make_cake()
print("-"*20)
cat.make_old_cake()
cat.make_cake()
"""
运行结果为:
自创
Prentice 按照<自创>制作一份
--------------------
Masrer 按照<古法>制作一份
Prentice 按照<古法>制作一份
"""
super函数的用法说明:
- super().父类函数(),它会默认多继承中从左到右的顺序来调用。
- super(C, self).父类函数(),它会调用多继承中C类的后一个类的函数。如果C类是子类,则调用多继承中的左边第一个。
1.3、多层继承
class Master(object):
def __init__(self):
self.kongfu = "古法"
def make_cake(self):
print("Maser 按照%s制作一份" % self.kongfu)
class School(object):
def __init__(self):
self.kongfu = "现代"
def make_cake(self):
print("School 按照%s制作一份" % self.kongfu)
class Prentice(School, Master):
def __init__(self):
self.kongfu = "自创"
def make_cake(self):
print("Prentice 按照%s制作一份" % self.kongfu)
def make_old_cake(self):
# 调用格式 : 父类类名.父类方法(self)
print("执行Master类的__init__之前self.kongfu是什么 %s" % self.kongfu)
Master.__init__(self)
print("执行Master类的__init__之后self.kongfu是什么 %s" % self.kongfu)
Master.make_cake(self)
def make_new_cake(self):
# 调用格式 : 父类类名.父类方法(self)
print("执行School类的__init__之前self.kongfu是什么 %s" % self.kongfu)
School.__init__(self) # 必须要,不然无法调用到父类的同名属性方法
print("执行School类的__init__之后self.kongfu是什么 %s" % self.kongfu)
School.make_cake(self)
class PrenticePrentice(Prentice):
pass
cat = PrenticePrentice()
cat.make_cake() # 父类实例方法(函数)
print("-"*30)
cat.make_old_cake() # 通过父类函数调用祖父类函数
print("-"*30)
cat.make_new_cake() # 通过父类函数调用祖父类函数
print("-"*30)
cat.make_cake() # 父类实例方法(函数)
二、公有与私有
2.1、公有属性和公有方法
类的属性
命名格式:由字母或_开头,总体由字母、数字和_构成
使用方法:在类外面 :对象名.属性名 在类内部 : self.属性名
类的方法
使用 def 关键字可以为类定义一个方法,与一般函数定义不同,类方法必须包含参数 self,且为第一个参数。
命名格式:由字母或_开头,总体由字母、数字和_构成
使用方法:在类外面 :对象名.方法名(实参表) 在类内部 : self.方法名(实参表) 或 类名.方法名(self, 实参表)——同名或不同均可使用
前面的示例中,类里定义的属性和方法为公用的。
2.2、私有属性和私有方法
Python中以属性命名方式来区分,如果在属性名前面加了2个下划线’__’,则表明该属性是私有属性,否则为公有属性(方法也是一样,方法名前面加了2个下划线的话表示该方法是私有的,否则为公有的)。
类的私有属性
命名格式:__private_attrs 由__开头,总体由字母、数字和_构成
使用方法: 只在类内部 : self.__private_attrs
类的私有方法
命名格式:__private_function 由__开头,总体由字母、数字和_构成
使用方法:只在类内部 : self.__private_function(实参表)
示例:
class People(object):
def __init__(self, name):
self.__name = name
def getName(self):
return self.__name
class stu(People):
def __init(self, name):
self.__stu_name = name
def show(self):
pass
# print(self.__name) 'stu' object has no attribute '_stu__name'
people = People("xiaoming")
# print(people.__name) # 'People' object has no attribute '__name'
print(people.getName())
"""
运行结果为:
xiaoming
"""
1、私有属性和方法只能在类定义时使用,在对象是无法使用私有属性和方法的。(与C++的私有成员相似)
2、子类无法使用父类的私有属性和方法,需要通过公用方法间接使用。(与C++的继承性质相似)
三、多态
定义时的类型和运行时的类型不一样,此时就成为多态。
多态的概念是应用于Java和C#这一类强类型语言中,而Python崇尚“鸭子类型 ”。
鸭子类型:
虽然我想要一只"鸭子",但是你给了我一只鸟。 但是只要这只鸟走路像鸭子,叫起来像鸭子,游泳也像鸭子,我就认为这是鸭子。
class F1(object):
def show(self):
print("F1.show")
class S1(F1):
def show(self):
print("S1.show")
class S2(F1):
def show(self):
print("S2.show")
def func(obj):
obj.show()
s1 = S1()
func(s1)
s2 = S2()
func(s2)
"""
运行结果为:
S1.show
S2.show
"""
四、属性与方法
区分概念:类属性、实例属性、类方法、实例方法、静态方法、类对象cls、实例对象self
4.1、类属性
类属性就是类对象所拥有的属性,它被所有类对象的实例对象所共有,在内存中只存在一个副本,这个和C++中类的静态成员变量有点类似。对于公有的类属性,在类外可以通过类对象和实例对象访问。
对于公有的类属性访问:
- 类名.类属性名
- 实例对象.类属性名
对于私有的类属性只能在类内被访问。
示例:
class People(object):
country = "china" # 类属性
a = People() # 实例对象a
print(a.country) # 通过实例对象访问公有类属性
print(People.country) # 通过类名访问公有类属性
"""
运行结果为:
china
china
"""
通过类对象修改类属性的值 |
通过实例对象.属性名或者self.属性名的方式修改类属性,从代码执行效果上看,确实已经修改成功,但是,事实上并没有对类属性进行修改,只是在当前的实例对象中创建了一个同名的实例属性,并且之后以实例对象引用该名称的属性的方式时(实例对象.属性值),实例属性会强制屏蔽类属性,即引用的是实例属性,除非删除该示例属性。
示例:
class A(object):
value1 = 11
a = A()
a.value1 = 2
print(a.value1)
print(A.value1)
"""
运行结果为:
2
11
"""
从示例代码中可知,这种方式并没有修改类属性的值!!!
只有通过类对象才能修改类属性的值
示例:
class A(object):
value1 = 11
a = A()
A.value1 = 2
print(a.value1)
print(A.value1)
"""
运行结果为:
2
2
"""
4.2、实例属性
定义:一个类的实例对象所拥有的属性
特点:独立的,一对一
拥有者:每个对象都拥有自己的实例属性
创建实例属性的方式:在类中通过 self.属性名 或者 实例对象.属性名 。
第二种方式,只是在当前的实例对象中仅有的,如果想在所有示例对象中有该属性,需要在__init__初始化方法中定义该示例属性。
示例:
class A(object):
def __init__(self):
self.value1 = 1
a = A()
a.value2 = 2
print(a.value1)
print(a.value2)
"""
运行结果为:
1
2
"""
4.3、类方法
类方法:是类对象所拥有的方法。
特点:@classmethod 在上面修饰 ,类方法第一个参数必须是类对象。(与实例方法相似,第一个参数必须是实例对象)。
应用:访问类属性,并且可以对类属性进行修改。
语法格式:
@classmethod
def 类方法名(cls):
执行代码
对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数(当然可以用其他名称的变量作为其第一个参数,但是大部分人都习惯以’cls’作为第一个参数的名字,就最好用’cls’了)
访问格式:
类名.类方法名(实参列表)
实例对象名.类方法名(实参列表)
示例:
class A(object):
v1 = 0
def __init__(self):
self.v2 = 1
@classmethod
def func(cls, self):
cls.v1 = 2 # 类属性
self.v2 = 3 # 实例属性
print(cls.v1)
print(self.v2)
a = A()
a.func(a) # 实例对象名.类方法名(实参列表)
A.func(a) # 类名.类方法名(实参列表)
"""
运行结果为:
2
3
2
3
"""
4.4、实例方法
类方法去掉前面的修饰器@classmethod就是实例方法,实例方法的第一个参数是实例对象self。
4.5、静态方法
静态方法:是类对象所拥有的方法
特点:@staticmethod来进行修饰,不需要多定义参数。(添加参数好像也可以)
应用:访问类属性,并且可以对类属性进行修改
语法格式:
@staticmethod
def 静态方法名():
执行代码
访问格式:
类名.静态方法名(实参列表)
实例对象名.静态方法名(实参列表)
示例:
class People(object):
country = "china" # 类属性
@staticmethod
def get_country():
return People.country
p = People()
# 通过对象来访问静态方法
print(p.get_country())
# 通过类来访问静态方法
print(People.get_country())
"""
运行结果为:
china
china
"""
或者
class A(object):
v1 = 0
def __init__(self):
self.v2 = 1
@staticmethod
def func(cls, self):
cls.v1 = 2 # 类属性
self.v2 = 3 # 实例属性
print(cls.v1)
print(self.v2)
a = A()
a.func(A, a) # 实例对象名.静态方法名(实参列表)
A.func(A, a) # 类名.静态方法名(实参列表)
"""
运行结果为:
2
3
2
3
"""
4.6、小结 ☆
- 类方法第一个参数必须是类对象cls,通过cls引用的必定是类对象的属性和方法,即类属性和类方法。
- 实例方法第一个参数必须是实例对象self,通过self引用的可能是实例属性、实例方法,也可能是类属性、类方法。
- 如果出现类属性名和实例属性名同名的话,实例对象.属性名的方式进行访问时,实例属性优先级更高。
- 如果出现类方法名和实例方法名同名的话,实例对象.方法名的方式进行访问时,类方法优先级更高。
- 继承时,父类与子类出现同名属性和方法时,按__mro__属性的查找顺序查找。
类属性 | 实例属性 | 类方法 | 实例方法 | 静态方法 | |
---|---|---|---|---|---|
定义 | 属性名 = 属性值 | self.属性名 = 属性值 | 方法前加@classmethod,方法的第一参数为类对象cls | 方法,方法的第一参数为实例对象self | 方法前加@staticmethod,无参数 |
类内访问 | cls.属性名 或 类名.属性名 | self.属性名 | cls.方法名(实参列表) 或 类名.方法名(实参列表) | self.方法名(实参列表) 或 类名.方法名(self, 实参列表) | 类名.方法名(实参列表) |
类外访问 | 实例对象名.属性名 或 类名.属性名 | 实例对象名.属性名 | 实例对象名.方法名(实参表列) 或 类名.方法名(实参表列) | 实例对象名.方法名(实参列表) | 实例对象名.方法名(实参列表) 或 类名.方法名(实参列表) |
说明:
- 调用类方法和实例方法时,实参列表不包括类对象和实例对象。
- 调用静态方法,实参列表包括类对象和实例对象。
- 类名.方法名(self, 实参列表),在类内访问,解决父子类中方法同名问题。
五、异常
当Python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常"。
5.1、捕获多个异常——try…except…
try:
file = open("1.txt") # 文件不存在
print(num) # num属性不存在
except(IOError, NameError): # 捕获IOError和NameError异常
print("---find error---")
"""
运行结果为:
---find error---
"""
注意: 当发现open出现异常时,直接跳转到except后面的语句进行执行,不会执行print(num)。
5.2、捕获异常并储存
获取一个异常的信息描述,并存储,语法格式:
try:
可能会出现异常的语句
except 异常类型 as 存储位置变量名:
pass # 表示直接略过 # 或者执行语句
获取多条异常,并存储,语法格式:
try:
可能会出现异常的语句
except (异常类型1, 异常类型2,...,异常类型N) as 存储位置变量名:
pass # 表示直接略过 # 或者执行语句
示例:
try:
file = open("1.txt")
print(num)
except(IOError, NameError) as rest:
print("---find error---")
print(rest)
print("-"*20)
# print(rest) # rest只能在except内部使用
"""
运行结果为:
---find error---
[Errno 2] No such file or directory: '1.txt'
--------------------
"""
注意: rest只能在except内部使用。
5.3、捕获所有异常
方式一 |
语法格式:
try:
可能会出现异常的语句
except:
pass # 表示直接略过 # 或者执行语句
这种捕获所有异常没有存储,不利于调试代码,一般不推荐使用。
示例:
try:
file = open("1.txt")
print(num)
except:
print("---find error---")
"""
运行结果为:
---find error---
"""
方式二 |
语法格式:
try:
可能会出现异常的语句
except Exception as 存储位置变量名:
pass # 表示直接略过 # 或者执行语句
示例:
try:
file = open("1.txt")
print(num) # 未执行
except Exception as rest:
print("---find error---")
print(rest)
"""
运行结果为:
---find error---
[Errno 2] No such file or directory: '1.txt'
"""
5.4、异常的传递
异常传递一般会出现在try嵌套使用时和多函数之间调用,如果try嵌套,那么如果里面的try没有捕获到这个异常,那么外面的try会接收到这个异常,然后进行处理,如果外边的try依然没有捕获到,那么再进行传递。。。
5.4.1、抛出自定义异常——raise语句
raise语句:可以引发一个异常。异常/错误对象必须有一个名字,自定义一个异常类,且它们应是Error或Exception类的子类。
语法格式:
class 自定义异常类名(Error或者Exception):
# 异常类的方法...
def __init(self):
...
抛出异常语句格式:
raise 异常类名(传参列表)
示例:
class shortInput(Exception):
def __init__(self, length):
self.length = length
def main():
try:
s = input("请输入:")
if len(s) < 3:
raise shortInput(2)
except shortInput as e:
print(e.length)
main()
"""
输入1, 运行结果为:
2
"""
5.4.2、异常处理中抛出异常
在使用try…except…中,如果出现异常,可以选择捕捉该异常,自己处理;也可以选择抛出该异常,让系统处理(默认处理)。
语法格式: 添加一个判断条件,若条件为真,表示捕捉异常自己处理;若为假,可以抛出异常,系统默认处理。
try:
可能出现异常的语句
except Exception as 存放位置:
if 条件:
异常该处理
else:
# 重新抛出异常
raise
六、参考资料
[1] : python多继承中子类调用父类的同名方法
[2] : Python面向对象之私有属性和方法
[3] : python的类属性、实例属性、类方法、静态方法
[4] : 类属性、实例属性;类方法,实例方法,静态方法