💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、豆包、星火、月之暗面及文生图、文生视频 广告
[TOC] ---- # Python3 面向对象 ## 什么是面向对象: 面向对象是现代语言必备的一个特性。传统的面向过程编程(模块封装都是通过函数实现的,类似于C语言),不符合人类思考的一种方式。比如人吃饭这种行为,如果使用面向过程编程,那么是首先定义一个吃饭的行为,然后再把这个人传过去。这明显是不符合人类的思考方式的,人类正常的思考方式应该是,吃饭这个行为是属于人类的,应该由人的这个对象自己去执行。这种思考方式的转变,就是面向对象。面向对象把所有属于某种物体(比如人)的属性和行为全都定义在这个物体上,比如年龄,身高,体重这些是属性,比如招手,跑步,吃饭这些是行为。以后要知道一个人的年龄,应该查看这个人的年龄属性,要让这个人奔跑,应该调用这个人的奔跑方法。这种思考方式就是面向对象。 ## 面向对象技术简介: * 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 * 方法:类中定义的函数。唯一的不同在于我们还拥有一个额外的 `self` 变量。 * 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。 * 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。 * 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。 * 局部变量:定义在方法中的变量,只作用于当前实例的类。 * 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。 * 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。 * 实例化:创建一个类的实例,类的具体对象。 * 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。 ## self 代表对象本身(实例化到后的对象) 类方法与普通函数只有一种特定的区别——前者必须多加一个参数在参数列表开头,这个名字必须添加到参数列表的开头,但是你不用在你调用这个功能时为这个参数赋值,Python 会为它提供。这种特定的变量引用的是对象本身,按照惯例,它被赋予 self 这一名称。 尽管你可以为这一参数赋予任何名称,但是强烈推荐你使用 self 这一名称——其它的任何一种名称绝对会引人皱眉。使用一个标准名称能带来诸多好处——任何一位你的程序的读者能够立即认出它,甚至是专门的 IDE(Integrated Development Environments,集成开发环境)也可以为你提供帮助,只要你使用了 self 这一名称。 >Python 中的 self 相当于 C++ 中的 this 指针以及 Java 与 C# /php中的 this 引用。 你一定会在想 Python 是如何给 self 赋值的,以及为什么你不必给它一个值。一个例子或许会让这些疑问得到解答。假设你有一个 MyClass 的类,这个类下有一个实例 myobject。当你调用一个这个对象的方法,如 myobject.method(arg1, arg2) 时,Python 将会自动将其转换成 MyClass.method(myobject, arg1, arg2)——这就是 self 的全部特殊之处所在。 这同时意味着,如果你有一个没有参数的方法,你依旧必须拥有一个参数——self。 ## 类和对象的使用: 1. 类:定义一个类的语法是使用class关键字,开发者自己定义的类,必须继承自object类,object类是Python中所有的类的基类。类中所有的方法都要以self作为第一个参数传递进去,这是规定。self代表的是当前的对象。 ```python #第一个类的例子 class Dog(): name = 'xiaohui' age = 2 def getGogInfo(self): print('name:%s' % self.name) print('age:%s' % self.age) ``` 2. 对象:对象是对类的一个实例化,对象的创建方式如下(比如要实现一个Person的对象): ```python # 实例化类 dog=Dog() dog.getGogInfo() # 打印结果: # name:xiaohui # age:2 ``` ## 构造函数和实例属性: 构造函数是Python类在初始化对象的时候会调用的方法。一般在这个方法中给一些属性进行初始化。 类有一个名为 __init__() 的特殊方法(构造方法),该方法在类实例化时会自动调用,像下面这样: 当然, __init__() 方法可以有参数,参数通过 __init__() 传递到类的实例化操作上。例如: self代表类的实例,而非类 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。 一般就是用来初始化实例属性的: ```python class Complex: def __init__(self, realpart, imagpart): self.r = realpart self.i = imagpart x = Complex(3.0, -4.5) print(x.r, x.i) # 输出结果:3.0 -4.5 ``` 给person1这个对象绑定name属性 ```python class Person(): def __init__(self,name,age): self.name = name self.age = age # 给person1这个对象绑定name属性 person1 = Person('zhiliao',18) print(person1.name,person1.age) #输入结果: zhiliao 18 ``` 给对象添加实例属性非常简单,以上是通过self.name的方式实现的,其实也可以通过以下方式实现,原理都是一样的。 ```python # 类,对象 # 实例化 # 类最基本的作用:封装 #第一个类的例子 class Student(): sum = 0 #类变量 name = '' #实例化变量赋值 age = 0 #实例化变量赋值 # 构造函数 def __init__(self,name,age): # 实例化变量 # 初始化对象的属性 self.name = name #给实例化变量赋值 self.age = age def getGogInfo(self): print('name:%s' % self.name) #调用实例化类变量 print('age:%s' % self.age) #调用实例化类变量 self.__class__.sum += 1 #操类变量计数 print('sum的值%a'%self.__class__.sum) student = Student('lisi',18) student.getGogInfo() # 打印: # name:lisi # age:18 # sum的值1 # name:wangwu # age:20 # sum的值2 ``` ## 类变量与对象变量 我们已经讨论过了类与对象的功能部分(即方法),现在让我们来学习它们的数据部分。数据部分——也就是字段——只不过是绑定(Bound)到类与对象的命名空间(Namespace)的普通变量。这就代表着这些名称仅在这些类与对象所存在的上下文中有效。这就是它们被称作“命名空间”的原因。 字段(Field)有两种类型——`类变量`与`对象变量`,它们根据究竟是类还是对象拥有这些变量来进行分类。 **类变量(Class Variable)** 是共享的(Shared)——它们可以被属于该类的所有实例访问。该类变量只拥有一个副本,当任何一个对象对类变量作出改变时,发生的变动将在其它所有实例中都会得到体现。 **对象变量(Object variable)** 由类的每一个独立的对象或实例所拥有。在这种情况下,每个对象都拥有属于它自己的字段的副本,也就是说,它们不会被共享,也不会以任何方式与其它不同实例中的相同名称的字段产生关联。上面一个例子可以帮助你理解. **例子:** ```python class Dog(): name = 'xiaohui' age = 2 def getGogInfo(self): print('name:%s' % self.name) print('age:%s' % self.age) #实例一 dog = Dog() print(dog.age) # 实例调用 Dog.age+=2 # 类调用 print(Dog.age) #类调用 #实例二 dog2 = Dog() print(dog2.age) Dog.age+=2 #类调用 print(Dog.age) # 类调用 # 打印结果: # 2 # 4 # 4 # 6 ``` ## 类方法(cls代表类本身)与对象方法 **类方法** 是类里用装饰器`@classmethod`声明的方法,指的是公用的方法 **对象方法** 是实例化类时,实例里的普通方法.此实例里自己的方法 ```tython class Student(): #类变量 sum = 0 name = '' age = 0 # 构造函数 def __init__(self,name,age): # 实例化变量 # 初始化对象的属性 self.name = name self.age = age #实例方法 def getGogInfo(self): print('name:%s' % self.name) print('age:%s' % self.age) self.sum += 1 print('sum的值%a'%self.sum) #通过装饰器来定义类方法 @classmethod def plus_sum(cls): cls.sum += 1 print(cls.sum) student1 = Student('lisi',18) # 类调用类方法 Student.plus_sum() # 输出: 1 # 实例调用类方法(不推荐这么使用,不容易理解) student2 = Student('wangwu',20) student2.plus_sum() # 输出: 2 ``` ## 类的外部调用和内部调用 **类的外部** 实例化对象后,调用. **内部调用** 类内部调用属性和方法. ```python class Student(): #构造函数 def __init__(self,name,age): # 实例化变量 # 初始化对象的属性 self.name = name self.age = age self.score = 0 def marking(self,score): if score < 0: return '不能给别人打负分' self.score = score self.get_name() # 我是内部调用类内的变量name:lisiname 类的内部调用 return '%s本次考试的分数为:%d'%(self.name,self.score) def get_name(self): print('我是内部调用类内的变量name:%sname' % self.name) student1 = Student('lisi',18) result=student1.marking(60) #外部调用 print(result) #lisi本次考试的分数为:60 result=student1.marking(-1) print(result) #不能给别人打负分 #不推荐这样在外部直接修改类内部的变量,要通过类内部的方法进行验证之后再去操作相应的变量 student1.score = -1 print(student1.score) # -1 # 输出: # 我是内部调用类内的变量name:lisiname # lisi本次考试的分数为:60 # 不能给别人打负分 # -1 ``` ## 访问限制: 1. 受保护的属性和方法: 有时候在类中的属性或者是方法不想被外界调用,但还是可以被外界所调用,那么就叫做受保护的属性或者方法。受保护的属性或者方法,使用一个下划线开头: ```python class Person(object): def __init__(self): self._age = 19 p1 = Person() # 以下代码可以打印出_age,但这样做是违背开发者意愿的。 print(p1._age) #19 ``` 2. 私有属性和方法: 有时候在类中的属性或者方向不让外界调用,那么就可以使用定义成私有属性或者私有方法。私有属性或者方法使用两个下划线开头: ```python class Person(object): def __init__(self): self.__age = 19 p1 = Person() # 以下代码将报错 print(p1.__age) ``` 3. 更多: * 私有方法或者属性不是说100%不能访问,以上方式,可以通过_Person__age来访问,但这样做是不推荐的。 * __init__这些方法不是私有方法,是特殊变量或方法。 ## 析构函数和引用计数: 1. `Python`中的类也有析构函数,也即`__del__`方法. 只要这个对象在内存中即将被消灭的时候,就会调用这个方法。 ```python class Person(object): def __del__(self): print('我即将被消灭了~') p1 = Person() # 调用完这个代码后,就会去执行Person类中的__del__方法 # 打印: 我即将被消灭了~ ``` 2. 引用计数: Python中的对象是使用引用计数的方式实现的。也即如果没有任何对象引用到一块内存,那么Python将会把这块内存回收。看以下代码: ```python class Person(object): def __del__(self): print('我即将被消灭了~') p1 = Person() p2 = p1 del p1 # 先会打印===== print('======') # 再会去执行__del__方法 # 打印: # ====== # 我即将被消灭了~ ``` ## 继承和重写父类的方法: **继承** 可以使用其他类当作自己的父类,那么父类中的方法和公有属性都可以被子类使用。继承的好处是可以让子类节省代码,实现更多的功能。 **重写父类的方法** 有些时候,父类中的方法不一定适合子类,那么这时候可以重写父类的方法,而使用子类自己定义的方法。 子类`demo1.py` ```python from demo2 import Human #引入父类里的Human类 class Student(Human): #继承父类Human #类变量 sum = 0 #构造函数 def __init__(self,school,name,age): # 实例化变量 # 初始化对象的属性 Human.__init__(self,name,age) #类调用实例方法灯给父类的初使化传值 def get_name(self): self.name='wangwu' print(self.name) print(Student.sum) # 输出0 继承父类里的sum 通过类来调用变量 student1 = Student('人民大学','lisi',18) #实例化时要给父类里的init传值 print(student1.sum) # 输出0 继承父类里的 通过对象调用变量 print(student1.name) # 输出lisi 继承父类里的 通过对象调用变量 print(student1.age) # 输出18 继承父类里的 通过对象调用变量 student1.get_name() # 输出wangwu 继承并重写父类里的方法 通过对象调用方法 ``` 父类`demo2.py` ```python class Human(): sum = 0 def __init__(self,name,age): self.name = name self.age =age def get_name(self): print(self.name) ```