封装
① 第一个层面的封装:类本身就是一种封装
②第二个层面的封装:类中定义私有的,只有在类的内部使用,外部无法访问,命名变化,只是遵守约定,实际还是可以访问
#####python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
class People:
#star = 'earth' #内部外部随便调用
#_star = 'earth' #内部可以调用,但是只是约定,要调用还是可以的
__star = 'earth'
def __init__(self,id,name,age,salary):
print('-->>',self.__star) #内部就可以访问了
self.id = id
self.name = name
self.age = age
self.salary = salary
def get_id(self):
print('我的id是【%s】' % self.id) #整个类内部所有的方法和属性封装在一起,直接访问类调用即可
#访问函数,外部可以通过函数访问到私有的属性
def get_star(self):
print(self.__star)
#print(People.__dict__) #类的属性方法
p1 = People('123456789','ID Card',18,10000)
#增加下划线前
print(p1.star) #earth
print(p1._star) #earth,单下划线外部可以调用
#增加下划线后
#print(p1.__star) #报错,外部访问不了,不能通过__star访问到
print(People.__dict__) #属性字典里面:'_People__star': 'earth',因此可以访问
print(p1._People__star) #earth,py对__star重命名了 {'_People__star': 'earth'}
print(People._People__star) #earth
print(People.__star) ##报错,外部访问不了,不能通过__star访问到
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#正常情况
>>> class A:
... def fa(self):
... print('from A')
... def test(self):
... self.fa()
...
>>> class B(A):
... def fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from B
#把fa定义成私有的,即__fa
>>> class A:
... def __fa(self): #在定义时就变形为_A__fa
... print('from A')
... def test(self):
... self.__fa() #只会与自己所在的类为准,即调用_A__fa
...
>>> class B(A):
... def __fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from A
③ 第三个层面的封装:明确区分内外,内部的实现逻辑,外部无法知晓,并且为封装到内部的逻辑提供了访问的接口给外部使用
#求面积
class Room:
def __init__(self,name,owner,width,length,height):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__height = height
#访问函数
def tell_area(self): #求面积
return self.__width * self.__length
r1 = Room('别墅','laowang',100,100,10)
#area = r1.__width * r1.__length #私有属性隐藏,外部访问不了
area = r1.tell_area() #调用访问函数间接从外部访问到私有属性
print(area)
######求体积
class Room:
def __init__(self,name,owner,width,length,height):
self.name = name
self.owner = owner
self.__width = width
self.__length = length
self.__height = height
#访问函数
def tell_area(self): #求面积
return self.__width * self.__length * self.__height #外部调用感知不到,仍然使用该方法,但是功能已经变了
#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性
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()
property的概念
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
class People:
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 = People('laowang',75,1.8)
print(p1.bmi) #23.148148148148145
import math
class Circle:
def __init__(self,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.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行
print(c.perimeter)
为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
当类里面的属性设置为私有时,在python中通过property方法可以实现统一的访问,设置set、get、delete方法(接口)进行访问
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 #抛出异常'TypeError: Can not delete'
class Foo:
def __init__(self,val):
self.__NAME=val #将所有的数据属性都隐藏起来
def getname(self):
return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)
def setname(self,value):
if not isinstance(value,str): #在设定值之前进行类型检查
raise TypeError('%s must be str' %value)
self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME
def delname(self):
raise TypeError('Can not delete')
name=property(getname,setname,delname) #不如装饰器的方式清晰
总结
1.通过封装明确内部和外部
2.通过继承和多态在语言层面上支持了归一化设计