python面向对象编程
面向对象的概念:
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。直接通过类名.属性访问的就是类成员。定义成员在方法外面定义。
- 数据成员:类变量或者实例变量用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。类似于java的重写
- 实例变量:定义在方法中的变量,只作用于当前实例的类。调用对象.属性访问的就是对象的变量。写在构造方法里面的成员
- 继承:即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制。
Python中的类提供了面向对象编程的所有基本功能:类的继承机制允许多个基类,派生类可以覆盖基类中的任何方法,方法中可以调用基类中的同名方法。
对象可以包含任意数量和类型的数据。
其规则如下:
1. Python的构造函数是不能重载一个类只能有一个构造函数存在。定义多个构造方法时,实例化类只实例化最后的构造方法,即后面的构造方法会覆盖前面的构造方法,并且会根据最后一个构造方法的形式进行实例化。 2. 类方法和实例方法: 类方法类似于java的static 方法,类方法需要增加 @staticmethod 修饰;实例方法第一个参数必须为self 对象自身; 如果不加 @staticmethod 修饰默认是实例方法。(当然可以通过实例访问类方法) 3. 类属性和实例属性 类变量在整个实例化的对象中是公用的, 类似于java 的static 变量, 可以通过 类.属性 进行访问, 也可以通过 实例.属性 进行访问(和java 的static 变量有区别的是修改实例的该属性不会影响类的该属性以及其他实例的该属性, 默认 实例.属性 = 类.属性)。 类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。 4. 单下划线、双下划线、头尾双下划线说明: (1) __foo__: 定义的是特殊方法,一般是系统定义名字 ,类似 __init__() 之类的。 是为了和python 的关键字区分。 (2) _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import * (3) __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。
例如:自定义一个类,包含属性与方法
# 面向对象练习 class ParentClazz: # 直接定义的类的变量,属于类(类似于java的static 变量, 唯一的区别是修改实例的该属性不会影响类的属性) name = 'zs' # 这种属于普通的属性,等价于name,是为了和python关键字区分开 __like__ = 'foods' # 单前置下划线, 私有化属性或方法,但是类对象和子类可以访问(类似于java的protected) _birthday = '2020-02-02' # 私有成员(类似于java的private,python中私有成员和私有方法用__开头,且不以__ 结尾)。声明该属性为私有,不能在类外部被使用或直接访问 __password = '123456' # 有参构造方法 def __init__(self, name, password, height, birthday): print("A的有参数的构造方法被调用") self.name = name self.__password = password self._birthday = birthday # 类实例才有的属性 self.height = height # 成员方法访问私有成员变量 def getPassword(self): return self.__password def getHeight(self): return self.height def getName(self): return self.name def getBirthday(self): return self._birthday # 类方法 @staticmethod def getLike(): return ParentClazz.__like__ # 重载方法(类似于java的toString()方法) def __str__(self): return "name:" + self.name + "\t" + "height:" + str(self.height) + "\t" \ + "password:" + str(self.__password)
客户端测试代码:
from qz.oop.ParentClazz import ParentClazz print(ParentClazz.name) print(ParentClazz.__like__) print(ParentClazz._birthday) # 如下获取属性会报错 type object 'ParentClazz' has no attribute '__password' # print(ParentClazz.__password) print("======1") par = ParentClazz("lisi", "123456", 30, "2022-02-02") print(par) print(par._birthday) print(par.height) print(ParentClazz.name) # 修改类属性 print("======2") ParentClazz.__like__ = "shi" print(ParentClazz.__like__) print(ParentClazz("lisi", "123456", 30, "2022-02-02").getLike())
结果:
zs foods 2020-02-02 ======1 A的有参数的构造方法被调用 name:lisi height:30 password:123456 2022-02-02 30 zs ======2 shi A的有参数的构造方法被调用 shi
注意:
(1)类的私有属性:两个下划线开头,声明该属性为私有,不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。
(2)类的私有方法
__private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类地外部调用。self.__private_methods。
(3)self与this代表对象自己,一般用self,类似于java的this关键字
(4)在类地内部,使用 def 关键字来定义一个方法,与一般函数定义不同,类方法必须包含参数 self, 且为第一个参数,self 代表的是类的实例。
类的专有方法:
-
__init__ : 构造函数,在生成对象时调用
-
__del__ : 析构函数,释放对象时使用
-
__str__( self ) toString 方法,
-
__cmp__ ( self, x ) 对象比较方法
-
__repr__ : 打印,转换
-
__setitem__ : 按照索引赋值
-
__getitem__: 按照索引获取值
-
__len__: 获得长度
-
__cmp__: 比较运算
-
__call__: 函数调用
-
__add__: 加运算
-
__sub__: 减运算
-
__mul__: 乘运算
-
__div__: 除运算
-
__mod__: 求余运算
-
__pow__: 乘方
-------------------------------类的继承:------------------------------------
- 继承单个类:
# B类继承A类 from qz.oop.ParentClazz import ParentClazz class SubClazz1(ParentClazz): # 在原有属性的基础上增加的属性 sex = "男" # 有参数构造 def __init__(self, name, sex): self.name = name self.sex = sex # 方法重写(覆盖) def getName(self): # super调用父类的方法 print(super().getName()) return "这里是SubClazz1类重写过的方法" # 重写方法,写完此方法打印对象不会打印地址 def __str__(self): return "name:" + self.name + "\t" \ + "sex:" + self.sex + "\t" \ + "_birthday:" + self._birthday
测试代码:
from qz.oop.SubClazz1 import SubClazz1 print(SubClazz1.name) print(SubClazz1.sex) sub = SubClazz1("王五", "女") print(sub) print(sub.getName()) print(SubClazz1.sex)
结果:
zs
男
name:王五 sex:女 _birthday:2020-02-02
王五
这里是SubClazz1类重写过的方法
男
- 继承多个类:
from qz.oop.ParentClazz import ParentClazz from qz.oop.SubClazz1 import SubClazz1 class SubClazz2(SubClazz1, ParentClazz): # 属性 idCode = 'xxx' # 构造方法 def __init__(self, idCode): self.idCode = idCode
测试类:
from qz.oop.SubClazz2 import SubClazz2 print(SubClazz2.name) print(SubClazz2.sex) sub2 = SubClazz2("112233") print(sub2.idCode)
结果:
zs
男
112233
注意:
(1) 上面只能把SubClazz1类写在ParentClazz类前面,否则会报错,因为类SubClazz1是ParentClazz类的派生类; 颠倒顺序报错如下:
Traceback (most recent call last): File "E:\pyspace\craw\qz\oop\main.py", line 1, in <module> from qz.oop.SubClazz2 import SubClazz2 File "E:\pyspace\craw\qz\oop\SubClazz2.py", line 5, in <module> class SubClazz2(ParentClazz, SubClazz1): TypeError: Cannot create a consistent method resolution order (MRO) for bases ParentClazz, SubClazz1
(2). 需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。
补充: 关于接口
可以有类似Java的接口和抽象方法
1. 接口
# 抽象类加抽象方法就等于面向对象编程中的接口 from abc import ABCMeta, abstractmethod class Interface1(object): __metaclass__ = ABCMeta # 指定这是一个抽象类 @abstractmethod # 抽象方法 def Lee(self): pass def Marlon(self): pass
2. 实现类
from qz.interface.test.Interface1 import Interface1 class Impl1(Interface1): # 必须实现interface中的所有函数,否则会编译错误 def __init__(self): # 无参构造方法 print('这是接口interface的实现Impl1') def Lee(self): print('实现Lee功能') def Marlon(self): pass
3. 客户端
from qz.interface.test.Impl1 import Impl1 from qz.interface.test.Interface1 import Interface1 obj = Impl1(); obj.Lee(); print(isinstance(obj, Impl1)) # True print(isinstance(obj, Interface1)) # True print(type(obj)) # <class 'qz.interface.test.Impl1.Impl1'> print(type(obj) == Impl1) # True print(type(obj) == Interface1) # False
结果: isinstance 可以用于判断父子关系
这是接口interface的实现Impl1 实现Lee功能 True True <class 'qz.interface.test.Impl1.Impl1'> True False
补充: class 本身也是一个对象
class ClassA: name = 'zs' print(ClassA) ClazzB = ClassA print(ClazzB) print(ClazzB.name)
结果:
<class '__main__.ClassA'> <class '__main__.ClassA'> zs