Python基础教程(三)类和对象、异常处理和模块

8.类与对象

8.1 面向对象

面向对象的三大基本特征: 封装、继承、多态。

在面向对象编程中,封装(Encapsulation)是一种将数据和操作(方法)组合在一起的机制。通过封装,我们可以隐藏数据的具体实现细节,只暴露出一些对外的接口,外部程序只能通过这些接口来访问和操作数据。

封装的主要目的是保护数据的安全性和完整性,防止外部程序直接修改数据,导致不可预料的错误。同时,封装也提供了简化操作的便利性,对外部程序来说,只需要关注公开的接口,而不需要关心具体的实现方式。

在封装中,通常会将数据声明为私有的(private),并提供一些公开的方法(public methods)来获取和修改数据。这些公开的方法可以对数据进行一些校验和处理,保证数据的有效性和一致性。

封装也有助于实现代码的可维护性和可扩展性,通过封装,我们可以将修改数据和操作的逻辑集中在一个地方,方便后续的修改和扩展。

总结来说,封装是一种将数据和操作组合在一起的机制,通过封装可以隐藏数据的实现细节,保护数据的安全性,提供简化操作的接口,并提高代码的可维护性和可扩展性。

继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类、基类或超类)继承属性和方法。

子类可以继承父类的属性和方法,并且还可以添加自己的特定属性和方法。这样可以实现代码的重用和扩展。

多态是面向对象编程中的一个概念,指的是同一个方法在不同对象上的不同表现形式。简单说,就是通过使用父类的引用指向子类的对象,实现对不同子类对象调用同一个方法时产生不同的结果。

8.2 类与对象

8.2.1 类与对象的关系

类与对象的关系是一种包含与被包含的关系。

类是对一类具有相同属性和行为的对象的抽象描述,它定义了对象的属性和方法。它可以看作是一个模板或蓝图,描述了对象应该具有的特征和行为。

对象是类的实例化,是具体存在的实体,它具有类定义的属性和方法。

类和对象的关系可以用类比现实生活中的一对模具和铸件的关系。类就像是一个模具,定义了对象的特征和行为;对象就像是具体的铸件,是根据模具创建出来的实体。

类与对象的关系还可以用包含与被包含的关系来理解。一个类可以包含多个对象,这些对象都是类的实例化。而一个对象则包含类定义的属性和方法。

总结来说,类是对一类对象的抽象描述,对象是类的实例化,它们之间是一种包含与被包含的关系。

8.2.2 类的定义与访问

class 类名:
        属性名=属性值
        def 方法名(self):
                方法体

>>> class Car:
    wheels=4
    def drive(self):
        print('开车方法')
    def stop(self):
        print('停车方法')
        
>>> 

在Python中,self是一个特殊的参数,它代表当前对象实例本身。通过使用self,我们可以在类的方法中引用和操作对象的属性和方法。

在类的方法中,通常第一个参数会被命名为self,用于接收当前对象的引用。当我们调用对象的方法时,Python会自动将对象本身作为第一个参数传递给self。

使用self可以让类的方法操作对象的属性和方法。通过self,我们可以访问和修改对象的属性,调用对象的其他方法,以及在方法中创建临时变量。

实际上,self并不是Python中固有的关键字,我们可以用其他名称来代替self,但约定俗成的做法是使用self。在方法的定义和调用中,都应遵循这个约定,以保持代码的可读性和一致性。

例如,下面是一个简单的示例,演示了如何在类的方法中使用self:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hello(self):
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

person1 = Person("Alice", 25)
person1.say_hello()  # 输出: Hello, my name is Alice and I am 25 years old.

在上面的例子中,self被用于访问对象的name和age属性。当调用person1对象的say_hello方法时,self会自动接收person1对象的引用,并以此访问和操作对象的属性。

8.2.3 对象的创建与使用

①对象的创建
对象名=类名()
my_car=Car

②访问对象成员
对象名.属性
对象名.方法()

class Car:
    wheels=4
    def drive(self):
        print('开车方法')
    def stop(self):
        print('停车方法')

my_car=Car()   #创建对象
print(my_car.wheels)#访问对象的成员属性
my_car.drive()     #访问对象的成员方法

8.2.4 访问限制

①定义私有成员
__属性名
__方法名

②私有成员的访问

>>> class PersonInfo:
    __weight=55
    def __info(self):
        print(f'我的体重是:{__weight}')
        
>>> person=PersonInfo()
>>> person.__weight        #类外不访问私有成员,会报错
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
AttributeError: 'PersonInfo' object has no attribute '__weight'
>>> person.__info()
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
AttributeError: 'PersonInfo' object has no attribute '__info'
>>> 

(1) 访问私有属性

可以通过访问公有方法间接访问私有属性

>>> class PersonInfo:
    __weight=55
    def get_weight(self):
        print(f'我的体重是:{self.__weight}kg')
        
>>> person=PersonInfo()
>>> person.get_weight()
我的体重是:55kg

(2) 访问私有方法

可以通过访问公有方法间接访问私有方法

>>> class PersonInfo:
    __weight=55
    def __info(self):
        print(f'体重是:{self.__weight}')
    def get_weight(self):
        print(f'我的体重是:{self.__weight}kg')
        self.__info()
        
>>> person=PersonInfo()
>>> person.get_weight()
我的体重是:55kg
体重是:55
>>>

8.3 构造方法与析构方法

类中有两个特殊的方法: 构造方法 __init__()和析构方法 __del__()

8.3.1 构造方法

>>> class Inforamtion(object):
    def __init__(self,name,sex):
        self.name=name
        self.sex=sex
    def info(self):
        print(f'姓名:{self.name}')
        print(f'性别:{self.sex}')
        
>>> infomat=Inforamtion('小王','女')
>>> infomat.info()
姓名:小王
性别:女

8.3.2 析构方法

析构方法(Destructor)是一种特殊类型的方法,用于在对象被销毁之前执行清理操作。在许多编程语言中,每个类都可以定义一个析构方法,该方法没有任何参数,并且在对象被销毁时自动调用。

析构方法通常用于释放对象占用的资源,比如关闭文件、释放内存等。当对象不再被引用或程序结束时,析构方法会被自动调用。在调用析构方法之后,对象的内存空间将被回收。

需要注意的是,有些编程语言不支持析构方法,而是通过垃圾回收机制来自动处理内存释放。在这种情况下,开发人员无需显式定义析构方法,而是让垃圾回收机制自动回收不再使用的对象。

在创建自定义类时,如果需要执行一些清理操作(如关闭连接、释放资源等),可以考虑使用析构方法来实现。一般来说,析构方法应该与构造方法相对应,以确保对象创建和销毁时的一致性和完整性。

import sys
class Destruction:
    def __init__(self):
        print('对象被创建')
    def __del__(self):
        print('对象被释放')
    
des=Destruction()
print(sys.getrefcount(des))
del(des)

打印出 "des" 实例的引用计数。请注意,由于 sys.getrefcount() 会在使用期间增加一个额外的引用计数,因此打印结果将比预期的引用计数高 1。

8.4 类方法和静态方法

Python中的类除了self修饰的普通方法,还可定义使用@classmethon修饰的类方法和使用@staticmethon修饰的静态方法。

8.4.1 类方法

(1) 类方法使用装饰器@classmethod 修饰。
(2) 类方法的第一个参数为 cls 而非 self,它代表类本身
(3) 类方法即可由对象调用,亦可直接由类调用。
(4) 类方法可以修改类属性,实例方法无法修改类属性。

①定义类方法
类名.类方法
对象名.类方法
②修改类属性

>>> class Test:
    @classmethod
    def use_classmet(cls):
        print('我是类方法')
        
>>> test=Test()
>>> test.use_classmet()    #类方法即可由对象调用,亦可直接由类调用。
我是类方法
>>> Test.use_classmet()
我是类方法


>>> class Apple():
    count=0
    def add_one(self):
        self.count=1
    @classmethod
    def add_two(cls):
        cls.count=2
        
>>> apple=Apple()
>>> apple.add_one()    #实例方法无法修改类属性,但可以修改实例化对象的属性
>>> print(Apple.count)
0
>>> print(apple.count)
1
>>> apple.add_two()    #类方法可以修改类属性,但不可以修改实例化对象的属性
>>> print(Apple.count)
2

8.4.2 静态方法

(1) 静态方法没有 self 参数,它需要使用@staticmethod 修饰
(2) 静态方法中需要以“类名.方法/属性名”的形式访问类的成员
(3) 静态方法即可由对象调用,亦可直接由类调用。

>>> class Example():
    num=10
    @staticmethod
    def static_method():
        print(f'类属性的值为:{Example.num}')
        print('----静态方法')
        
>>> exp=Example()
>>> exp.static_method()
类属性的值为:10
----静态方法
>>> Example.static_method()
类属性的值为:10
----静态方法

8.5 继承

继承是面向对象编程中的一个重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类、基类或超类)继承属性和方法。

子类可以继承父类的属性和方法,并且还可以添加自己的特定属性和方法。这样可以实现代码的重用和扩展。

下面是一个简单的示例,展示了继承的基本用法:

class Animal:
    def __init__(self, name):
        self.name = name

    def eat(self):
        print(f"{self.name} is eating.")

class Cat(Animal):
    def meow(self):
        print("Meow!")

class Dog(Animal):
    def bark(self):
        print("Woof!")

cat = Cat("Tom")
cat.eat()  # 继承自父类 Animal 的 eat 方法
cat.meow()  # 子类 Cat 自己定义的方法

dog = Dog("Max")
dog.eat()  # 继承自父类 Animal 的 eat 方法
dog.bark()  # 子类 Dog 自己定义的方法

在这个例子中,Cat 类和 Dog 类都继承了 Animal 类。它们可以使用父类 Animal 的属性和方法,同时也可以添加自己的方法。

通过继承,子类可以重用父类的代码,同时还可以通过添加新的特定方法来扩展功能。这样可以减少代码的重复编写,提高代码的可维护性和灵活性。

8.5.1 单继承

class 子类(父类):
isinstace()函数和issubclass () 函数

①isinstance()函数是Python内置的一个函数,用来判断一个对象是否属于指定的类型。

isinstance()函数的语法格式如下:

isinstance(object, type)

其中,object是要判断的对象,type是指定的类型。

isinstance()函数的返回值是一个bool类型的值,如果对象是指定类型的实例,返回True;否则,返回False。

下面是一些示例:

x = 5
print(isinstance(x, int))  # 输出True,因为x是int类型的对象

y = "hello"
print(isinstance(y, str))  # 输出True,因为y是str类型的对象

z = [1, 2, 3]
print(isinstance(z, list))  # 输出True,因为z是list类型的对象

a = (1, 2, 3)
print(isinstance(a, tuple))  # 输出True,因为a是tuple类型的对象

b = {"name": "Tom", "age": 25}
print(isinstance(b, dict))  # 输出True,因为b是dict类型的对象

c = 5.5
print(isinstance(c, float))  # 输出False,因为c不是float类型的对象

dog=Dog()
isinstance(dog,Dog)    # 输出True,因为dog是Dog类型的对象

可以看到,isinstance()函数可以用来判断对象的类型,非常方便。

②issubclass()函数是用来判断一个类是否是另一个类的子类的函数。

函数语法:

issubclass(subclass, classinfo)

参数说明:subclass:要判断的类。

      classinfo:要对比的类或元组,可以是一个类对象或由类对象组成的元组

返回值:如果 subclassclassinfo 类的子类或者是 classinfo 类本身,则返回 True,否则返回 False

8.5.2 多继承

多继承指的是一个类可以继承多个父类的特性和行为。在一些编程语言中,如Python,允许一个类同时继承多个父类。

多继承的优点是可以在一个类中获得多个父类的特性和行为,增加了代码的复用性和灵活性。

class 子类(父类A,父类B):

class English:
    def eng_know(self):
        print('具备英语知识')
class Math:
    def math_know(self):
        print('具备数学知识')
        
class Student(English,Math):
    def study(self):
        print('学生的任务是学习')
        
stu=Student()
stu.eng_know()
stu.math_know()
stu.study()

执行结果:
>>> %Run main.py
具备英语知识
具备数学知识
学生的任务是学习

8.5.3 方法的重写

class Felines:
    def feature(self):
        print('猫科动物特长是爬树')
class Cat(Felines):
    name='猫'
    def feature(self):
        print(f'{self.name}会抓老鼠')
        print(f'{self.name}会爬树')

cat=Cat()
cat.feature()

执行结果:
>>> %Run main.py
猫会抓老鼠
猫会爬树

8.5.4 super()函数

super().方法名()

super()函数是一个用于调用父类方法的函数。它返回一个临时对象,这个对象是指向父类的一个实例,通过该对象可以调用父类的方法。在使用super()函数时,通常是在子类的方法内部使用,以便在子类中执行父类的方法,并可以对其进行扩展。通过super()函数,可以实现方法的重写、覆盖或者覆写,从而实现对继承的灵活应用。

class Felines:
    def feature(self):
        print('猫科动物特长是爬树')
class Cat(Felines):
    name='猫'
    def feature(self):
        print(f'{self.name}会抓老鼠')
        print(f'{self.name}会爬树')
        print('-'*20)
        super().feature()

cat=Cat()
cat.feature()

执行结果:
>>> %Run main.py
猫会抓老鼠
猫会爬树
--------------------
猫科动物特长是爬树

8.6 多态

多态的实现方式有两种:继承和接口。继承方式是通过子类继承父类的方法,然后在子类中重写父类的方法,实现多态效果。接口方式是通过定义接口,并让不同的子类实现接口中的方法,然后通过接口引用指向不同实现类的对象,实现多态效果。

多态的好处在于可以提高代码的灵活性和可扩展性。由于多态可以使得同一个方法适用于不同的对象,可以减少重复的代码,提高代码的复用性。另外,通过多态可以实现接口的抽象,可以在不改变接口的情况下扩展程序的功能。

class Animal(object):    #object是所有类的父类
    def move(self):
        pass  #pass用来占位保持函数结构的完整,相当于{}
class Rabbit(Animal):
    def move(self):
        print('兔子蹦蹦跳跳')
        
class Snail(Animal):
    def move(self):
        print('蜗牛缓慢爬行')
        
def test(obj):    #通过参数的不同调用不同的move方法
    obj.move()
    
rabbit=Rabbit()
test(rabbit)
snail=Snail()
test(snail)

#执行结果
'''
>>> %Run main.py
兔子蹦蹦跳跳
蜗牛缓慢爬行
'''

9.异常处理

9.1 除零异常

i=input('请输入数字:')    #当输入为0时,会引发程序无法执行或异常结束
n=12
result=n/int(i)
print(result)
print(f'{n}除以{i}等于{result}')

9.2 捕获异常

在Python中可使用try...except语句捕获一次,try...except还可以与else、finally组合使用实现更强大的异常处理功能。

9.2.1 try...except语句

try:
        可能出错的代码
except [异常类型]:
        错误处理语句

i=input('请输入数字:')
n=12
try:
    result=n/int(i)
    print(result)
    print(f'{n}除以{i}等于{result}')
except:
    print('除数不能为0!')
    
'''执行结果
>>> %Run main.py
请输入数字:0
除数不能为0!
'''

9.2.2 捕获异常信息

①捕获程序运行时的单个异常
i=input('请输入数字:')
n=12
try:
    result=n/int(i)
    print(result)
    print(f'{n}除以{i}等于{result}')
except ZeroDivisionError as e:    #指定异常类型为除数为零异常
    print('除数不能为0!')
    print(f'异常原因:{e}')
    

'''执行结果
>>> %Run main.py
请输入数字:0
除数不能为0!
异常原因:division by zero
'''
②捕获程序运行时的多个异常
try:
    print(count)
    demo_list=['Python','java','C','C++']
    print(demo_list[4])
except (NameError,IndexError) as e:
    #NameError:未定义名字异常
    #IndexError:索引越界异常
    print(f'异常错误原因:{e}')
'''
执行结果
>>> %Run main.py
异常错误原因:name 'count' is not defined
'''

try:
    #print(count)
    demo_list=['Python','java','C','C++']
    print(demo_list[4])
except NameError as e:
    #NameError:未定义名字异常
    print(f'异常错误原因:{e}')
except IndexError as e:
    #IndexError:索引越界异常
    print(f'异常错误原因:{e}')
'''
执行结果
>>> %Run main.py
异常错误原因:list index out of range
'''
③捕获程序运行时的所有异常
try:
    print(count)
    demo_list=['Python','java','C','C++']
    print(demo_list[4])
except Exception as e:          #与"excpt :"一样捕获所有异常
    print(f'异常错误原因:{e}')
#excpt :
#    print('程序出现异常,原因未知')

9.2.3 else子句

try:
        可能出错的语句
except :
        出错后的执行语句
else:
        未出错时的执行语句

num=input('请输入每页显示多少条数据:')
try:
    page_num=int(num)
except Exception as e:
    page_num=10
    print(f'当前页面显示{page_num}条数据')
else:
    print(f'当前页面显示{num}条数据')

'''
执行结果
>>> %Run main.py
请输入每页显示多少条数据:1
当前页面显示1条数据
>>> %Run main.py
请输入每页显示多少条数据:ts
当前页面显示10条数据
'''

9.2.4 finally子句

try:
        可能出错的语句
except:
        出错后的执行语句
finally:
        无论是否错误都会执行的语句

9.3 抛出异常

9.3.1 raise语句

(1) 由异常类名引发异常
(2) 由异常对象引发异常
(3) 由程序中出现过的异常引发异常

#(1) 由异常类名引发异常
>>>raise NameError
#(2) 由异常对象引发异常
name_error=NameError()
raise name_error
#(3) 由程序中出现过的异常引发异常
try:
    num
except NameError as e:
    raise

9.3.2 异常的传递

异常的传递是指在程序中,当一个异常被抛出但没有在当前的代码块中被捕获处理时,它会被传递给调用栈中的上一级代码块进行处理。这样的过程会一直持续,直到异常被捕获或者到达程序的最顶层代码块。

在异常的传递过程中,如果没有捕获到异常,最终会传递到程序的最顶层代码块,如果还没有进行处理,程序将会终止运行,并输出异常信息。因此,在编写程序时,我们应该合理地设计异常处理机制,捕获并处理可能出现的异常,以保证程序的稳定性和可靠性。

def get_width():
    print('get_width开始执行')
    num=int(input('请输入除数:'))
    width_len=10/num
    print('get_width执行结束')
    return width_len
def calc_area():
    print('calc_area开始执行')
    width_len=get_width()
    print('calc_area执行结束')
    return width_len*width_len
def show_area():
    try:
        print('show_area开始执行')
        area_val=calc_area()
        print(f'正方形的面积是: {area_val}')
        print('show_area执行结束')
    except ZeroDivisionError as e:
        print(f'捕获到异常: {e}')
if __name__=='__main__':
    show_area()
'''
执行结果
>>> %Run main.py
show_area开始执行
calc_area开始执行
get_width开始执行
请输入除数:1
get_width执行结束
calc_area执行结束
正方形的面积是: 100.0
show_area执行结束
>>> %Run main.py
show_area开始执行
calc_area开始执行
get_width开始执行
请输入除数:0
捕获到异常: division by zero
'''

9.4 自定义异常

class CustomError(Exception):    #自定义异常类
    pass

try:
    pass
    raise CustomError('出现错误')    #抛出异常
except CustomError as e:
    print(e)
    
'''
执行结果
>>> %Run main.py
出现错误
'''

10. 模块

10.1 模块的概念

Python中的模块可分为三类: 内置模块、第三方模块和自定义模块

10.2 模块的导入方式

使用import导入和使用from....import....导入

①使用import导入
import 模块1,模块2,...
模块名.函数名 ()/类名
import 模块名 as 别名

'''
这段代码使用了time模块和random模块,并且还导入了sys模块。
可以使用time.sleep()函数来让程序暂停执行一段时间。
random模块可以用来生成随机数。
sys模块提供了对Python解释器的访问和控制。
'''
import time
import random,sys

time.sleep(1)    #延时1S

②使用from...import...导入
from 模块名 mport 函数/类/变量
from time import sleep, time
from 模块名 import *
from 模块名 import 函数名 as 别名
from time import sleep as sl
sl(1)        #延时1S

10.3 自定义模块

#main.py
age=13
def introduce():
    print(f'我的名字是小王,今年{age}岁')


#test.py
import main
main.introduce()
print(main.age)

'''
执行结果
>>> %Run test.py
我的名字是小王,今年13岁
13
'''

#test1.py
from main import introduce
introduce()

'''
执行结果
>>> %Run test.py
我的名字是小王,今年13岁
'''

10.4 模块的导入特性

10.4.1 __all__属性

__all__属性是一个列表,用于定义模块中可以被导入的公共接口。当其他模块使用from module import *语句时,只有__all__列表中指定的成员会被导入。如果__all__属性未定义或为空列表,则不允许使用from module import *语句导入任何成员。

#calc.py文件
__all__=['add' , 'subtract']    #只允许导入add,subtract

def add(a, b):
    return a+b
def subtract(a,b):
    return a-b
def multiply(a,b):
    return a*b
def divide(a,b):
    if b:
        return a/b
    else:
        print('error')


#test.py文件
from main import*

print(add(2,3))
print(subtract(2,3))
print(multiply(2,3))
print(divide(2,3))

'''
执行结果:
>>> %Run test.py
5
-1
Traceback (most recent call last):
  File "E:\ESP32物联网开发板\4--实验程序\1--MicroPython实验\test.py", line 5, in <module>
    print(multiply(2,3))
NameError: name 'multiply' is not defined
'''

10.4.2 __name__属性

__name__属性是一个内建属性,用于表示当前模块的名称。

当模块作为主程序运行时,__name__的值为'__main__'。 当模块作为被导入的模块使用时,__name__的值为模块的名称。

这个属性可用于判断在不同的模式下执行不同的代码。例如,可以在模块的主程序中加入以下代码:

if __name__ == "__main__": # 如果模块作为主程序运行,则执行以下代码 main_function()

这样,当模块作为主程序运行时,主函数main_function()会被调用;而当模块被作为被导入的模块使用时,主函数main_function()将不会被调用。

这个属性对于模块的调试和测试非常有用,可以将一些测试代码放在这个判断语句中,只有当模块作为主程序运行时才会执行这些测试代码。

all__=['add','subtract']
def add(a, b):
    return a+b
def subtract(a,b):
    return a-b
def multiply(a,b):
    return a*b
def divide(a,b):
    if b:
        return a/b
    else:
        print('error')
if __name__=='__main__':
    print(add(2,3))
    print(subtract(2,3))
    print(multiply(2,3))
    print(divide(2,3))

10.5 Python中的包

10.5.1 包的结构

包的结构一般由多个子包或模块组成,每个子包或模块中包含一组相关的类或功能。这种组织方式的好处是可以更好地管理代码,提高代码的可维护性和可重用性。

包的结构可以按照不同的层次或模块划分,例如按照功能划分、按照模块划分、按照层次划分等。具体的包的结构设计要根据具体的项目需求和开发规范来确定。

10.5.2 包的导入

#module_demo.py文件在package_demo包内
def add(num1,num2):
    print(num1+num2)



#test.py文件
#方法一
from package_demo import module_demo
#package_demo包名,module_demo模块名
module_demo.add(1,3)

#方法二
import package_demo.module_demo
package_demo.module_demo.add(1,3)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值