面向对象-访问控制
私有(private)属性
class Person: def __init__(self, name, age=18): self.name = name self.age = age def growup(self, i=1): if i > 0 and i < 150: self.age += i p1 = Person('tom') p1.growup(20) p1.growup(160) print(p1.age)
输出为:
38
上例,本来想通过方法控制属性,但是由于属性在外部可以访问,或者说可见,就可以直接绕过方法,直接修改这个属性
python提供了私有属性可以解决这个问题
私有属性
使用双下划线开头的属性名,就是私有属性
示例:
class Person: def __init__(self, name, age=18): self.name = name self.__age = age def growup(self, i=1): if i > 0 and i < 150: self.__age += i p1 = Person('tom') p1.growup(20) p1.growup(160) print(p1.__age)
输出为:
Traceback (most recent call last): File "E:/PycharmProjects/untitled/test/a.py", line 23, in <module> print(p1.__age) AttributeError: 'Person' object has no attribute '__age'
外部访问不到__age,age没有定义,也访问不到
可以使用方法来访问__age
示例:
class Person: def __init__(self, name, age=18): self.name = name self.__age = age def growup(self, i=1): if i > 0 and i < 150: self.__age += i def getage(self): return self.__age p1 = Person('tom') p1.growup(20) p1.growup(160) print(p1.getage())
输出为:
38
私有变量的本质
类定义的时候,如果声明一个实例变量的时候,使用双下划线,python解释器会将其改名,转换名称为_类名——变量名的名称,所以用原来的名字访问不到了
直接修改私有变量
class Person: def __init__(self, name, age=18): self.name = name self.__age = age def growup(self, i=1): if i > 0 and i < 150: self.__age += i def getage(self): return self.__age p1 = Person('tom') p1.growup(20) p1._Person__age = 15 print(p1.__dict__) print(p1.getage())
输出为:
{'name': 'tom', '_Person__age': 15} 15
从上例可以看出,知道了私有变量的名称,就可以直接从外部访问到并修改他
保护变量
在变量名前使用一个下划线,称为保护变量
示例:
class Person: def __init__(self, name, age=18): self.name = name self._age = age tom = Person('tom') print(tom.__dict__) print(tom._age)
输出为:
{'name': 'tom', '_age': 18} 18
_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何特殊处理
这只是开发者共同的约定,看见这种变量,就如同私有变量,不要直接使用
私有方法
参照保护变量,私有变量,使用单下划线,双下划线命名方法
私有方法的本质
单下划线的方法只是开发者之间的约定,解释器不做任何改变
双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量相同,_类名_方法名
方法变量都在类的__dict__中可以找到
属性装饰器
一般好的设计师,把实例的属性保护起来,不让外部直接访问,外部使用getter读取属性和setter方法设置属性
示例:
class Person: def __init__(self, name, age=18): self.name = name self.__age = age def age(self): return self.__age def set_age(self,age): self.__age = age tom = Person('tom') print(tom.age()) tom.set_age(30) print(tom.age())
输出为:
18 30
通过age'和set_age方法操作属性
python提供了属性property装饰器
class Person: def __init__(self, name, age=18): self.name = name self.__age = age @property def age(self): return self.__age @age.setter def age(self,age): self.__age = age @age.deleter def age(self): print('del') tom = Person('tom') print(tom.age) tom.age = 30 print(tom.age) del tom.age
property装饰器
后面跟的函数名就是以后的属性名,他就是getter,必须有,有了它至少是只读属性
setter装饰器
与属性名同名,且接收2个参数,第一个是self,第二个是将要复制的值,有了它属性可写
deleter装饰器
可以控制是否删除属性,很少用
property装饰器必须在前,setter,deleter装饰器在后
property装饰器能通过简单的方法,把对方法的操作变成对属性的访问,并起到了一定隐藏效果
对象的销毁
类中可以定义__del__方法,称为析构函数(方法)
销毁类的实例的时候调用,以释放占用的资源,其中就放些清理资源的代码,比如释放连接
注意这个方法不能引起对象的真正销毁,只是对象销毁的时候会自动调用
使用del语句删除实例,引用计数减1,当引用计数为0时,会自动调用__del__方法
由于python实现了垃圾回收机制,不能确定对象何时执行垃圾回收
由于垃圾回收对象销毁时,才会真正清理对象,还会在回收对象之前自动调用__del__方法,除非你明确知道自己的目的,建议不要手动调用这个方法
方法重载(overload)
其他面向对象的高级语言中,会有重载的概念。
所谓重载,就是同一个方法名,但参数数量,类型不一样,就是同一个方法的重载
python没有重载,不需要重载,python中,方法定义中,形参非常灵活,不需要指定类型,参数个数也不固定,一个函数的定义可以实现很多种不同形式实参的调用,所以python不需要方法重载
封装
将数据和操作组织到类中,即属性和方法
将数据隐藏起来,给使用者提供操作,使用者通过操作就可以获取或者修改数据getter和setter
通过访问控制,暴露适当的数据和操作给用户,该隐藏的隐藏起来,例如保护成员或虽有成员