面向对象编程
提示:面向对象编程(OOP)是一种程序设计方法,它的核心就是将显示世界中的概念,过程和事务抽象成Python中的模型,使用这些模型进行程序的设计和构建。因为Python是一种面向对象的语言,所以要想熟练熟练使用Python语言,就一定要掌握类和对象的使用。面向对象的三个重要特征(继承性、封装性、多态性)及声明创建类和对象的方法。
一、类的构造方法、析构方法和内置属性
1.类的构造方法和析构方法
Python类中有两个比较特殊的方法,他们不是某类具备的功能,而是类的专有方法,即构造方法和析构方法。
- 构造方法:实例化类时自定义执行的方法,定义时用__init__形式。【self是必要的参数且为第一个参数,用户可以在__init__()构造方法内加入许多参数,在创建类时同时设置类的属性值。】
- 析构方法:销毁类时自动执行的方法,定义时用__del__形式。
class MarketMember:
def __init__(self):
print("欢迎光临")
def __del__(self):
pass
m = MarketMember()
注意:构造方法和析构方法是在特殊情况下或当使用特别语法时由Python替你调用的,不需要在代码中直接调用(类似普通犯法那样),这两个方法在类中都只能被定义一次。
2.Python类的内置属性
- Classname.dict:类内的属性是以字典对象的方法存储的。__dict__属性为该字典对象的值。
- classname.doc:__doc__属性返回此类的文件字符串。
- classname.name:__name__属性返回此类的名称。
- classname.module:__module__属性返回包含此类的模块名称。
- classname.bases:__bases__属性是一个tuple对象,返回此类的基类名称。
class Cc:
"这是一个定义类的例子"
a = 100
class a(Cc):
"a继承Cc"
b = 200
if __name__ == '__main__':
print( Cc.__dict__ )
print( Cc.__doc__ )
print( Cc.__name__ )
print( Cc.__module__ )
print( Cc.__bases__ )
print( a.__bases__ )
运行结果:
{'__module__': '__main__', '__doc__': '这是一个定义类的例子', 'a': 100, '__dict__': <attribute '__dict__' of 'Cc' objects>, '__weakref__': <attribute '__weakref__' of 'Cc' objects>}
这是一个定义类的例子
Cc
__main__
(<class 'object'>,)
(<class '__main__.Cc'>,)
二、类的继承
所谓类的继承(inheritance),就是心累继承就累的属性和方法,这种行为称为派生子类(subclassing)。继承的心累称为派生类(derived class),被继承的旧类则称为基类(base class)。当用户创建派生类后,就可以在派生类内新增或改写基类的任何方法。
class <类名称> [(基类1,基类2,...)]
["文件字符串"]
<语句>
注意:当用户在类内编写函数时,要记得类函数名称空间的搜索顺序是:类的实例–>类–>基类。
三、类的多态
所谓类的多态(polymorphism),就是指类可以有多个名称相同、参数类型却不同的函数。Python并没有明显的多态特性,因为Python函数的参数不必声明数据类型。但是Python利用动态数据类型(dynamic typing),仍然可以处理对象的多态。
因为使用动态数据类型,所以Python必须等到运行该函数时才能知道该函数的类型,这种特性成为运行期绑定(runtime binding)。
class myClass:
def __init__(self):
pass
def handle(self,*args):
print(len(args))
if len(args) == 1:
self.handle1(*args)
elif len(args) == 2:
self.handle2(*args)
elif len(args) == 3:
self.handle3(*args)
else:
print("wrong argumens")
def handle1(self,x):
print("1 arguments")
def handle2(self,x,y):
print("1 arguments")
def handle3(self,x,y,z):
print("3 arguments")
if __name__ == '__main__':
x = myClass()
x.handle()
x.handle(1)
x.handle(1,2)
x.handle(1,2,3)
x.handle(1,2,3,4)
结果如下:
0
wrong argumens
1
1 arguments
2
1 arguments
3
3 arguments
4
wrong argumens
四、类的封装
所谓类的封装(encaosulation),就是指类将其属性(变量与方法)封装在该类内,只有该类中的成员,才可以使用该勒种的其他方法。这种被封装的变量与方法,成为该类的私有变量(private variable)与私有方法(private method)。
要做到类的封装,必须遵循以下两个原则:
- 如果属性(方法)名称的第一个字符是单下划线,那么该属性视为类的内部变量,外面的变量不可以引用该属性。
- 如果属性(方法)名称的前两个字符都是单下划线,那么在编译时属性名称 attributeName 会被改成 _className_attributeName,className是该类的名称。由于属性名称之前加上了类的名称,因此与类中原有的属性名称有差异。
# 类的封装
class Fruit:
def __init__(self,value):
self._n = value # 变量_n的第一个字符是单下划线
self.__n = value # 变量__n的前两个字符都是单下划线
def __func(self): # 函数的__func()前两个字符都是单下划线
print(self._n+1)
if __name__ == '__main__':
f = Fruit(5.88)
print( f._n ) # 第一个字符是单下划线的变量_n,所以可以任意存取
# f.__n # 错误,因为__n已经被改名为 _Fruit__n
print( f._Fruit__n ) # 正确
# print( f.__func() ) # 错误,因为__func()已经被改名为_Fruit__func()
print( f._Fruit__func() ) # 正确
虽然对类的属性和方法名称前两位字符使用单下划线定义可以视为私有变量和私有方法,正常的访问方式是无法访问的,但不代表这些属性和方法无法在类外部访问,我们可以通过特殊方法访问这些私有变量和私有方法。
私有变量和私有方法除了名称前两位字符使用单下划线定义之外,还可以对名称前一位字符使用单下划线定义,这种方式(名称前一位字符使用单下划线定义)与公有属性或公有方法的调用过程相同,它主要给开发者标记当前属性或方法是私有属性或方法。
总的来说,定义类的私有属性和私有方法的方式如下:
- 使用property属性装饰类的实例方法,使实例方法以类属性的方法访问和调用,在某些程度上实现封装功能。
- 对类的属性和方法名称前两位字符使用单下划线定义,在调用过程中只能通过_class__xxx方式访问(class代表类名, xxx代表属性或方法名称)
- 对类的属性和方法名称前一位字符使用单下划线定义,它与公有属性或公有方法的调用过程相同,主要给开发者标记当前属性或方法是私有属性或方法。
参考资料:
本文编写参考书籍《Python 3.X基础教程》史卫东 编著、Python 3.6 零基础入门与实战 王启明 罗从良 著《精通Python自动化编程》黄永祥 编著