理解Python面向对象
Python中的类
在Python中。类使用class语句来定义。在类的代码中包含了一系列语句,用赋值语句创建变量,用def定义函数等。Python中的类只要有下面几个特点:
- 一个类可以有多个实例对象,每个实例对象拥有自己的命名空间。
- 类支持继承,通过继承对类进行扩展。
- 支持运算符重载。通过内置的特定方法,可以使类的对象支持内置类型的各种运算。
Python中的对象
类对象
类对象具有如下特点:
- Python执行class语句时创建一个类对象和一个变量(名称就是类名称),变量引用对象。导入类模块时,class语句被执行,创建类对象。
- 类的数据(变量)用“对象名.属性名”格式来访问。
- 类的方法属性用“对象名.方法名()”格式来访问。
- 类的数据(变量)和方法由所有实例对象共享
实例对象
实例对象具有如下特点:
- 实例对象通过调用类对象来创建。
- 每个实例对象继承类对象的属性,并获得自己的命名空间。
实例对象拥有私有属性。类的方法函数的第一个参数默认为self,表示引用方法对象的实例。在方法中对self属性赋值才会创建属于实例对象的属性。
定义和使用类
类定义的基本格式如下:
1 | class 类名: |
各种语句的先后顺序没有关系。
定义类
下面的代码演示了定义了一个testclass类:
1 | class testclass: |
使用类
1 | type(testclass) #输出<class 'type'> Python的所有类对象都是type类型 |
对象的属性和方法
对象的属性
在Python中实例对象继承了类对象的所有属性和方法,可以通过dir()方法来查看。
1 | dir(testclass) # 输出['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'data', 'setpdata', 'showpdata'] |
以双下划线开始和结尾的变量名属于内置属性。
共享属性
类对象的数据属性是全局的,即默认情况下它属于类对象,并可以通过实例变量来引用。例如:
1 | x = testclass() |
在没有给实例对象赋值时,它引用的是类对象的同名属性。给实例对象的属性赋值时,使其引用了新对象,不再与类对象的同名属性相关联。所以出现了x的data属性值与testclass和y的属性值不一样。实例对象和类对象的关系如下所示:
实例对象的“私有”属性
实例对象的“私有”属性指类的函数中以”self.属性值=值”格式进行赋值创建的属性。“私有”属性只属于当前实例对象。例如:
1 | x = testclass() |
对象的属性具有动态性
对于类对象或者实例对象而言,当给不存在的属性赋值时,Python为其创建属性。例如:
1 | x = testclass() |
可以看出实例对象也自动拥有了为类对象动态添加的属性。
对象的方法
实例对象没有自己的方法,只是通过继承的方法名变量来引用属于类对象的方法。通过实例对象调用方法时,当前实例对象作为一个参数传递给方法,所以在定义供实例对象调用的方法时,通常第一个参数名称为self(也可以用其他的名称)。
例如:
1 | class test: |
可以看出类对象可以调用类中的所有方法,只是需要记住参数个数。在执行“test.add2(2,3,4)”时第一个参数在函数中未使用。
类的”伪私有”属性和方法
在模块中使用双下划线作为变量名前缀,可以避免变量在使用from…import*语句时被导入。类似地,在类中使用双下划线作为变量名前缀,这些变量名不能直接在类外使用。例如:
1 | class test: |
可以看出类对象不能直接访问双下划线作为变量名前缀的属性和方法。之所以成为”伪私有”,是Python在处理这类变量名时自动在带双下划线前缀的变量名前加上”_类名”。比如上面的例子可以通过test._test__data2访问__data2变量,test._test__sub访问__sub方法
构造函数和析构函数
类的构造函数和析构函数名称是由Python预设的,__init__为构造函数名,__del__为析构函数名。构造函数在调用类创建实例对象时自动被调用,完成对实例对象的初始化。析构函数在实例对象被回收时调用。在定义类时,可以不定义构造函数和析构函数。
类的继承
通过继承,子类(新类)可以获得父类(超类)的属性和方法。在子类中可以定义新的属性和方法,从而实现对父类的扩展。基本代码格式如下:
1 | class 类名(超类名): #如果超类有多个用,分隔 |
简单继承
下面的代码定义一个空的子类来说明子类继承了超类的所有属性和方法。
1 | class supper_class: |
定义子类的属性和方法
如果子类定义的方法和属性与父类的方法和属性重名,那么子类实例对象调用子类中定义的方法和属性。这在java中叫方法重写。例如:
1 | class supper_class: |
Python中允许子类方法中通过类对象直接调用父类的方法。例如:
1 | class sub_class(supper_class): #定义子类 |
调用超类的构造函数
在使用构造函数对实例对象进行初始化时,可以在子类的构造函数中调用超类的构造函数。例如:
1 | class supper: |
多重继承
多重继承指子类可以同时继承多个超类。如果超类中存在同名的属性或方法,Python按照从左到右的顺序在超类中搜索,如果左边的超类中找到了就不再从右边找。例如:
1 | class supperone: |
运算符重载
重载运算符就是在类中定义相应的方法,当使用实例对象执行相关运算时,则调用对应方法。
方法 | 说明 | 何时调用方法 |
---|---|---|
__add __ | 加法运算 | 对象加法:x+y、x+=y |
__sub __ | 减法运算 | 对象减法:x-y、x-=y |
__mul __ | 乘法运算 | 对象乘法:x*y、x*=y |
__div __ | 除法运算 | 对象除法:x/y、x/=y |
__mod __ | 取模运算 | 对象取模:x%y、x%=y |
__contains __ | 成员测试 | item in x |
__getitem __ | 索引、分片 | x[i]、x[i:j]、没有__iter__的for循环等 |
__setitem __ | 索引赋值 | x[i]=值、x[i:j]=序列对象 |
__len __ | 求长度 | len(x) |
__iter __、__next __ | 迭代 | Iter(x)、next(x)、for循环等 |
加法运算符重载
1 | class test: |
__getitem__方法
在对实例执行索引、分片或for循环时,调用__getitem__方法。例如:
1 | class test: |
setitem方法
在通过赋值语句给索引或分片赋值时,调用__setitem__方法,实现对序列对象的修改。例如:
1 | class test: |