封装
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形
特点:
1、在类外部是无法通过boj.__N这个名字访问到的。
2、在类内部是可以直接使用:obj.__N(由于是在定义的阶段已经改正正确的访问方式)
3、子类无法覆盖父类__开头的属性
小结:
1、加__可以在类定义阶段把类的属性变形了,达到对外部隐藏的目的
2、类内部可以调用开头加了__的属性,因为在类定义的阶段,已经把访问方式改成boj.__AttrName的方式。
3、子类无法覆盖父类__的属性,因为两并不是同一个类名
注意:
1、这种机制没有真正意义上限制,只是做了变性,可以通过A._A__N的方式访问。
# 第1问题 ,这种没有真正意义上的隐藏没有真正的隐藏 class B: __x = 1 # print(B.__x) # 由于隐藏了,查看的时候报错 print(B.__dict__) # {'_B__x': 1,} print(B._B__x) # 知道正確的查看方式后也能找到。
2、变性的过程只在类定义的时候只发生一次,定义后赋值操作不会变形。
class B: __x =1 def __init__(self,name): self.__name =name b=B('abc') print(b.__dict__) # __在类定义阶段会发生变形 b.__z=2 print(b.__dict__) # __在类定义后增加属性并不会变形
3、继承中,父类如果不想让子类覆盖自己的方法,可以将方法设为私有
第3个问题,如果不想继承中,父类如果不想让子类覆盖自己的方法,可以将方法设为私有 class A: def __foo(self): print('A .foo') def bar(self): print('bar') self.__foo() # self._A__foo() class B(A): def __foo(self): # _B__foo() print('B.foo') b = B() b.bar()
封装的意义:
封装数据属性——明确区分内外,控制外部对隐藏的属性操作行为。
1、明确的区分内外
class People: def __init__(self,name,age ): self.__name = name self.__age = age def tell_info (self): print('name: <%s> age:<%s>' %(self.__name,self.__age)) p =People('abc',10) print(p.tell_info())
这种封装的使用,使得用户需要调接口才能查看类里面的信息。
2、限定用户输入的类型
class People: def __init__(self,name,age ): self.__name = name self.__age = age def tell_info (self): print('name: <%s> age:<%s>' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): print('名字必须是字符串类型') return if not isinstance(name,int): print('名字必须是数字类型') return self.__name = name self.__age = age p =People('abc',10) print(p.tell_info()) #方法2 只能同过别人开的接口进行修改 p.set_info('11','aa') print(p.tell_info())
这种的使用,使得用户需要调接口才能修改类里面的信息。
封装方法属性——隔离复杂度
class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw()
这种封装的用法是,用户不需要关心流程的细节,只需要关心动作。
封装的可扩展性:
1、封装处理明确区分内外,使得类的使用者可以修改疯长内部的东西而不印象外部调用者的代码。
2、对于外部使用者,只知道一个接口函数,只要接口名,参数不变,使用者代码不需改变。
class Room: def __init__(self,name,owener,height,weight): self.name = name self.owener = owener self.__weight = weight self.__height = height def tell_area(self): return self.__weight* self.__height r = Room('abc','abc',10,10) print(r.tell_area()) #对于可封装来说,
特性(property):
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
@name.property
class Pepole: def __init__(self,name ,weight,height): self.name=name self.weight= weight self.height=height @property def bmi(self): return self.weight/(self.height**2) p1 = Pepole('abc',80,175) # p1.bmi = p1.weight/(p1.height**2) # print(p1.bmi()) # 多了括号,影响使用者的调用。 # 加了装饰器后 print(p1.bmi) # p1.bmi = 333 # 由于跟了对象,所以不能赋值。
意思是:1、之前我们执行类方法的时候是这样的print(a.bmi())
2、加了property 装饰器后,可以通过print(a.bmi)方式取得值。
3、a.bmi由于是对象所以不能赋值操作,如果要赋值的情况如下。
@name.setter
class Foo: def __init__(self,val): self.__NAME=val #将所有的数据属性都隐藏起来 @property def name(self): return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置) @name.setter def name(self,value): if not isinstance(value,str): #在设定值之前进行类型检查 raise TypeError('%s must be str' %value) self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME # @name.deleter # def name(self): # raise TypeError('Can not delete') f=Foo('egon') print(f.name) f.name=10 #抛出异常'TypeError: 10 must be str' # del f.name
@name.deleter
@name.deleter def name(self): raise TypeError('Can not delete')