面向对象 封装 property setter classmethod staticmethod
【封装】
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有变量和私有方法
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
私有变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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.类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。
2.这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。
3.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
这种变形需要注意的问题是:
1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N
2.变形的过程只在类的内部生效,在定义后的赋值操作,不会变形
私有方法
3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
>>> 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
在类中,静态属性,方法,对象属性都可以变成私有的,只需要在这些名字之前加上__
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# class D: # def __func(self): # '_D__func' # print('in func') # # class E(D): # def __init__(self): # self.__func() # '_E__func' # e = E() # 私有的名字不能被子类继承 # class D: # def __init__(self): # self.__func() # def __func(self): # print('in D') # # class E(D): # def __func(self): # print('in E') # e = E()
私有的名字,在类内使用的时候,就是会变形成_该类名__方法名
以此为例 :没有双下换线会先找E中的func
但是有了双下划线,会在调用这个名字的类D中直接找_D__func
# class F: # pass # F.__name = 'alex' # 不是在创建私有属性 # print(F.__name) # print(F.__dict__) # 变形只在类的内部发生 # class F: # def ADCa(self): # self.__name = 'alex' # _F__name # # f = F() # f.ADCa() # print(f._F__name) # java中的对比 # public 公有的 在类的内部可以使用,子类可以使用,外部可以使用 python中所有正常的名字 # protect 保护的 在类的内部可以使用,子类可以使用,外部不可以使用 python中没有 # private 私有的 只能在类的内部使用,子类和外部都不可以使用 python中的__名字 # 私有的用法 # 当一个方法不想被子类继承的时候 # 有些属性或者方法不希望从外部被调用,只想提供给内部的方法使用
人体BMI指数
体质指数(BMI)=体重(kg)÷身高^2(m)
写一个类 描述人体BMI指数
property setter
lass Person: def __init__(self,name,weight,height): self.name = name self.__height = height self.__weight = weight # self.bmi = self.__weight / self.__height ** 2 # self.bmi = self.cal_BMI() def cal_BMI(self): return self.__weight / self.__height ** 2 @property def bmi(self): return self.__weight / self.__height ** 2 p = Person('大表哥',92,1.85) # print(p.cal_BMI()) # p.cal_BMI() # bmi是一个名词 # print(p.bmi) # bmi是一个名词 # p._Person__weight = 90 # print(p.bmi) # 将一个方法伪装成一个属性 # 并不会让你的代码有什么逻辑上的提高 # 只是从调用者的角度上换了一种方式,使之看起来更合理 # 单纯的在init中计算 # class Person: # def __init__(self,name,weight,height): # self.name = name # self.__height = height # self.__weight = weight # self.bmi = self.__weight / self.__height ** 2 # # p = Person('大表哥',92,1.85) # # print(p.bmi) # bmi是一个名词 # p._Person__weight = 90 # 3天 # print(p.bmi) # class Person: # def __init__(self,name,weight,height): # self.name = name # self.__height = height # self.__weight = weight # @property # def bmi(self): # return self.__weight / self.__height ** 2 # # p = Person('大表哥',92,1.85) # print(p.bmi) # p._Person__weight = 90 # print(p.bmi) # @property 能够将一个方法伪装成一个属性 # 从原来的的对象名.方法名(),变成了对象名.方法名 # 只是让代码变的更美观 # 如果有重名的名字 # class Person: # def __init__(self,name,weight,height): # self.name = name # self.__height = height # self.__weight = weight # @property # def bmi(self): # return self.__weight / self.__height ** 2 # print(Person.__dict__) # p = Person('大表哥',92,1.85) # print(p.__dict__) # print(p.bmi) # 对这个属性 只能看了 # 被property装饰的bmi仍然是一个方法 存在Person.__dict__ # 对象的.__dict__中不会存储这个属性 # 在一个类加载的过程中,会先加载这个中的名字,包括被property装饰的 # 在实例化对象的时候,python解释器会先到类的空间里看看有没有这个被装饰的属性, # 如果有就不能再在自己对象的空间中创建这个属性了 # 圆形类 # 半径 面积 周长 # from math import pi # class Circle: # def __init__(self,r): # self.r = r # def cal_area(self): # return self.r**2*pi # def cal_perimeter(self): # return 2*pi*self.r # c = Circle(10) # print(c.cal_area()) # print(c.cal_perimeter()) # 将方法伪装成属性,方法中一般涉及的都是一些计算过程 # from math import pi # class Circle: # def __init__(self,r): # self.r = r # @property # def area(self): # return self.r**2*pi # # @property # def perimeter(self): # return 2*pi*self.r # c = Circle(10) # print(c.area) # print(c.perimeter) # c.r = 15 # print(c.area) # print(c.perimeter) # __name setter deleter # class Person0: # def __init__(self,name): # self.name = name # # p = Person0('alex') # print(p.name) # p.name = 'sb' # p.name = 123 # class Person: # def __init__(self,name): # self.__name = name # 私有的属性了 # @property # def name(self): # return self.__name # # def set_name(self,new_name): # if type(new_name) is str: # self.__name = new_name # else: # print('您提供的姓名数据类型不合法') # # p = Person('alex') # print(p.name) # # 和直接定义name属性有什么区别??? # p.set_name('alex_sb') # print(p.name) # p.set_name(123) # print(p.name) # 方法伪装成的属性的修改 # class Person: # def __init__(self,n): # self.__name = n # 私有的属性了 # @property # def name(self): # return self.__name # # @name.setter # 重要程度 *** # def name(self,new_name): # if type(new_name) is str: # self.__name = new_name # else: # print('您提供的姓名数据类型不合法') # # p = Person('alex') # print(p.name) #def name(self): # p.name = 'alex_sb' #def name(self,new_name): # print(p.name) #def name(self): # p.name = 123 #def name(self,new_name): # print(p.name) #def name(self): # 方法伪装成的属性的删除 class Person: def __init__(self,n): self.__name = n # 私有的属性了 @property # 重要程度 **** def name(self): return self.__name # @name.deleter # def name(self): # print('name 被删除了') # @name.deleter # 重要程度* # def name(self): # del self.__name # p = Person('alex') # print(p.name) # del p.name # 只是执行了被@name.deleter装饰的函数 # print(p.name) #@property --> func 将方法伪装成属性,只观看的事儿 #@func.setter --> func 对伪装的属性进行赋值的时候调用这个方法 一般情况下用来做修改 #@func.deleter --> func 在执行del 对象.func的时候调用这个方法 一般情况下用来做删除 基本不用 # 商品的 折扣 # 有一个商品 : 原价 折扣 # 当我要查看价格的时候 我想看折后价 # class Goods: # def __init__(self,name,origin_price,discount): # self.name = name # self.__price = origin_price # self.__discount = discount # # @property # def price(self): # return self.__price * self.__discount # @price.setter # def price(self,new_price): # if type(new_price) is int or type(new_price) is float: # self.__price = new_price # apple = Goods('apple',5,0.8) # print(apple.price) # # 修改苹果的原价 # apple.price = 8 # print(apple.price) # 将一些需要随着一部分属性的变化而变化的值的计算过程 从方法 伪装成属性 # 将私有的属性保护起来,让修改的部分增加一些约束,来提高程序的稳定性和数据的安全性
classmethod staticmethod
店庆 全场八折
class Goods: __discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * Goods.__discount @classmethod def change_discount(cls,new_discount): # 类方法 可以直接被类调用 不需要默认传对象参数 只需要传一个类参数就可以了 cls.__discount = new_discount Goods.change_discount(1) # 不依赖对象的方法 就应该定义成类方法 类方法可以任意的操作类中的静态变量 apple = Goods('apple',5) banana = Goods('banana',8) print(apple.price) print(banana.price) # 折扣变了 店庆结束 恢复折扣 # apple.change_discount(1) # 如果要改变折扣 是全场的事情 不牵扯到一个具体的物品 所以不应该使用对象来调用这个方法 # print(apple.price) # print(banana.price) # staticmethod # 当一个方法要使用对象的属性时 就是用普通的方法 # 当一个方法要使用类中的静态属性时 就是用类方法 # 当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法 # def login(): # user= input('user :') # if user == 'alex':print('success') # else :print('faild') # # login() class Student: def __init__(self,name):pass @staticmethod def login(a): # login就是一个类中的静态方法 静态方法没有默认参数 就当成普通的函数使用即可 user = input('user :') if user == 'alex': print('success') else: print('faild') Student.login(1)
完全面向对象编程
先登录 后 实例化
还没有一个具体的对象的时候 就要执行login方法
使用什么样的方法要看具体用到了哪些名称空间中的变量
当一个方法要使用对象的属性时 就是用普通的方法
当一个方法要使用类中的静态属性时 就是用类方法
当一个方法要既不使用对象的属性也不使用类中的静态属性时,就可以使用staticmethod静态方法