python全栈开发基础【第十五篇】面向对象三大特性——封装
1.封装:
什么是封装呢?(封装不是单纯意义的隐藏,其实它还是可以查看的)
就是把一些不想让别人看的给隐藏起来了
封装数据:目的是保护隐私
功能封装:目的是隔离复杂度
如果用了私有的,在类的外部,无法直接使用变形的属性,但是在类的内部可以直接使用
# 对象名.属性名取值的三种方法 1.用我们常用的__init__方法里的self取值 class Course:#恰好给我们提供了实现这种思路的方法 # #一种思路,python def __init__(self,price,period,name): self.price = price self.period = period self.name = name c = Course(2000,'linux','6 months') print(c.period) 2.在类里面定义一个空字典,然后装在字典里面取值 def course(price,name ,period): dic = {} dic['price'] = price dic ['name'] = name dic ['period'] = period return dic c = Course(2000,'python','6 months') print(c.period) #对象名.属性名 查看属性的值 3.利用namedtuple方法 from collections import namedtuple #只有属性没有方法的类 Course = namedtuple('Course',['name','price','period']) #传两个参数,第一个为自定义的名字,第二个传进去的是属性 python = Course('python',10000,'6 moths') #相当于实例化了 print(python.name)
2.封装类属性的私有属性(就是类属性前面加__)
#类属性1 class Goods: # 按照打八折计算 (定义了一个私有类属性) __discount = 0.8 #变形后:_Goods__discount def __init__(self,name,price): self.name = name self.price = price def goods_price(self): return self.price * Goods.__discount apple = Goods('apple',10) print(apple.goods_price()) # print(Goods.__dict__) #类名.__dict__ print(Goods._Goods__discount)
#类属性的私有方法 # 封装:把你不想让人看的隐藏起来 # 数据封装:目的保护隐私 class Teacher: __School = 'oldboy' #类属性 def __init__(self,name,salary): self.name = name self .__salary = salary #_Teacher__salary # 老师的属性 值 #怎么把薪水隐藏起来? self.__salary=salary def foo(self): print('------') t=Teacher('egon',2000) print(t.__dict__) # print(t.name) print(t._Teacher__salary)#让显示出来 print(Teacher._Teacher__School) #类属性使用_类名__属性名 t.foo() #在本类内是可以正常调用的 #在本类外就必须以_类名__属性名调用(但是不建议你调)
3.封装类对象的私有属性
成人的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,height,weight,name,sex): self.__height = height #私有属性(让你不再外面调它) # 在本类中可以调用,在类外就不可以调用了 self.__weigth = weight self.__name = name self.__sex = sex def tell_bmi(self): #体重指数 return self.__weigth/self.__height ** 2 #在本类中可以调用 def tell_height(self): print(self.__height) def tell_weight(self): #告诉体重 return self.__weigth def set_weigth(self,new_weight): #修改体重 if new_weight >20: self.__weigth = new_weight else: raise TypeError('你也太瘦了,瘦的连斤数都(快)没了') #如果体重小于20或者负的,就主动提示一个报错 egg = Person(1.6,96,'haiyan','female') print(egg.tell_bmi()) # egg.__height #在类外不能调用 # print(egg._Person__height) #在类外查看得这样调用 print(egg.__dict__) #查看变形后的类型 # egg.set_weigth(-10) # print(egg.tell_weigth()) egg.set_weigth(66) #修改体重为66 print(egg.tell_weight())
# 对象属性的私有属性二 class People: def __init__(self,name,age,sex,height): self.__name = name self.__age = age self.__sex = sex self.__height = height def tell_name(self): #看人名字 print(self.name) def set_name(self,val): #修改名字 if not isinstance(val, str): raise TypeError('名字必须是字符串类型') self.__name = val def tell_info(self): print(''' ---------%s info----------- name:%s age:%s sex:%s height:%s'''%(self.__name,self.__name,self.__age,self.__sex,self.__height)) p=People('egon',21,'male','180') p.tell_info() p.set_name('haiyan') #调用修改名字的方法 p.tell_info() # print(p._People__name)#就可以看到了
4.封装类方法的私有属性
类方法的私有属性1 # 方法的私有属性 class Parent: def __init__(self): self.__func() #__func==_Parent__func def __func(self): print('Parent func') class Son(Parent): def __init__(self): self.__func() #_Son__func def __func(self): print('Son func') def _Parent__func(self): print('son _Parent__func') s = Son() print(Parent.__dict__) #类名.__dict__查看变形后的结果 # 私有属性:在本类内是可以正常调用的 # 在本类外就必须以_类名__属性名调用(但是不建议你调)
#方法的私有属性2 class Foo: def __func(self): print('from foo') class Bar(Foo): def __func(self): print('from bar') b = Bar() b._Foo__func() b._Bar__func()
#装饰方法的私有属性3 class Foo: def __init__(self,height,weight): self.height = height self.weight = weight def __heightpow(self): #私有方法 return self.height * self.height def tell_bmi(self): return self.weight/self.__heightpow() egon = Foo(1.7,120) print(egon.tell_bmi()) print(Foo.__dict__) print(egon._Foo__heightpow()) #虽说是私有的,但是还是可以查看的
5.property
为什么要用property:将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
1.计算圆的面积和周长
from math import pi class Circle: def __init__(self,radius): self.radius = radius @property #装饰器:把一个方法当成一个属性用了 def area(self): return self.radius * self.radius* pi @property def peimeter(self): return 2*pi*self.radius c = Circle(10) print(c.area) #当成一个属性来调了,就不用加括号了 print(c.peimeter)
2.缓存网页信息
from urllib.request import urlopen class Web_page: def __init__(self,url): self.url = url self.__content = None #内容设置为None @property def content(self): if self.__content: #如果不为空,就说明已经下载了 _Web_page__content return self.__content else: self.__content = urlopen(self.url).read()#做缓存 return self.__content mypage = Web_page('http://www.baidu.com') print(mypage.content) print(mypage.content) print(mypage.content)
3.求和,平均值,最大值,最小值
class Num: def __init__(self,*args): print(args) if len(args)==1 and (type(args[0]) is list or type(args[0]) is tuple): self.numbers=args[0] else: self.numbers = args @property def sum(self): return sum(self.numbers) @property def avg(self): return self.sum/len(self.numbers) @property def min(self): return min(self.numbers) @property def max(self): return max(self.numbers) num = Num([3,1,3]) vvv = Num(8,2,3) print(num.sum) print(num.min) print(num.avg) print(num.max) print('-----------') print(vvv.sum) print(vvv.min) print(vvv.avg) print(vvv.max)
6.setter
#买东西 class Goods: __discount = 0.8 #类的私有属性 def __init__(self,name,price): self.name = name self.__price = price @property def price(self): # if hasattr(self,'__price'): return self.__price * Goods.__discount # else: # raise NameError @price.setter def price(self,new_price): if type(new_price) is int: self.__price = new_price @price.deleter def price(self): del self.__price apple = Goods('apple',10) # print(apple.price) apple.price = 20 print(apple.price) # del apple.price # print(apple.price) # apple.set_price(20) # apple._Goods__apple
@property把一个类中的方法 伪装成属性
原来是obj.func()
现在是obj.func -->属性
1.因为属性不能被修改
所以用了@funcname.setter
obj.func = new_value 调用的是被@funcname.setter装饰器装饰的方法
被@property装饰的方法名必须和被@funcname.setter装饰的方法同名
2.也可以另一种方法修改,但是上一种方法吧一个类中的方法伪装成属性来调用了,而这种方法
还是原来实例化一样调用
例如:
class People: def __init__(self,name,age,sex,height): self.__name = name self.__age = age self.__sex = sex self.__height = height def tell_name(self): #看人名字 print(self.name) def set_name(self,val): #修改名字 if not isinstance(val, str): raise TypeError('名字必须是字符串类型') self.__name = val def tell_info(self): print(''' ---------%s info----------- name:%s age:%s sex:%s height:%s'''%(self.__name,self.__name,self.__age,self.__sex,self.__height)) p=People('egon',21,'male','180') p.tell_info() p.set_name('haiyan') #调用修改名字的方法 p.tell_info() # print(p._People__name)#就可以看到了