小编在此整理了python面向对象的相关内容,内容较长,请耐心读完。
注意:由于本人的个人习惯,为了使代码更加清晰明了,将采取以下方式呈现代码,诸位在学习敲代码时注意将前面的>>>
去掉
>>> 代码部分
>>> 代码部分
输出部分
一、面向对象介绍
1.1面向对象与面向过程
面向过程:面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
面向对象:面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
1.2 类和对象
在面向对象编程过程中,有两个重要组成部分:类和对象。
类:就是具有相同属性和功能的一类事物会使用类进行定义。
对象:就是类的具体表现,是面向对象编程的核心。
类的三大特性:封装,继承,多态
类和对象的区别:类是对客观世界中事物的抽象,而对象是类实例化后的实体。
类和对象的关系:对象是由类派生的、创建的。一个类可以创建无穷多个对象,每个对象都属于类。类只有创建了对象才可以使用。
1.3 类的定义
class 类名:
pass/属性/方法
类命名规范: 大驼峰命名规则(一种命名规则,用于命名书写的标识符或者变量,例如类名、函数名、变量名等,其中每个单词的首字母都大写,单词之间没有下划线或其他分隔符。大驼峰命名规则也称为 Pascal Case。)
>>>class A:
>>> pass
此时创建了一个最简单的类A,里面不含有任何属性和方法,故使用pass来做占位符
1.4 对象的创建
对象名 = 类名()
>>>a = A() #注意:这里还不需要参数
1.5 方法和属性
1.5.1 方法
方法:类中定义的函数
例如:下面就定义了一个名叫test的方法
>>>class B:
>>> def test(self):
>>> pass
注意:类中的方法一般会在括号中有一个参数self,表示该方法所属的实例自身。self指向该实例,从而可以在方法中操作和访问实例的属性和方法。
方法分为很多种,包括实例方法,类方法,静态方法,魔法方法(实例方法中的特殊方法)
方法 | 定义 | 解释 |
---|---|---|
实例方法 | 实例方法是类中最常见的方法类型 | 方法定义中需要包含 self 参数用来表示实例自身 |
类方法 | 类方法是一种在类级别上操作类属性的方法 | 在方法定义中需要使用 @classmethod 修饰符,并且必须有一个参数 cls 用来表示实例自身 |
静态方法 | 静态方法是类中不依赖实例的方法 | 在方法定义中需要使用 @staticmethod 修饰符,不需要传递任何实例或类参数,只需要在方法中传递需要的参数即可 |
魔法方法 | 魔法方法是 Python 中特殊的方法 | 其名称以双下划线开始和结束,用于在类中定制一些内置函数的行为 |
1.5.2 属性
属性分为类属性和实例属性
属性 | 定义位置 | 定义格式 |
---|---|---|
类属性 | 在类里面,方法外面定义 | 属性名=值 |
实例属性 | 定义在方法里面 | self.属性名=值 |
添加和修改类属性:
在类里面添加和修改类属性:类属性名 = 值
在类外面添加和修改类属性:类名.类属性名 = 值
获取类属性:
类外面获得类属性有两种方式,既可以通过类来访问,也可以通过对象来访问
使用类名访问类属性:类名.类属性
使用对象名访问类属性:对象名.类属性
添加和修改实例属性:
类里面添加和修改实例属性:self.实例属性名 = 值
类外面添加和修改实例属性:对象名.实例属性名 = 值
获取实例属性:
类里面获取实例属性:self.实例属性名
类外面获取实例属性:对象名.实例属性名
例如:定义类属性和实例属性,以及访问
>>>class C:
>>> a = 100 #添加类属性a
>>> def __init__(self): #这是构造函数,在后面会具体介绍
>>> self.b = 10 #添加实例属性b
>>>d = C()
>>>print(d.a) #通过实例访问类属性
100
>>>print(C.a) #通过类访问类属性
100
>>>print(d.b) #通过对象访问实例属性
10
1.6 构造函数与析构函数
类中的构造函数与析构函数是类中自带函数,构造函数与析构函数是两个特殊的魔法方法,它们的方法名分别为 __init__
和 __del__
。
构造函数:构造函数是一种特殊的方法,用于在创建类实例时初始化实例的属性。构造函数的方法名为 __init__
,其形参中的第一个参数通常为 self
,表示该构造函数方法所属实例自身;其余参数可以用来传递给实例的成员变量进行初始化。
例如:
>>>class Person:
>>> def __init__(self, name, age):
>>> self.name = name
>>> self.age = age
>>>person = Person('Alice', 23)
>>>print(person.name)
Alice
>>>print(person.age)
23
析构函数:析构函数是一种特殊的方法,用于在删除类实例时清理实例的资源。析构函数的方法名为 __del__
,其没有形参,只需定义即可。一旦实例被删除,Python 会自动调用析构函数。
例如:
>>>class Person:
>>> def __del__(self):
>>> print('{} has been destroyed.'.format(self.__class__.__name__)) #.format()表示填入{}的内容
>>>person = Person()
>>>del person #使用del 调用析构函数清理实例的内存
Person has been destroyed.
需要注意的是,析构函数并不是必需的,如果类中没有提供析构函数,Python 会自动清理实例的资源。此外,由于 Python 使用垃圾收集机制进行内存管理,实例可能不会立即被删除,因此析构函数的调用时机并不确定。
二、方法
2.1 实例方法
实例方法是类中最常见的方法类型
定义语法:(注意:在类中定义)
class 类名:
def 实例方法名(self):
实例属性
例如:
class A:
def getself(self): # 定义名为getself的实例方法
return self # 返回实例对象自身
2.2 类方法
在定义类方法时,要使用**@classmethod**进行修饰,并且必须有默认参数“cls”,通过它可以传递类的属性和方法(不能传递实例的属性和方法)。
类方法使用场景:
1.当方法中需要使用类对象(如访问私有类属性等)时,定义类方法
2.类方法一般和类属性配合使用,可以直接对类属性进行修改
例如:
>>>class Fun:
>>> name = 'classproperty'
>>> def funa(self):
>>> print('selfway')
>>> Fun.funb('xiaoming')
>>> @classmethod # 注意:一定要加上修饰符
>>> def funb(cls,name): # cls: 类
>>> print(cls) # 打印当前类对象的信息
>>> print('classway')
>>> print(cls.name) # 打印类属性
>>> cls.name = name # 修改类属性
>>>f = Fun()
>>>Fun.funb('xiaohong')
<class '__main__.Fun'>
classway
classproperty
>>>f.funb('xiaotian')
<class '__main__.Fun'>
classway
xiaohong
>>>print(Fun)
<class '__main__.Fun'>
>>>f.funa()
selfway
<class '__main__.Fun'>
classway
xiaotian
2.3 静态方法
在定义静态方法时,要使用**@staticmethod**进行修饰。参数随意,没有“self”和“cls”参数,但是方法体中不能使用类或实例的任何属性和方法。
静态方法使用场景:
当方法中既不需要使用实例对象(如实例对象、实例属性),也不需要使用类对象(如类属性、类方法、创建实例等)时,定义静态方法。
例如:
>>>class Fun:
>>> @staticmethod
>>> def func():
>>> print('staticway')
>>> @staticmethod
>>> def fund(name):
>>> print(name)
>>>Fun.func() #注意: 静态方法既可以使用对象访问又可以使用类访问
staticway
>>>Fun.fund('xiaoming')
xiaoming
2.4 魔法方法
Python中的魔法方法(也称特殊方法或魔术方法)都是以“__”(双下划线)开头和结尾的特殊方法,提供了类与对象在运行过程中的一些钩子函数。魔法方法的调用通常是隐式的,也就是说,它们像普通方法一样被调用,但是它们的调用是由Python解释器进行的。
以下是一些常用的Python中的魔法方法:
魔法方法 | 作用 |
---|---|
__init__(self[, ...]) | 构造函数,对象创建时自动调用,用于初始化实例变量 |
__del__(self) | 析构函数,对象被销毁时自动调用 |
__str__(self) | 控制对象的字符串表达,将实例转换成一个字符串 |
__repr__(self) | 用于调试,返回一个对象的字符串表示 |
__getattr__(self, name) | 在查找不到特定属性时被调用 |
__setattr__(self, name, value) | 对象执行属性赋值操作时被调用 |
__call__(self, args) | 允许将对象作为函数调用 |
__len__(self) | 控制对象的长度 |
__getitem__(self, key) | 对象允许使用中括号[]操作符时被调用 |
__setitem__(self, key, value) | 对象允许使用中括号[]操作符进行赋值时被调用 |
__iter__(self) | 控制对象的迭代行为,使其支持迭代器协议 |
__next__(self) | 控制迭代器的行为,返回下一个迭代器 |
__reversed__(self) | 控制reversed( )函数的行为,返回翻转后的序列 |
__eq__(self, other) | 控制相等运算符“==”的行为 |
__lt__(self, other) | 控制小于运算符“<”的行为 |
__add__(self, other) | 控制加法运算符“+”的行为 |
__sub__(self, other) | 控制减法运算符“-”的行为 |
__mul__(self, other) | 控制乘法运算符“*”的行为 |
__div__(self, other) | 控制除法运算符“/”的行为 |
还有许多其他的魔法方法,具体的使用情况可以参考Python官方文档
三、封装
类的封装是指将数据和对数据的操作封装在一起,通过调用方法来访问或修改数据,从而实现数据的安全性、可维护性和重用性。类的封装可以避免外部直接修改类的属性和方法,从而防止意外修改和保护数据的完整性。
封装的意义:
1.将属性和方法放到一起做为一个整体,然后通过实例化对象来处理。
2.隐藏内部实现细节,只需要和对象及其属性和方法交互就可以了。
3.对类的属性和方法增加 访问权限控制。
Python 中的类封装主要是通过访问控制来实现的,即使用 public、private 和 protected 关键字来控制属性和方法的访问范围。
-
公有属性和方法:在类的内部和外部都可以直接访问和使用,没有访问限制。公有属性和方法可以使用默认的命名方式,即不加下划线。
-
私有属性和方法:私有属性和方法只能在类的内部使用,外部无法直接访问和修改。私有属性和方法的命名方式是在其前面加上两个下划线“__”。
-
保护属性和方法:在类的内部和子类中可以直接访问和使用,但是在外部无法直接访问。保护属性和方法的命名方式是在其前面加上一个下划线“_”。
在 Python 中,封装并不是绝对的,也可以通过一些方式访问私有和保护属性和方法,但这种访问方法不被推荐使用。
以下是一个使用封装的 Python 类的示例:
>>>class Person:
>>> def __init__(self, name, age, gender):
>>> self.__name = name # 私有属性
>>> self._age = age # 保护属性
>>> self.gender = gender # 公有属性
>>> def getname(self): # 公有方法
>>> return self.__name # 在类外无法访问私有属性,但是可以通过调用公有方法,其返回值为私有属性的值
>>> def getage(self):
>>> return self._age # 在类外同样无法访问保护属性,也可以通过这种方法获得
>>> def setage(self, age):
>>> if age > 0 and age < 150:
>>> self._age = age # 公有方法可以在类内修改保护属性
>>>p = Person("xiaoming",18,"male")
>>>print(p.getname)
xiaoming
>>>print(p._Person__name) # 在类外面访问私有属性:对象名._类名__私有属性名 (了解,但是最好不要这样做)
xiaoming
>>>print(p.getage)
18
>>>p.setage(20)
>>>print(p.getname)
20
在这个例子中,__name
是私有属性 _age
是保护属性,外部无法直接访问和修改。
getname()
方法是公有方法,可以用来获取 __name
属性。
getage()
方法是公有方法,可以用来获取_age
属性。
setage()
方法是公有方法,可以用来修改 _age
属性。在 setage()
方法中可以添加条件判断,限制属性值的合法性。
需要注意的是,Python 中的私有属性和方法可以被访问和修改,但是需要使用一些特殊的方式,这种方式不被推荐使用。例如,可以使用 _类名__属性名
的方式来访问私有属性,但这可能会导致代码的可读性和可维护性降低。因此,尽量不要对私有属性和方法进行访问和修改。
注意:python中方法中的变量类型没有特定指定时是可变的,例如:构造函数中的形参age
在传入18
时为整型,在传入"18"
时则为字符串型。这会使不论传入什么样的参数的都可以正常运行,为程序埋下隐患。
如果想要指定变量的类型则需要变量名:类型名
这样这些变量就只能传入特定的参数了。若是想要指定默认参数就需要在后面添加,其形式为变量名:类型名=默认值
。
例如:a:int=0
定义了一个整型变量a,其默认参数为0
四、继承
4.1继承的概念
子类拥有父类的所有属性和方法。
继承必定发生在两个类之间, 参与继承关系的双方成为是父类和子类。
类的继承是指新类从已有的类中取得已有的特性(如属性、变量、方法等)
4.2 继承的优点
1.增加了类的耦合性(耦合性不宜多,宜精)。
2.减少了重复代码。
3.使得代码更加规范化,合理化。
4.3 继承语法
class 子类名(父类名):
语句/pass
4.4 继承的属性访问权限
类 | 私有属性 | 保护属性 | 公有属性 |
---|---|---|---|
父类 | 可以访问 | 可以访问 | 可以访问 |
子类 | 不可以访问 | 可以访问 | 可以访问 |
类外 | 不可以访问 | 不可以访问 | 可以访问 |
注意:
1.这里的父类和子类是指在对应的类内访问。
2.访问指的是直接访问,通过公有函数返回值访问属于间接访问。
3.这里的访问皆是非特殊方法。
例如:
>>>class Father:
>>> def __init__(self):
>>> self.money = 100000000
>>> self.house = 10
>>> self.__car = 6 # 私有属性和私有方法是不会被子类继承
"""
创建子类的语法:
class 子类名(父类名):
语句/pass
"""
>>>class Son(Father): # 子类,继承父亲类
>>> pass
>>>son = Son() # 创建子类的对象
>>>print(son.money)
100000000
>>>print(son._Son.__car)
AttributeError: 'Son' object has no attribute '_Son' # 调用父类的私有属性发生报错,子类不能继承父类的私有属性与私有方法
>>>class Grandson(Son): # 继承的传递:孙子类,继承儿子类
>>> pass
>>>grandson = Grandson() # 创建孙子类对象
>>>print(grandson.money)
100000000
4.5 重写和扩展
4.5.1 重写
重写:当子类在使用父类中的方法时,如果发现父类中的方法不符合子类的需求,可以对父类中的方法进行重写。这个过程称为方法的覆盖,也称为方法的重写。
重写的作用:
1.修改方法的行为:在子类中重新定义父类的方法,可以改变父类的方法行为或实现逻辑,实现不同的业务需求。
2.扩展方法的功能:在子类中继承父类方法的同时,也可以覆盖父类方法的部分实现,增加子类特有的功能,从而实现更复杂的操作。
3.实现多态:重写是多态的一种实现形式,它允许以相同的方式处理不同的对象,在父类中定义通用的方法,子类可以根据自己的需要对方法进行重写,以实现各自的特定行为。
4.提高代码效率:通过对继承的方法进行重写,可以使代码结构更加优雅,同时减少代码量,提高代码效率,避免重复编写代码。
4.5.2 扩展
扩展:类的扩展通常指在现有类的功能上添加新的功能或功能,而不必在现有类上进行全面的修改或者重新定义。
扩展的作用:
1.代码重用:在现有类的基础上扩展类可以避免重复编写代码,节省了大量的时间和精力,同时也减小了出错的机会。
2.功能增强:在不影响原来类的基础上,通过添加新的方法、属性或者修改方法等方式对现有类实现功能的扩充,使得类在新的场景下得以更好地应用。
3.实现接口:通过类的扩展,可以实现相同接口的不同实现,为多态的实现提供支持,使得代码更加灵活。
4.代码模块化:通过将类扩展分解为小模块,可以使程序结构更加清晰,易于维护和管理。
5.简化代码逻辑:通过类扩展,可以让一部分代码逻辑独立出来,代码更清晰,更易于理解。
扩展的语法:
1.父类名.方法名(self)
2.super().方法名()
例如:
>>>class Animal(object): # object基类
>>> def eat(self):
>>> print('eating')
>>> def drink(self):
>>> print('drinking')
>>>class Dog(Animal):
>>> def eat(self): # 覆盖父类方法:在子类中定义跟父类同名的方法
>>> print('meat')
>>> def drink(self):
>>> Animal.drink(self) # 扩展:父类名.方法名(self)
>>> print('water')
>>> super().drink() # 扩展:super().方法名()
>>>dog = Dog() # 创建狗类对象
>>>dog.eat()
meat
>>>dog.drink()
drinking
water
drinking
>>>print(Dog.__mro__) # 用__mro__ 可以查看调用顺序 :类名.__mro__
(<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)
4.6 多继承
多继承语法:
class 子类名(父类1,父类2,父类3,…):
pass/方法/属性
例如:
>>>class Father: # 父亲类 -- 父类
>>> def eyecolor(self):
>>> print("blue")
>>> def money(self):
>>> print(20000)
>>>class Mother: # 母亲类 -- 父类
>>> def skincolor(self):
>>> print('yellow')
>>> def money(self):
>>> print(10000)
>>>class Son(Mother,Father): # 儿子类 -- 子类,继承母亲类、父亲类
>>> def money(self):
>>> print(200)
>>> super().money() # 默认调用第一个继承的父类中的同名方法
>>>son = Son()
>>>son.eyecolor()
blue
>>>son.skincolor()
yellow
>>>son.money()
200
10000 # 这里自动调用Mother类的money函数
>>>print(Son.__mro__)
(<class '__main__.Son'>, <class '__main__.Mother'>, <class '__main__.Father'>, <class 'object'>)
>>>class Daughter(Father,Mother): # 女儿类 -- 子类,继承父亲类、母亲类
>>> def money(self): # 重写
>>> print(300)
>>> Father.money(self) # 扩展: 父类名.方法名(self)
>>> Mother.money(self)
>>> super().money() # 扩展: super().方法名()
>>>daughter = Daughter()
>>>daughter.eyecolor()
blue
>>>daughter.skincolo()
yellow
>>>daughter.money()
300
20000
10000
20000
>>>print(Daughter.__mro__)
(<class '__main__.Daughter'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)
**注意:**使用super()
方法时会默认从上一级父类中继承(从__mro__
函数可以看到调用顺序)同名方法,而使用父类名.方法名可以精准调用所继承的父类中的同名方法。
五、多态
5.1 多态的概念
多态:指的是一类事物有多种形态
定义:多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
5.2 多态实现步骤
1.定义父类,并提供公共方法
2.定义子类,并重写父类方法
3.传递子类对象给调用者,可以看到不同子类执行效果不同
5.3 使用多态的好处
调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求不断变化
5.4 多态与多态性
多态:同一种事物的多种形态(在定义角度:继承,重写)
多态性:一种调用方式,不同的执行效果(在使用角度)
多态性是指让具有不同功能的函数可以使用相同的函数名,这样就可以用**一个函数名调用不同内容(功能)**的函数。
例如:
>>>class Animal:
>>> def make_sound(self): # 定义函数make_sound
>>> print("Animal makes sound")
>>>class Dog(Animal):
>>> def make_sound(self):
>>> print("Dog barks")
>>>class Cat(Animal):
>>> def make_sound(self):
>>> print("Cat meows")
>>>def animal_sound(animal): # 接受一个`Animal`类型的参数,并调用其make_sound()方法
>>> animal.make_sound()
>>>dog = Dog()
>>>cat = Cat()
>>>animal_sound(dog) #由于Dog和Cat都是Animal的子类,因此它们可以被传入animal_sound()函数
Dog barks
>>>animal_sound(cat)
Cat meows
六、运算符重载
运算符重载是一种特殊的函数重载,它使得我们可以将C++中已有的运算符(如“+”,“-”,“*”等)用于自定义类型。在python也可以使用运算符重载来使一些本来不支持运算符操作的类能够支持运算符操作,同时也可以提高代码可读性和程序员的工作效率。
以下是一些常用的运算符和对应的特殊方法:
运算符名称 | 运算符 | 特殊方法 |
---|---|---|
加法 | + | __add__(self, other) |
减法 | - | __sub__(self, other) |
乘法 | * | __mul__(self, other) |
除法 | / | __div__(self, other) |
取余 | % | __mod__(self, other) |
求负 | - | __neg__(self) |
求平方 | ** | __pow__(self, other) |
小于 | < | __lt__(self, other) |
大于 | > | __gt__(self, other) |
等于 | == | __eq__(self, other) |
不等于 | != | __ne__(self, other) |
小于等于 | <= | __le__(self, other) |
大于等于 | >= | __ge__(self, other) |
例如:
>>>class Matrix:
>>> def __init__(self,a,b,c,d,e,f,g,h,i):
>>> self.arr = [[a,b,c],[d,e,f],[g,h,i]]
>>> def __radd__(self, other): # 重载右加运算符,即对象+整数
>>> return Matrix(self.arr[0][0] + other, self.arr[0][1] + other, self.arr[0][2] + other,
>>> self.arr[1][0] + other, self.arr[1][1] + other, self.arr[1][2] + other,
>>> self.arr[2][0] + other, self.arr[2][1] + other, self.arr[2][2] + other)
>>> def __add__(self, other): # 重载加法运算符
>>> if isinstance(self,Matrix) and isinstance(other,int): #判断加号左边的是否为Matrix类型,右边是否为整型
>>> return Matrix(other + self.arr[0][0], other + self.arr[0][1], other + self.arr[0][2],
>>> other + self.arr[1][0], other + self.arr[1][1], other + self.arr[1][2],
>>> other + self.arr[2][0], other + self.arr[2][1], other + self.arr[2][2])
>>> elif isinstance(self,Matrix) and isinstance(other,Matrix):#判断加号左边的是否为Matrix类型,右边是否为Matrix类型
>>> return Matrix(self.arr[0][0] + other.arr[0][0], self.arr[0][1] + other.arr[0][1], self.arr[0][2] + other.arr[0][2],
>>> self.arr[1][0] + other.arr[1][0], self.arr[1][1] + other.arr[1][1], self.arr[1][2] + other.arr[1][2],
>>> self.arr[2][0] + other.arr[2][0], self.arr[2][1] + other.arr[2][1], self.arr[2][2] + other.arr[2][2])
>>> def __sub__(self, other): # 重载减法运算符
>>> return Matrix(self.arr[0][0] - other.arr[0][0],
>>> self.arr[0][1] - other.arr[0][1],
>>> self.arr[0][2] - other.arr[0][2],
>>> self.arr[1][0] - other.arr[1][0],
>>> self.arr[1][1] - other.arr[1][1],
>>> self.arr[1][2] - other.arr[1][2],
>>> self.arr[2][0] + other.arr[2][0],
>>> self.arr[2][1] + other.arr[2][1],
>>> self.arr[2][2] + other.arr[2][2])
>>> def __rmul__(self, other): # 重载右乘运算符,即对象*整数
>>> return Matrix(self.arr[0][0] * other, self.arr[0][1] * other, self.arr[0][2] * other,
>>> self.arr[1][0] * other, self.arr[1][1] * other, self.arr[1][2] * other,
>>> self.arr[2][0] * other, self.arr[2][1] * other, self.arr[2][2] * other)
>>> def __mul__(self, other:int or Matrix): # 重载乘法运算符
>>> if isinstance(self,Matrix) and isinstance(other,int):
>>> return Matrix(other * self.arr[0][0], other * self.arr[0][1], other * self.arr[0][2],
>>> other * self.arr[1][0], other * self.arr[1][1], other * self.arr[1][2],
>>> other * self.arr[2][0], other * self.arr[2][1], other * self.arr[2][2])
>>> elif isinstance(self,Matrix) and isinstance(other,Matrix): # 三阶矩阵乘法
>>> return Matrix(self.arr[0][0] * other.arr[0][0] + self.arr[0][1] * other.arr[1][0] + self.arr[0][2] * other.arr[2][0],
>>> self.arr[0][0] * other.arr[0][1] + self.arr[0][1] * other.arr[1][1] + self.arr[0][2] * other.arr[2][1],
>>> self.arr[0][0] * other.arr[0][2] + self.arr[0][1] * other.arr[1][2] + self.arr[0][2] * other.arr[2][2],
>>> self.arr[1][0] * other.arr[0][0] + self.arr[1][1] * other.arr[1][0] + self.arr[1][2] * other.arr[2][0],
>>> self.arr[1][1] * other.arr[0][1] + self.arr[1][1] * other.arr[1][1] + self.arr[1][2] * other.arr[2][1],
>>> self.arr[1][2] * other.arr[0][2] + self.arr[1][1] * other.arr[1][2] + self.arr[1][2] * other.arr[2][2],
>>> self.arr[2][0] * other.arr[0][0] + self.arr[2][1] * other.arr[1][0] + self.arr[2][2] * other.arr[2][0],
>>> self.arr[2][1] * other.arr[0][1] + self.arr[2][1] * other.arr[1][1] + self.arr[2][2] * other.arr[2][1],
>>> self.arr[2][2] * other.arr[0][2] + self.arr[2][1] * other.arr[1][2] + self.arr[2][2] * other.arr[2][2],)
>>> def print(self):
>>> print(f"{self.arr[0][0]} {self.arr[0][1]} {self.arr[0][2]}\n{self.arr[1][0]} {self.arr[1][1]} {self.arr[1][2]}\n{self.arr[2][0]} {self.arr[2][1]} {self.arr[2][2]}")
>>>a = Matrix(1,1,1,2,2,2,3,3,3)
>>>b = Matrix(1,2,3,4,5,6,7,8,9)
>>>c = a + b # 检测加法运算符,实现对象中的每个对应属性相加
>>>c.print()
2 3 4
6 7 8
10 11 12
>>>d = a - b # 检测减法运算符,实现对象中的每个对应属性相减
>>>d.print()
0 -1 -2
-2 -3 -4
10 11 12
>>>e = a * 5 + b * 3 # 检测乘法运算符:对象*整数,实现对象中的每个对应属性的值乘上整数倍
>>>e.print()
8 11 14
22 25 28
36 39 42
>>>f = 5 * a # 检测乘法运算符:整数*对象,实现对象中的每个对应属性的值乘上整数倍(与上面的有区别)
>>>f.print()
5 5 5
10 10 10
15 15 15
>>>g = a * b # 检测乘法运算符:对象*对象(这里都是三阶矩阵,所以存在这种重载)
>>>g.print()
12 15 18
24 30 36
36 45 54
>>>h = 10 + a + b # 检测加法运算符:整数+对象,实现对象中的每个对应属性的值加上整数的值
>>>h.print()
12 13 14
16 17 18
20 21 22
>>>i = a + b + 100 # 检测加法运算符:对象+整数,实现对象中的每个对应属性的值加上整数的值
>>>i.print()
102 103 104
106 107 108
110 111 112
七、文件操作
7.1 文件操作步骤
1.打开文件
2.读写等操作
3.关闭文件
7.2 创建文件
首先创建一个文件名为test 的txt文件
内容如下:
10001 小明
10002 小红
10003 小天
7.3 打开文件
Python open() 方法用于打开一个文件,默认以读的方式打开,并返回文件对象。
打开文件语法:
f = open(name, mode='r')
name:必需,是要打开的目标文件的文件路径。
mode:设置打开文件的模式(访问模式):只读(‘r’)、写入(‘w’)、追加(‘a’)等。
常用的文件打开模式及作用
模式 | 作用 |
---|---|
r | 只读模式,文件必须存在,否则会抛出异常 |
w | 写入模式,打开文件后清空文件内容,如果文件不存在则创建新文件 |
a | 追加模式,不清除文件原有内容,如果文件不存在则创建新文件 |
x | 独占模式,创建新文件并只允许写操作,如果文件已存在,则抛出异常 |
b | 二进制模式,打开文件用于读或写成二进制字节数据 |
t | 文本模式,打开文件用于读或写成普通文本数据 |
+ | 可以在读取文件内容的同时进行写入操作,或者在进行写入操作的同时读取文件原有内容 |
r+ | 可读可写模式。打开后可以读取和编辑文件内容 |
w+ | 可写可读模式。打开后如果文件存在会清空文件内容,如果不存在则会创建新文件,并且可以读取和写入文件 |
a+ | 可追加可读模式。打开后可以在文件末尾追加新内容,并且可以读取和编辑文件 |
注意:在使用open()函数的时候,可以同时设置多个模式,例如"rb"表示以二进制模式打开文件并只读取文件内容
7.4 关闭文件
关闭文件语法:
文件对象.close()
注意:如果忘记关闭文件,会造成系统资源消耗,而且会影响到后续对文件的访问。
7.5 查看文件对象的属性
需要获得的属性 | 方式 | 作用 |
---|---|---|
name | 文件对象.name | 返回被打开文件的访问模式 |
mode | 文件对象.mode | 返回文件的名称 |
closed | 文件对象.closed | 如果文件已被关闭返回True,否则返回False |
例如:
>>>f = open('test.txt',mode='r')
>>>print(f) # f 接收打开的文件对象
<_io.TextIOWrapper name='test.txt' mode='r' encoding='cp936'>
>>>print(f.mode)
r
>>>print(f.name)
test.txt
>>>print(f.closed)
False
>>>f.close()
>>>print(f.closed)
True
7.6 with关键字
基本语法:
with open() as 文件对象名:
pass/文件操作
用于文件的读写,省去了关闭文件的麻烦
注意:with
关键字在文件操作完后会自动关闭
7.7文件读写
7.7.1 读文件
1.文件对象.read(num)
num表示要从文件中读取的数据长度(单位值字节),如果没有传入num或为负数,那么就表示读取文件中所有的数据。
2.文件对象.readline()
readline()一次读取一行内容。
3.文件对象.readlines()
readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。
例如:
>>># 文件对象.read(num)
>>>with open('test.txt') as f:
>>> print(f.read()) # 可以获取文件的所有内容
# 读取的文件中有中文,会出现报错或者乱码
10001 灏忔槑 # 正确的应该是10001 小明
10002 灏忕孩 # 正确的应该是10002 小红
10003 灏忓ぉ # 正确的应该是10003 小天
>>> print(f.read(-1)) # 可以获取文件的所有内容
10001 灏忔槑
10002 灏忕孩
10003 灏忓ぉ
>>> print(f.read(2)) # 设置了num值,就只能够读取num个字符
10
>>># 文件对象.readline()
>>>with open('test.txt') as f:
>>> print(f.readline()) # 一次读取一行
10001 灏忔槑
>>> print(f.readline())
10002 灏忕孩
>>># 文件对象.readlines()
>>>with open('test.txt') as f:
>>> data = f.readlines()
>>> print(data)
['10001 灏忔槑\n', '10002 灏忕孩\n', '10003 灏忓ぉ']
>>> # 将读取的数据进行输出,跟文件原本的格式一样
>>> for i in data:
>>> print(i.rstrip('\n')) # rstrip() 删除字符串右边的字符
10001 灏忔槑
10002 灏忕孩
10003 灏忓ぉ
7.7.2 编码
在读取的文件中有中文,会出现报错或者乱码,我们可以通过设置编码来解决报错、乱码。
解决办法:设置encoding
例如:
>>>with open('test.txt',encoding='utf-8') as f: #这里的'test.txt'
>>> print(f.read())
10001 小明
10002 小红
10003 小天
>>>f = open('test.txt',encoding='utf-8')
>>>print(f.read())
10001 小明
10002 小红
10003 小天
7.7.3 写文件
写文件的语法:
文件对象.write(字符串)
w: 只写模式; 文件不存在则创建,存在则先清空再写入
>>>with open('test1.txt',mode='w') as f: #这里没有test1这个文件,所以会创建这个文件,注意这里默认是同目录下
>>> f.write('123')
这是同目录下就会出现test1.txt文件,打开test1文件可以看到
123
a:追加模式 如果文件存在在文件原本内容后面进行追加,如果文件不存在就会创建新的文件再进行内容写入
>>>with open('test1.txt',mode='a') as f:
>>> f.write('456')
打开test1文件可以看到
123456
注意:如果想让中间换行,只需在写入时加入换行符
7.7.4 读写文件
假设文件 “example.txt” 中原有如下内容:
Hello world!
我们现在想要读取该文件的同时在文件末尾添加一行文字 “This is a new line!”。可以使用如下代码:
with open("example.txt", "r+") as file:
content = file.read()
file.write("\nThis is a new line!")
print("文件内容:\n", content)
在上面的代码中,我们使用 r+
打开文件,读取文件内容并输出到控制台。然后在文件末尾添加了一行新文字,最后再次读取文件内容(已包含新添加的内容)。
运行上述代码后可以看到如下输出:
文件内容:
Hello world!
执行完文件写入操作后,需要将文件指针指回文件开头再次读取文件内容。所以在使用 +
号打开文件时,需要注意文件指针的位置。
7.7.5 文件指针
函数 | 使用语法 | 作用 |
---|---|---|
tell() | 文件对象.tell() | 显示文件内的当前位置 |
seek() | 文件对象.seek(偏移量(默认0),起始位置) | 用来移动文件指针 |
起始位置值:
值 | 位置 |
---|---|
0 | 文件开头 |
1 | 当前位置 |
2 | 文件结尾 |
例如:
>>>with open('test.txt',mode='r+',encoding='utf-8') as f:
>>> print('openfile:',f.tell())
openfile:0
>>> print(f.read())
10001 小明
10002 小红
10003 小天
>>> print('readfile:',f.tell())
readfile:40
>>> f.write('\n10004 小帅')
>>> print('writefile:',f.tell())
writefile:54
>>> f.seek(0,0) # 移动光标到文件的开头
>>> print('movepointer:', f.tell())
movepointer:0
>>> print(f.read())
10001 小明
10002 小红
10003 小天
10004 小帅
7.8 目录的常用操作
os模块
os模块是一个Python的系统编程的操作模块,os 模块提供了非常丰富的方法用来处理文件和目录。
导入os模块:
import os
注意:不导入os模块下面的操作均不能使用!!
常用操作:
名称 | 函数 | 解释 | 用法 |
---|---|---|---|
重命名 | rename(src,dst) | 重命名文件或目录 | os.rename("原名", "新名") |
删除文件 | remove(path) | 删除路径为path的文件 | os.remove(‘文件名路径’) |
创建文件夹 | mkdir(path) | 创建一个名为path的文件夹 | os.mkdir(‘文件夹名’) |
删除空目录 | rmdir(path) | 删除path指定的空目录 | os.rmdir("新建文件夹") |
返回目录下的文件等 | listdir(path) | 返回指定目录下的所有文件和目录名 | print(os.listdir('文件夹名')) |
返回目录 | getcwd() | 返回当前工作目录 | print(os.getcwd()) |
八、异常、模块和包
8.1 异常
当Python检测到一个错误时,解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的"异常”。
异常解释:
Traceback—异常的追踪信息,可以追溯到程序异常的具体位置
xxxxError—异常类型,后面包含异常具体信息
常见异常:
异常 | 异常名称 | 异常解释 |
---|---|---|
NameError | 名称异常 | 当使用未定义的变量或函数时会引发该异常 |
TypeError | 类型异常 | 当使用不适当类型的对象时会引发该异常 |
IndexError | 索引异常 | 当索引超出范围时会引发该异常 |
ValueError | 值异常 | 当使用不正确的值时会引发该异常,例如将字符串类型的字母尝试转换为数字类型 |
ZeroDivisionError | 除零异常 | 当尝试用零除以另一个数字时会引发该异常 |
FileNotFoundError | 文件打开异常 | 当尝试打开不存在的文件时会引发该异常 |
不同的异常类型对应着不同的解决方案。以下是对之前提到的那些异常类型的解决方案:
NameError:在使用变量或函数之前,请确保它们具有正确的命名。如果出现 NameError,最常见的情况可能是在变量名或函数名中拼写错误或者没有对变量或函数进行定义。
TypeError:当出现 TypeError 时,要特别注意要使用的变量和数据类型。如果出现 TypeError,可能需要制定正确的类型或对表达式进行转换来确保类型匹配。
IndexError:当使用列表或数组等索引时,请确保不会超出索引范围。如果困惑,可以打印出正在使用的变量以及它们的长度或者使用内置函数 len() 来获取变量的长度,来进行索引操作。
ValueError:在使用构造函数、转换或输入函数之类的函数时,请检查它们是否接受正确的参数。例如,如果使用 int() 函数将字符串转换为整数,并且字符串中包含非数字字符,则会引发 ValueError。
ZeroDivisionError:在使用除法操作时,请确保不会将值除以零。可以通过在操作之前检查分母是否为零来避免 ZeroDivisionError。
FileNotFoundError:在尝试打开文件时,请确保文件存在并且您具有读取权限。可以使用 Python 的 os 模块来检查文件是否存在,并在不存在时进行相应的处理。
8.2 捕获异常
在编程中,捕获异常是指在程序运行过程中遇到异常情况时,使用try…except语句捕获并处理这些异常。这样可以让程序在遇到异常情况时不崩溃,而是能够继续运行下去,或者按照我们预先设定的方式来处理这些异常情况。
捕获异常语法:
try:
可能引发异常现象的代码。不确定是否能够正常执行的代码
except:
出现异常现象的处理代码。检测到异常执行的代码
else:
未出现异常现象的处理代码
finally:
try代码块结束后运行的代码
**异常的传递:**当函数/方法执行出现异常,会将异常传递给函数/方法的调用处。如果没有异常处理,程序就会被终止。
>>>try:
>>> print('1'+12) # 类型不一致,不能进行+操作
>>> print(1/0)
>>>except TypeError:
>>> print('类型不一致,不能进行+')
>>>except ZeroDivisionError:
>>> print('除数不能为0')
类型不一致,不能进行+
>>>try:
>>> print(1+'12')
>>> print(1/0)
>>>except (TypeError,ZeroDivisionError): #上述代码合并
>>> print('这一个错误')
这一个错误
>>>funa(x,y):
>>> print(x/y)
>>>try:
>>> funa(1,0)
>>>except ZeroDivisionError as zero: # as 别名 将捕获异常信息赋值给别名# 将捕获的异常详细信息赋值给zero
>>> print('除数不能为0')
>>> print(zero)
除数不能为0
division by zero
# 捕获所有异常 -- Exception
>>>try:
>>> print(a)
>>>except Exception as e: # 万能异常:Exception 可以捕获任意异常
>>> print(e)
name 'a' is not defined
>>>number = input('请输入一个数字')
>>>try:
>>> number = int(number)
>>>except Exception as e:
>>> print(e)
>>>else:
>>> print(f'输入正确:{number}')
>>>finally:
>>> print('try执行完毕')
请输入一个数字 10
输入正确:10
try执行完毕
#若输入的不是数字
请输入一个数字 a
invalid literal for int() with base 10: 'a'
try执行完毕
8.3 抛出异常
抛出异常格式:
raise 异常名称(异常描述信息)
注意:抛出异常必须要有捕获异常
>>>def funa(x, y):
>>> if y!=0:
>>> print(x / y)
>>> else:
>>> raise ZeroDivisionError('除数不能为0')
>>>try:
>>> funa(1,2)
>>> funa(1,0)
>>>except ZeroDivisionError as zero:
>>> print(zero)
0.5
除数不能为0
8.4 模块
为了使代码更容易维护,提高代码重用价值,可以将一组相关功能的代码写入一个单独的py文件中供别人导入使用,这个.py文件就被称作是一个模块
8.4.1模块分类
第一类:内置模块,也叫做标准库。此类模块就是python解释器给你提供的。
第二类:第三方模块,第三方库。一些别人写的非常好用的模块,必须通过安装模块才能使用。
第三类:自定义模块。我们自己在项目中定义的一些模块(py文件)。
8.4.2 下载第三方库
下载方法一:使用pip
Windows+r -输入’cmd’ - 输入 pip install 模块名
#pip–包管理工具,随python一起安装的
下载方法二: 在pycharm中下载
file - settings - project:项目名 - python interpreter- ‘+’
8.4.3 导入模块
import 模块名
import 模块名 as 别名
import 模块名1,模块名2
from 模块名 import 函数名 [as 别名]
from 模块名 import *
from…import | import |
---|---|
from…import这样的格式来进行模块的某个功能的导入 | import导入某个模块的整个功能 |
8.4.4 ____name____变量
____name____就是标识模块的名字的一个系统变量。可以显示一个模块的某功能是被自己执行还是被别的文件调用执行,换一种说法也就是表示当前程序运行在哪一个模块中。
8.5 包
概念:
包就是项目结构中的文件夹/目录
包含有____init____.py文件的文件夹
作用:
包用于将有联系的模块进行分类管理, 包本身不具有具体的含义, 仅用于文件分层管理
init.py
在Python工程里,当python检测到一个目录下存在____init____.py文件时,python就会把它当成一个模块(module)
注意:init.py可以是一个空文件
____all____变量
Python是通过____all____这个内置的变量来决定哪些内容会被外部导入。
____all____是一个拥有全局作用域的变量,存储的值为列表形式,里面的元素为标识符(变量名、函数名、类名等),标识符必须用引号括起来。
最后
如果你觉得这篇文章对你有用,建议点赞收藏。
欢迎各位读者指正错误,请在评论区留言。或者发表自己的看法,小编不胜感激。