Python学习日记(二十六) 封装和几个装饰器函数
封装
广义上的封装,它其实是一种面向对象的思想,它能够保护代码;狭义上的封装是面向对象三大特性之一,能把属性和方法都藏起来不让人看见
私有属性
私有属性表示方式即在一个属性名前加上两个双下划线
class fighter: def __init__(self,name,hp,atk,speed): self.name = name self.__hp = hp #私有属性 变形成self._fighter__hp self.__atk = atk #私有属性 变形为self._fighter__atk self.__speed = speed #私有属性 变形为self._fighter__speed f1 = fighter('akk-18',1500,200,300) print(f1.__dict__) #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300}
python的私有属性只是在代码的级别给加了一层密,不允许你直接去访问这些私有属性,像你去打印一个私有属性它的结果就是报错,或者你想尝试修改一个私有属性的时候,结果并不能修改成功
f1.__hp = 1000 print(f1.__dict__) #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300, '__hp': 1000}
因为上面的私有属性都已经变形成_类名__属性名的格式,我们这样写,相当于直接给self添加了一个新的'键名'__hp,所以在类的外部调用双下划线就不行
那如何访问我们的私有属性呢?我们可以看到上面的私有属性都被变形成了_类名__属性名的形式,那么我们可以这样去访问:
print(f1._fighter__hp) #1500 print(f1._fighter__atk) #200 print(f1._fighter__speed) #300
但光这样走这样的捷径是不完美的,我们可以在类内部添加一个get_value函数来获得那些私有属性
class fighter: def __init__(self,name,hp,atk,speed): self.name = name self.__hp = hp #私有属性 self.__atk = atk #私有属性 self.__speed = speed #私有属性 def get_value(self): return self.__hp,self.__atk,self.__speed #只要在类内部使用私有属性就会自动带上'_类名’ f1 = fighter('akk-18',1500,200,300) print(f1.__dict__) #{'name': 'akk-18', '_fighter__hp': 1500, '_fighter__atk': 200, '_fighter__speed': 300} print(f1.get_value()) #(1500, 200, 300) for i in f1.get_value(): print(i) #1500 #200 #300
在get_value中return的写法其实就是变形后再获取私有属性
def get_value(self): return self._fighter__hp,self._fighter__atk,self._fighter__speed
私有静态属性
class fighter: __id = 1234445 #私有静态属性 def __init__(self,name,hp,atk,speed): self.name = name self.__hp = hp #私有属性 self.__atk = atk #私有属性 self.__speed = speed #私有属性 def get_value(self): #获取内部私有属性方法 return self.__hp,self.__atk,self.__speed def get_id(self): return self.__id f1 = fighter('akk-18',1500,200,300) print(f1._fighter__id) #1234445 print(f1.get_id()) #1234445
私有方法
class fighter: __id = 1234445 #私有静态属性 def __init__(self,name,hp,atk,speed): self.name = name self.__hp = hp #私有属性 self.__atk = atk #私有属性 self.__speed = speed #私有属性 def get_value(self): #获取内部私有属性方法 return self.__hp,self.__atk,self.__speed def get_id(self): #获取静态属性id的方法 return self.__id def __shoot(self): #私有方法 print('shooting!') def get_shoot(self): #获取私有方法shoot self.__shoot() #self._fighter__shoot() f1 = fighter('akk-18',1500,200,300) f1.get_shoot() #shooting!
修改私有属性
class room: def __init__(self,name,length,width): self.__name = name self.__length = length self.__width = width def area(self): return self.__width*self.__length def get_name(self): #获取这个私有名字的方法 return self.__name def set_name(self,new_name):#重新设定这个房号 if type(new_name) is str and new_name.isdigit() == true: self.__name = new_name else: print('错误的命名方式!') r1 = room('1101',15,20) print(r1.area()) #300 print(r1.get_name()) #1101 r1.set_name('asd') print(r1.__dict__) #{'_room__name': '1103', '_room__length': 15, '_room__width': 20} print(r1.get_name()) #1103
几个装饰器
1.@property
我们在用面向对象的思想去写一个圆经常会把它的周长和面积当成一个方法,然而我们知道方法是一个获取的动作,而不是一个属性名词,所以要想处理这种将一个方法伪装成为一个属性就要用到@property,这样这个方法它就看起来像是一个属性了
from math import pi as p class circle: def __init__(self,r): self.r = r def perimeter(self): return 2*p*self.r def area(self): return p*self.r**2 c = circle(5) print(c.area()) #78.53981633974483 print(c.perimeter()) #31.41592653589793
改进:
from math import pi as p class circle: def __init__(self,r): self.r = r @property def perimeter(self): #这里在self的后面不能传任何参数 return 2*p*self.r @property def area(self): #这里在self的后面不能传任何参数 return p*self.r**2 c = circle(5) print(c.perimeter) #31.41592653589793 print(c.area) #78.53981633974483
bmi问题:bmi指数(bmi)=体重(kg)÷身高^2(m)
成人的bmi数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
非常肥胖:>32
class bmi: def __init__(self,height,weight): self.__height = height self.__weight = weight @property def __bmi(self): #把计算bmi的方法伪装成一个属性 return self.__weight/self.__height**2 def get_bmi(self): #bmi判断 if self.__bmi > 32: print('非常肥胖!') elif 24 <= self.__bmi <= 27: print('过重!') elif 18.5 <= self.__bmi <= 23.9: print('正常!') else: print('过轻!') b = bmi(1.75,72) b.get_bmi() #正常!
在@property的前提下用@setter来修改属性
class person: def __init__(self,name,): self.__name = name @property def name(self): return self.__name + ' hello!' @name.setter def name(self,new_name): self.__name = new_name p1 = person('jogn') print(p1.name) #jogn hello! #p1._person__name = 'maria' #maria hello! print(p1.name) #maria hello!
在@property的前提下用@deleter来删除属性
class person: def __init__(self,name,): self.__name = name @property def name(self): return self.__name + ' hello!' @name.deleter def name(self): del self.name p1 = person('jogn') print(p1.name) #jogn hello! del p1.name #del p1.name它在这里的作用是指向deleter下面这个方法,再去做这个方法的功能 print(p1.name) #找不到这个名字了
我们可以看一下在这里的del关键字它起到了什么的作用
class person: def __init__(self,name,): self.__name = name @property def name(self): return self.__name + ' hello!' @name.deleter def name(self): print('执行了这个方法!') p1 = person('jogn') print(p1.name) #jogn hello! del p1.name #del p1.name它在这里的作用是指向deleter下面这个方法,再去做这个方法的功能 print(p1.name) #jogn hello!
因为第二个name里面没有关于修改名字的操作所以在最后p1.name还能打印出原来的名字
2.@classmethod
当这个方法只涉及到静态属性的时候,就应该使用classmethod来装饰这个方法
class goods: __discount = 0.8 #折扣 def __init__(self,name,price): self.name = name self.__price = price @property def price(self): return self.__price * self.__discount @classmethod def change_discount(cls,new_discount): #cls为类名 cls.__discount = new_discount #修改折扣 apple = goods('苹果',4) print(apple.price) #3.2 goods.change_discount(0.5) print(apple.price) #2.0
3.@staticmethod
在完全面向对象的程序中,如果一个函数和对象没有关系那么就用staticmethod把这个函数变成一个静态方法
class login: def __init__(self,name,password): self.name = name self.password = password @staticmethod def get_n_psw(): #没有默认参数就像函数一样 username = input('请输入用户名:') psw = input('请输入密码:') login(username,psw) #请输入用户名:asdd return username,psw #请输入密码:4564648 print(login.get_n_psw()) #('asdd', '4564648')