Python类和对象4
正常情况下,类包含的属性应该是隐藏的,只允许通过类提供的方法来间接实现对类属性的访问和操作。因此,在不破坏类封装原则的基础上,为了能够有效操作类中的属性,类中应包含读(或写)类属性的多个getter(或setter)方法,这样就可以通过“类对象.方法(参数)”的方式操作属性,例如:
classCLanguage:
#构造函数
def__init__(self,name):
self.name=name
#设置name属性值的函数
defsetname(self,name):
self.name=name
#访问nema属性值的函数
defgetname(self):
returnself.name
#删除name属性值的函数
defdelname(self):
self.name="xxx"
clang=CLanguage("C语言中文网")
#获取name属性值
print(clang.getname())
#设置name属性值
clang.setname("Python教程")
print(clang.getname())
#删除name属性值
clang.delname()
print(clang.getname())
运行结果为:
C语言中文网Python教程xxx
可能有读者觉得,这种操作类属性的方式比较麻烦,更习惯使用“类对象.属性”这种方式。庆幸的是,Python中提供了property()函数,可以实现在不破坏类封装原则的前提下,让开发者依旧使用“类对象.属性”的方式操作类中的属性。property()函数的基本使用格式如下:
属性名=property(fget=None,fset=None,fdel=None,doc=None)
其中,fget参数用于指定获取该属性值的类方法,fset参数用于指定设置该属性值的方法,fdel参数用于指定删除该属性值的方法,最后的doc是一个文档字符串,用于说明此函数的作用。
注意,在使用property()函数时,以上4个参数可以仅指定第个、或者前2个、或者前3个,当前也可以全部指定。也就是说,property()函数中参数的指定并不是完全随意的。
例如,修改上面的程序,为name属性配置property()函数:
classCLanguage:
#构造函数
def__init__(self,n):
self.__name=n
#设置name属性值的函数
defsetname(self,n):
self.__name=n
#访问nema属性值的函数
defgetname(self):
returnself.__name
#删除name属性值的函数
defdelname(self):
self.__name="xxx"
#为name属性配置property()函数
name=property(getname,setname,delname,指明出处)
#调取说明文档的2种方式
#print(CLanguage.name.__doc__)
help(CLanguage.name)
clang=CLanguage("C语言中文网")
#调用getname()方法
print(clang.name)
#调用setname()方法
clang.name="Python教程"
print(clang.name)
#调用delname()方法
delclang.name
print(clang.name)
运行结果为:
Helponproperty:指明出处C语言中文网Python教程xxx
注意,在此程序中,由于getname()方法中需要返回name属性,如果使用self.name的话,其本身又被调用getname(),这将会先入无限死循环。为了避免这种情况的出现,程序中的name属性必须设置为私有属性,即使用__name(前面有2个下划线)。
有关类属性和类方法的属性设置(分为共有属性、保护属性、私有属性),后续章节会做详细介绍。
当然,property()函数也可以少传入几个参数。以上面的程序为例,我们可以修改property()函数如下所示
name=property(getname,setname)
这意味着,name是一个可读写的属性,但不能删除,因为property()函数中并没有为name配置用于函数该属性的方法。也就是说,即便CLanguage类中设计有delname()函数,这种情况下也不能用来删除name属性。同理,还可以像如下这样使用property()函数:
name=property(getname)#name属性可读,不可写,也不能删除
name=property(getname,setname,delname)#name属性可读、可写、也可删除,就是没有说明文档
Pythonproperty装饰器详解既要保护类的封装特性,又要让开发者可以使用“对象.属性”的方式操作操作类属性,除了使用property()函数,Python还提供了
property装饰器。通过property装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对“()”小括号。property的语法格式如下:propertydef方法名(self)代码块例如,定义一个矩形类,并定义用
property修饰的方法操作类中的area私有属性,代码如下:classRect:
def__init__(self,area):
self.__area=area
propertydefarea(self):
returnself.__area
rect=Rect(30)
#直接通过方法名来访问area方法
print("矩形的面积是:",rect.area)
运行结果为:
矩形的面积为:30
上面程序中,使用@property修饰了area()方法,这样就使得该方法变成了area属性的getter方法。需要注意的是,如果类中只包含该方法,那么area属性将是一个只读属性。也就是说,在使用Rect类时,无法对area属性重新赋值,即运行如下代码会报错:
rect.area=90
print("修改后的面积:",rect.area)
运行结果为:
Traceback(mostrecentcalllast):File"C:\Users\mengma\Desktop\.py",line0,inmodulerect.area=90AttributeError:cantsetattribute
而要想实现修改area属性的值,还需要为area属性添加setter方法,就需要用到setter装饰器,它的语法格式如下:
方法名.setterdef方法名(self,value):代码块例如,为Rect类中的area方法添加setter方法,代码如下:
area.setter
defarea(self,value):
self.__area=value
再次运行如下代码:
rect.area=90
print("修改后的面积:",rect.area)
运行结果为:
修改后的面积:90
这样,area属性就有了getter和setter方法,该属性就变成了具有读写功能的属性。除此之外,还可以使用deleter装饰器来删除指定属性,其语法格式为:
方法名.deleterdef方法名(self):代码块例如,在Rect类中,给area()方法添加deleter方法,实现代码如下:
area.deleter
defarea(self):
self.__area=0
然后运行如下代码:
delrect.area
print("删除后的area值为:",rect.area)
运行结果为:
删除后的area值为:0
Python封装机制及实现方法简单的理解封装(Encapsulation),即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法。就好比使用电脑,我们只需要学会如何使用键盘和鼠标就可以了,不用关心内部是怎么实现的,因为那是生产和设计人员该操心的。
注意,封装绝不是将类中所有的方法都隐藏起来,一定要留一些像键盘、鼠标这样可供外界使用的类方法。
那么,类为什么要进行封装,这样做有什么好处呢?首先,封装机制保证了类内部数据结构的完整性,因为使用类的用户无法直接看到类中的数据结构,只能使用类允许公开的数据,很好地避免了外部对内部数据的影响,提高了程序的可维护性。除此之外,对一个类实现良好的封装,用户只能借助暴露出来的类方法来访问数据,我们只需要在这些暴露的方法中加入适当的控制逻辑,即可轻松实现用户对类中属性或方法的不合理操作。并且,对类进行良好的封装,还可以提高代码的复用性。
Python类如何进行封装?和其它面向对象的编程语言(如C++、Java)不同,Python类中的变量和函数,不是公有的(类似public属性),就是私有的(类似private),这2种属性的区别如下:
public:公有属性的类变量和类函数,在类的外部、类内部以及子类(后续讲继承特性时会做详细介绍)中,都可以正常访问;
private:私有属性的类变量和类函数,只能在本类内部使用,类的外部以及子类都无法使用。
但是,Python并没有提供public、private这些修饰符。为了实现类的封装,Python采取了下面的方法:
默认情况下,Python类中的变量和方法都是公有(public)的,它们的名称前都没有下划线(_);
如果类中的变量和函数,其名称以双下划线“__”开头,则该变量(函数)为私有变量(私有函数),其属性等同于private。
除此之外,还可以定义以单下划线“_”开头的类属性或者类方法(例如_name、_display(self)),这种类属性和类方法通常被视为私有属性和私有方法,虽然它们也能通过类对象正常访问,但这是一种约定俗称的用法。
注意,Python类中还有以双下划线开头和结尾的类方法(例如类的构造函数__init__(self)),这些都是Python内部定义的,用于Python内部调用。
例如,如下程序示范了Python的封装机制:
classCLanguage:
defsetname(self,name):
iflen(name)3:
raiseValueError(名称长度必须大于3!)
self.__name=name
defgetname(self):
returnself.__name
#为name配置setter和getter方法
name=property(getname,setname)
defsetadd(self,add):
ifadd.startswith("