Python面向对象介绍(三)+异常的使用

1. 多重继承

在Python中是支持多重继承的,也就是我们可以为一个类同时指定多个父类;

小拓展
__bases__ 可以获取当前类所有的父类,使用 类对象.__bases__即可查看自己的父类。

示例代码:

# 先定义两个简单的类
class A(object):
    def test(self):
        print('我是A的test方法')
    def speak(self):
        print('我是A的test方法')

class B(object):
    def test(self):
        print('我是B的test方法')
    def talk(self):
        print('我是B的talk方法')
        
print('A的父类: ', B.__bases__)
print('B的父类: ', B.__bases__)

运行结果:

在这里插入图片描述

从结果看,A和B的父都是object,也证实之前所讲述的,所有类都是继承自object。
现在写一个类,同时继承A和B。

class A(object):
    def test(self):
        print('我是A的test方法')
    def speak(self):
        print('我是A的test方法')


class B(object):
    def test(self):
        print('我是B的test方法')
    def talk(self):
        print('我是B的talk方法')

class C(A, B):
    pass

c_obj = C()
print('C的父类: ', C.__bases__)
# 调用父类中的方法
c_obj.speak()
c_obj.talk()
# 调用父类中同名的方法
c_obj.test()

运行结果:

在这里插入图片描述
从上述结果看:

  1. 多重继承的类的实例对象可以调用所有父类中的方法;
  2. 如果多个父类中有同名的方法,则会先在第一个父类中寻找,然后在找第二个,第三个等,前面的会把后面的覆盖。

注意点:虽然python允许使用多重继承,但一般情况下自己编写代码时应尽量避免使用多重继承。因为使用多重继承容易让代码结构变得非常复杂,导致出错的几率上升。

2. 类的多态

2.1 多态的概念

多态也是面向对象的三大特性之一
从字面上理解是有多种形态;
一个对象可以不同的形态去呈现。

2.2 多态的使用

示例代码:

# A类里面有一个封装的__name属性,提供了相应的get和set方法
# 并转化为名字为name的属性
class A:
    def __init__(self, name):
        self.__name = name

    # 将name方法修饰为只读属性
    @property
    def name(self):
        return self.__name

    # 将name方法修饰为可修改属性
    @name.setter
    def name(self, name):
        self.__name = name

# 定义的B类除了类名字不同,其余功能与A一致
class B:
    def __init__(self, name):
        self.__name = name
    # 将name方法修饰为只读属性
    @property
    def name(self):
        return self.__name

    # 将name方法修饰为可修改属性
    @name.setter
    def name(self, name):
        self.__name = name

def speak(obj):
    print(f'你好, 我是{obj.name}')

# 创建实例对象
a1 = A('John')
b1 = B('Ban')

# 调用speak函数
speak(a1)
speak(b1)

注意点:上述代码中使用@property装饰器将name修饰成只读属性后,一般不推荐再将它用setter装饰器再度修饰为可修改属性,两种操作虽然并不冲突,但逻辑上有矛盾,此处仅为演示多态,一般情况下不推荐这样的写法(可以将setter修饰的方法改为别的名字以作区分)。

运行结果:

在这里插入图片描述

从上述结果看:

  1. 对于speak()这个函数,只要对象中有name属性,它就可以作为参数传递;
  2. 这个函数并不会考虑对象的类型,只要有name属性就可以,那么该现象就是一个多态的体现

为了更明显地突出多态的特点,现在创建一个speak2函数,在其中添加功能:检测传递的对象是否是特定类的对象,如果是则执行相应的操作。

def speak2(obj):
    # 类型检查
    if isinstance(obj, A):
        print(f'你好, 我是{obj.name}')
    else:
        print('传递的对象不是A的实例对象')

# 调用speak2函数
speak2(a1)
speak2(b1)

在这里插入图片描述

从上述结果看:

  1. 在speak2()中添加了一个类型检查,只有传递的对象是A类型的对象时,才可以正常使用;
  2. 当传递其它类型的对象无法使用该函数,这个函数就违反了多态。

进一步理解多态:
众所周知,len()函数可以用来获取字符串、列表、字典的长度,但是字符串、列表它们是不同的对象;
不同对象之所以都可以通过len()获取长度,是因为多态的体现;
多态特性的突出好处是极大地增强了程序的通用性

小总结——面向对象三大特性
封装:确保对象中数据的安全;
继承:保证了对象的扩展性;
多态:保证了程序的灵活性。

3. 属性和方法

3.1 类属性

直接在类中定义的属性就是类属性;
类属性可以通过类或类的实例访问;
类属性只能通过类对象去修改,无法通过实例对象去修改。

3.2 实例属性

通过实例对象添加的属性是实例属性;
实例属性只能通过实例对象来访问和修改,类对象无法访问和修改。

示例代码:

class A(object):
    # 在类中定义类属性
    name = 'A'

    def __init__(self, name):
        # 通过init特殊方法实例属性
        self.name = name

a = A('John')
print('A的name: ',  A.name)
print('a的name: ',  a.name)
# 修改类属性
A.name = 'AA'
print('A的name: ',  A.name)
print('a的name: ',  a.name)

运行结果:

在这里插入图片描述

从上述结果看,创建实例对象时给name属性赋值了’John’,但这并不能影响类属性name,类属性只能通过类对象修改;同样的,修改类对象属性,不会影响实例对象的属性。

3.3 实例方法

实例方法是在我们类中直接定义的,以self为第一个参数开头的都是实例方法;
当通过实例对象去调用时,会自动将当前对象作为self传入;

示例代码:

class A(object):
    # 在类中定义类属性
    name = 'A'

    def __init__(self, name):
        # 通过init特殊方法实例属性
        self.name = name

    # 定义一个实例方法
    def test(self):
        print('我是test方法...')

# 通过实例对象调用实例方法
print('通过实例对象调用实例方法, ', end='')
a = A('John')
a.test()
# 通过类对象调用实例方法
print('通过类对象调用实例方法, ', end='')
A.test(a)

运行结果:

在这里插入图片描述

从上述结果看,类对象也可以调用实例方法,但是当通过类调用时,不会自动传递self,需要传递相应的对象。

3.4 类方法

类方法的第一个参数是cls 它也会自动传递,cls就是指当期的类对象;
类方法可以通过类或类的实例访问。

示例代码:

class A(object):
    # 在类中定义类属性
    name = 'A'

    def __init__(self, name):
        # 通过init特殊方法实例属性
        self.name = name

    # 定义一个实例方法
    def test(self):
        print('我是test方法...')

    @classmethod
    def test2(cls):
        print('我是一个类方法test2, ', end='')
        print(cls.name)

a = A('John')

# 通过类对象调用类方法
print('通过类对象调用类方法: ')
A.test2()
# 通过实例对象调用类方法
print('通过实例对象调用类方法: ')
a.test2()

运行结果:

在这里插入图片描述

3.5 静态方法

静态方法基本上是一个和当前类无关的一个方法,它只是保存在当前类的一个函数;
静态方法一般都是一些工具方法,和当前类无关。

class A(object):
    # 在类中定义类属性
    name = 'A'

    def __init__(self, name):
        # 通过init特殊方法实例属性
        self.name = name

    @staticmethod
    def test3():
        print('我是一个静态方法test3')

a = A('John')

# 通过类对象调用静态方法
print('通过类对象调用静态方法: ')
A.test3()
# 通过实例对象调用静态方法
print('通过实例对象调用静态方法: ')
a.test3()

运行结果:

在这里插入图片描述

从上述结果看,静态方法可以通过类对象或者实例对象调用。

4. 异常

4.1 异常的简介

在程序运行的过程中会不可避免出现一些错误,例如:使用了没有赋值的变量、索引越界、key值不存在等等。上述的这些错误我们就称之为异常。
程序在运行过程中,一旦出现异常会导致程序立即终止,即在异常后面的代码就不会执行。

示例代码:

print('程序开始')
print(a)
print('程序结束')

运行结果:

在这里插入图片描述

从上述结果看,程序打印了第一句话,执行第二句话发生异常,程序退出,最后一句没有被打印(执行)。

处理异常:

  1. 程序运行的时候出现异常目的并不是让程序直接终止;
  2. Python希望在出现异常的时候,能够编写相应的代码来进行处理。

try语句的语法:

try:
  代码块(可能出现错误的语句)
except 异常类型 as 异常名:
  代码块(出错错误之后处理的方式)
else:
  代码块(没有出错时要执行的语句)

示例代码:
首先给出没有异常的代码:

print('程序开始')
try:
    print(10/2)
except:
    print('出错了...')
else:
    print('没有出错...')
print('程序结束')

运行结果:

在这里插入图片描述

程序没有出错,打印出10/2的结果,执行了else语句,接着按正常流程打印最后一句,程序执行完毕。
再看有异常的代码:

print('程序开始')
try:
    print(10/0)
except:
    print('出错了...')
else:
    print('没有出错...')
print('程序结束')

运行结果:

在这里插入图片描述

程序捕获到异常,执行了except语句,但是没有终止整个程序,继续向下执行print语句,保证了整个程序的执行完整性。

4.2 异常的传播

示例代码:

def fn():
    print('hello fn')
    print(10 / 0)

def fn2():
    print('hello fn2')
    fn()

def fn3():
    print('hello fn3')
    fn2()

fn3()

运行结果:

在这里插入图片描述

从上结果看:

  1. 当在函数中出现异常时,如果在函数中对异常进行了处理,则异常不会在进行传播;
  2. 如果函数中没有对异常进行处理,则异常会继续向函数调用传播;
  3. 如果函数调用处处理了异常,则不再传播异常,如果没有处理则继续向调用处传播;
  4. 直到传递到全局作用域(主模块)如果依然没有处理,则程序终止,并显示异常信息。

4.3 异常对象

当程序运行过程中,出现异常以后,所有的异常信息会保存到一个专门的异常对象当中。
而异常传播时,实际上就是异常对象抛给了调用处。
常见的error有:ZeroDivisionErrorNameErrorKeyErrorIndexError

示例代码:
捕获特定的异常:

try:
    print(10/0)
except ZeroDivisionError:
    print('出现ZeroDivisionError异常...')

运行结果:

在这里插入图片描述

上述代码中,程序指定了捕获除0异常,正常执行except的语句。
如果异常不是除0,则无法捕获:

try:
    print(a)
except ZeroDivisionError:
    print('出现ZeroDivisionError异常...')

运行结果:

在这里插入图片描述

如上,程序提示NameError,并未执行except语句的代码块。
所以,except后不跟任何内容,则此时它会捕获所有种类的异常。

Exception 是所有异常类的父类,所以except后面跟的是Exception,它会捕获所有的异常。

try:
    print(10/0)
# 如下语句表示把异常对象赋值给e
except Exception as e:
    print('出现异常...', e, type(e))

运行结果:

在这里插入图片描述

从上述结果看,捕获异常后可以把异常对象赋值给变量,方便查看异常信息。

finally语句后面可以接是否出现异常都要执行的代码块。换言之,添加finally语句后,无论是否出现异常都会执行相应的代码块。

try:
    print(10/0)
# 如下语句表示把异常对象赋值给e
except Exception as e:
    print('出现异常...', e, type(e))
finally:
    print('是否出现异常该语句都会执行')

运行结果:

在这里插入图片描述

至此,就可以把完整版的try语句罗列出来:

try:
  代码块(可能出现错误的语句)
except 异常类型 as 异常名:
  代码块(出错错误之后处理的方式)
else:
  代码块(没有出错时要执行的语句)
finally:
  代码块(不管有没有出错都要执行的语句)

注意点:使用try语句时,通常后面只能接一个异常会出现,如果会遇到多个异常,则应该分开处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值