python进阶——类(一)面向对象、类和实例
本文最后更新于:1 年前
¶(一)面向对象
¶1. 基本概念:
面向对象编程——Object Oriented Programming(OOP),是一种程序的设计思想,以对象作为程序的基本单元,在对象中封装数据和操作数据的函数。
和把程序作为一系列函数命令集合运行的面向过程不同,面向对象把函数继续分解为子函数,简化程序设计、降低系统复杂度。
对于众多的子函数,面向对象的程序设计把计算机程序视为一组对象的集合,不同的对象具有不同的属性和功能,这些子函数就被用在不同的对象之中。进而,程序的执行就是对象之间处理交互信息、完成自身功能的过程。
¶2. 使用技术:
下面简要说明面向对象的常用技术(后面会详细介绍):
-
类:用于描述具有相同的属性和方法的对象的集合,定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
类把数据和功能绑定在了一起,创建新类就是创建新的对象类型,从而以类为蓝图创建该类型的新的实例,也就是对象。
-
**实例化:**创建一个类的实例,类的具体对象,实例支持维持自身状态的属性以及修改自身状态的方法。
-
**对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
-
**方法:**在类中定义的函数,每个对象都能够使用。
类似于c++中的成员函数
-
**类变量:**类变量是一个类的所有不同对象/实例共享的变量,在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外,且通常不作为实例变量使用。
类似于c++中的静态变量,只有一份。
-
**实例变量:**类声明时的属性用变量表示,此类变量称为实例变量,实例变量就是一个用self修饰的变量。
实例变量可以用于区分不同的实例,因此,实例变量每个实例都各自拥有,相互独立。
类似于c++中的非静态数据成员
-
**数据成员:**类变量或者实例变量用于处理类及其实例对象的相关的数据。
和c++中的数据成员概念类似。
-
**局部变量:**只定义在方法中的变量,只作用于当前实例的类。
-
**继承:**即一个派生类继承基类的成员和方法。继承允许多级,例如A是B的基类,B仍可以是C的基类,此时A实际上也可以算是C的基类,继承可以形成由树组成的森林。
-
**方法重写:**如果从基类继承的方法不能满足派生类的需求,可以对其进行改写,这个过程叫方法的覆盖,也称为方法的重写。
¶3. Python的面向对象:
Python是一门面向对象的语言,这也是设计Python语言的初衷。
Python中提供了类机制,可以定义类,而且Python的类提供了面向对象编程的所有基本功能和标准特性:类的继承机制支持多个基类,派生类可以覆盖基类中的方法,类的方法可以调用基类中的同名方法,对象可以包含任意数量和类型的数据。
类机制也支持Python的动态特性:在运行时创建,创建后可以修改。
和其他编程语言相比,Python 的类只使用了很少的新语法和语义。
-
如果了解c++类的概念,这里为了防止混淆进行一些说明(不了解的可以跳过):
Python中的类成员通常为公有的(public),成员函数默认均为虚函数(virtual)。
Python的方法函数在声明时有一个显式的参数代表本对象,在调用时隐式提供,类似this指针。
和c++不同,Python中内置类型可以用作基类以供用户扩展。
和c++相同,算术运算符、下标等具有特殊语法的内置运算符可以为类实例而重新定义(运算符重载)。
下面正式介绍Python的类。
¶(二)Python中的类和实例
面向对象最重要的概念就是类和实例,所以,Python中首先必须要知道的一点:**类是创建对象的模版,对象是类的实例。**类本身不占据内存空间,实例才会占据内存空间。
¶1. Python的类定义和实例化:
¶(1)简单定义:
Python中,类通过class关键字来定义,简单的类的语法格式为:
class ClassName[(object)]: <statement-1> . . . <statement-N>
ClassName是类名,类名的命名一般情况要求是名词,使用首字母大写。
object是继承的类名,如果没有则一律使用object代替,当然对于这种情况也可以不加()来表示继承关系。更详细的内容后文介绍
<statement-i>是定义/声明语句,后文介绍。
与函数定义一样,类定义必须先执行才能生效。
进入类定义后会创建新的局部命名空间和局部作用域,对局部变量的赋值和处理也都是在此命名空间之内。
下面简单定义一个没有属性和方法的类:
1 |
|
¶(2)类对象:
在正常离开类定义后,会创建一个类对象。
类对象支持两种操作:属性引用和实例化。
-
属性引用使用Python中的标准属性引用语法:obj.name,obj是对象,name是属性名。
类对象创建时存在于类命名空间中的全部名称均为有效的属性名,包括数据属性和方法。
可以通过属性名对类属性进行赋值来修改其值。
属性引用后文介绍例子。
-
实例化和Python中的函数调用类似,可以将类对象视为一个不带参数的函数,该函数的返回值就是类的一个实例,以此方式来完成类的实例化。
实例化操作创建类的新实例,将此实例后的对象分配给某个变量,此时的对象是一个空对象。
如果想要自定义实例化对象,使其带有一定的初始状态,需要使用类定义的特殊方法__init__(),后文会详细介绍。
类实例化后才可以使用其属性和方法——其访问方式是通过上面的属性引用完成的。
简单的类对象实例化如下:
1 |
|
¶(3)属性和方法:
上面创建的类显然是不完整的,需要为类设置属性和方法来定义类的功能,从而完善一个类。
注意,属性是数据属性,和方法一起都是属性引用的内容。
-
属性:作为数据属性,其实际是类中的变量,包括了类属性和实例属性。
属性的添加可以在类中实现,也可以对某个实例对象单独实现。
-
方法:实际是类中的函数,包括了类方法和实例方法。
类中的方法对应的函数和普通函数的最大区别就是,类方法必须多出一个参数,调用方法的实例对象必须作为第一个参数被传入其中,这是为了区别一个类中的多个对象,便于解释器查找是哪一个对象调用的方法。
在类方法的定义中,额外的第一个参数名称一般默认为self,随后再根据需要定义参数——self代表的是类的实例,而不是类。
而在调用时,第一个额外参数self是不能指定的,类方法会隐性传入这个参数——所以,对于类方法,至少有一个参数,而如果类方法有n个参数需要传入,实际调用参数数量为n-1。
方法定义中的类属性需要指定类名.类属性来使用,实例属性需要指定第一个参数名.实例属性名来使用。
-
初始属性和类的构造方法:
对于类属性,是在类中直接定义的,但是对于实例属性,由于其对每个实例来说是不同的,又需要统一名称,所以,实例属性在类中的添加一般不是创建一个个实例后一个个指定的,而是利用类的构造方法完成添加的,类的构造方法允许所有实例的相同实例属性使用相同的实例属性名。
类的构造方法是一个特殊方法,名为__init__,该方法在类实例化时会自动调用。
__init__方法除了self参数外可以增加任何数量的参数,这些参数被称为初始化参数,会作为值被赋给在该方法中定义的实例属性,当然也可以直接为实例属性指定值。
__init__方法和c中的构造函数十分相似,但不像c的构造函数,它不一定是必要的。
-
类属性的定义:
类属性在类中直接按局部变量的定义处理即可,且对于定义后的类方法,可以直接使用类属性。
类属性因为其共享性,允许不通过实例进行访问,直接通过类名访问。
类属性除了用户定义的属性以外,实际上还有一部分的内置类属性,这些属性是解释器在新建类时自动创建的,用于保证类能够正常运行,上面的__init__也是内置类属性(这里是指方法名)。
下面列举几个常见的属性:
部分常用专有属性 说明 触发方式 __init__ 构造初始化函数 创建实例后,赋值时使用,在__new__后 __new__ 生成实例所需属性 创建实例时 __class__ 实例所在的类 实例.__class__ __str__ 实例字符串表示,可读性 print(类实例),如没实现,使用repr结果 __repr__ 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例)) __del__ 析构 del删除实例 __dict__ 实例属性和值的键值对 vars(实例.__dict__) __doc__ 类文档,子类不继承 help(类或实例) 另外注意,实例属性访问优先级比类属性高,可以用实例属性屏蔽掉类属性;实例无法直接用赋值的方法修改类属性,因此这实际上是给实例添加了一个同名的新实例属性。
¶2. 类定义及实例化和使用的例子:
首先,定义一个People类,其类属性为人数个数,其初始实例属性包括姓名、年龄、身高、体重、性别,为某些实例对象单独定义血型属性;People类的方法包括输出个人信息(不包括血型)、计算BMI、判断是否成年、输出当前人数。
1 |
|
下面通过几个例子来展示具体的运行逻辑:
1 |
|
1 |
|
1 |
|
¶3. 一切皆对象:
当学习了类的基本内容之后,可以发现,不管是之前的数据、函数,还是现在的类,全都都是对象,Python中的一切皆为对象,都可以用type函数判断类型,用dir函数查看其属性和方法,用help查看其帮助文档。
对于变量,Python允许对实例变量绑定任何数据,也就是说,两个不同的实例变量,虽然都是同一个类的不同实例,但拥有的变量名称都可能不同。
不同的变量名,可以绑定到一个对象上,通过任何一个变量修改对象,会影响到所有指向该对象的所有变量。
具体的例子,请结合python基础——函数(二) - ZZHの个人博客 (07xiaohei.com)中的参数传递理解。