封装
封装的好处
【封装】
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有定义
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
目的是要让内部属性不被外部访问,属性的名称前加上两个下划线__
,在Python中,实例的变量名如果以__
开头,
就变成了一个私有变量(private),只有内部可以访问,外部不能访问和
私有属性

class Teacher: __identifier = 'Teacher' #私有静态属性 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的加__ def __init__(self,name,pwd): self.name = name self.__pwd = pwd #私有属性 #变形为self._Teacher__pwd self.__p() def __p(self): #私有方法 #变形为_Teacher__foo return hash(self.__pwd) def login(self,password): #self.__pwd == password return hash(password) == self.__p() # print(Teacher._Teacher__identifier) alex = Teacher('alex','3714') ret = alex.login('3714') print(ret) # print(alex._Teacher__b) # print(alex.__dict__) 即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
这种自动变形的特点:
- 类中定义的__Teacher只能在内部使用,如self.__Teacher,引用的就是变形的结果。
- 这种变形其实正是针对外部的变形,在外部是无法通过__Teacher这个名字访问到的。
- 在子类定义的__Teacher不会覆盖在父类定义的__Teacher,因为子类中变形成了:_子类名__Teacher
- 而父类中变形成了:_父类名__Teacher,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
- 这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如alex._Teacher__b
- 变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
私有方法
- 有一些方法的返回值只是用来作为中间结果
- 父类的方法不希望子类继承

class Foo: def __jinghong_sb(self): #_Foo__jinghong_sb print('Foo') class Son(Foo): def __jinghong_sb(self): print('Son') def func(self): self.__jinghong_sb() #_Son__jinghong_sb son = Son() son.func() foo=Foo() foo._Foo__jinghong_sb()
结合实例的应用

# 成人的BMI数值: # 过轻:低于18.5 # 正常:18.5-23.9 # 过重:24-27 # 肥胖:28-32 # 非常肥胖, 高于32 # 体质指数(BMI)=体重(kg)÷身高^2(m) # EX:70kg÷(1.75×1.75)=22.86 class Person: def __init__(self,name,height,weight): self.name = name self.__height = height self.__weight = weight def get_bmi(self): return self.__weight / (self.__height*self.__height) def change_weight(self,new_weight): if new_weight > 20: self.__weight = new_weight else: print('体重过轻') # jinghong = Person('某某',1.81,94) # print(jinghong.get_bmi()) # jinghong.change_weight() # print(jinghong.get_bmi()) #房屋 : # 业主 长 宽 # 面积 class Host: def __init__(self,owner,length,width): self.owner = owner self.__length = length self.__width = width def area(self): return self.__length * self.__width guo = Host('某人',2,1) print(guo.area())
property属性
property这个语法糖:所表达的是将类的私有的方法(计算或者内部实现的结果)伪装成为一个类的属性

class Person: def __init__(self,name,height,weight): self.name = name self.__height = height self.__weight = weight @property def bmi(self): # 变成属性直接调用 return self.__weight / (self.__height*self.__height) jinghong = Person('景弘',1.8,94) print(jinghong.name,jinghong.bmi)

class Shop: discount = 0.75 def __init__(self,name,price): self.name = name self.__price = price @property #!!! def price(self): return self.__price * Shop.discount @price.setter #! def price(self,new_price): self.__price = new_price apple = Shop('apple',5) #apple.price = 6 print(apple.__dict__) print(apple.price)

import math class Circle: def __init__(self,radius): #圆的半径radius self.radius=radius @property def area(self): return math.pi * self.radius**2 #计算面积 @property def perimeter(self): return 2*math.pi*self.radius #计算周长 c=Circle(10) print(c.radius) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 ''' 输出结果: 314.1592653589793 62.83185307179586 ''' 圆的周长和面积 #注意:此时的特性area和perimeter不能被赋值 c.area=3 #为特性area赋值 ''' 抛出异常: AttributeError: can't set attribute '''
为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

ps:面向对象的封装有三种方式:
【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开
- 只有@property表示只读。
- 同时有@property和@x.setter表示可读可写。
- 同时有@property和@x.setter和@x.deleter表示可读可写可删除。
swtter 表示装饰同样伪装属性的值可以在外部被修改
del 表示只是删除了属性 但是代码和方法还是存在在类的内部

class Foo: def get_AAA(self): print('get的时候运行我啊') def set_AAA(self,value): print('set的时候运行我啊') def delete_AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应 f1=Foo() f1.AAA f1.AAA='aaa' del f1.AAA

class Goods: def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deleter def price(self): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 print(obj.price) del obj.price # 删除商品原价