封装 :把一些属性和方法放到类里 这本身就是一种封装
# 封装 : 把属性和方法藏在类里 我只能在类内部调用,不能再外部使用
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
私有变量和私有方法
在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
# class Dog: # __role = 'dog' #私有的静态属性 # print(Dog._Dog__role) #在外部调 #从类的外面不能直接调用,在类内的使用加上了一层密码:_类名
class Dog: __role = 'dog' #私有的静态属性 def func(self): print(Dog.__role) #在内部调 理解为_类名__名字,类里面省掉了_类名,外部需要手动加上_类名 d = Dog() d.func() 输出; dog
方法也可以私有起来:
class Dog: __role = 'dog' # 私有的静态属性 def __func(self): # 私有的方法 print("in_func") d = Dog() # d.func() 这样调调不到 d._Dog__func() #这样调用
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
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,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。
#定义一个私有变量\属性\方法 : __名字
#在类的内部可以直接用 : __名字
#在类的外部不能直接使用,如果一定要用,在私有方法之前加上:_类名,变成 _类名__名字
#在类外的名字 通过__dict__就可以查看
例子:
class Room: def __init__(self, name, price, length, width): self.name = name self.price = price self.__length = length self.__width = width def area(self): return self.__length*self.__width house = Room("小超超", 1000000, 2, 1) print(house.price) print(house.area()) 输出; 1000000 2
#私有的
#私有的静态属性、方法、对象属性
#使用__名字的方式调用,保证在类内部可以调用,外部不行
class A: def __func(self): #_A__func() print('__a_func') class B(A): def __init__(self): self.__func() #_B__func() b = B() 输出: 报错
结论:
#私有的 不能被继承
# 当有一个名字,不想被外部使用也不想被子类继承,只想在内部使用的时候就定义私有的
封装与扩展性
封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。这就提供一个良好的合作基础——或者说,只要接口这个基础约定不变,则代码改变不足为虑。
#类的设计者 class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积 return self.__width * self.__length #使用者 >>> r1=Room('卧室','egon',20,20,20) >>> r1.tell_area() #使用者调用接口tell_area
类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room: def __init__(self,name,owner,width,length,high): self.name=name self.owner=owner self.__width=width self.__length=length self.__high=high def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,
而且外部调用感知不到,仍然使用该方法,但是功能已经变了 return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能 >>> r1.tell_area()
对象继承与property:
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
计算李岩的bmi指数:
class Person: def __init__(self, name, height, weight): self.name = name self.height = height self.__weight = weight def bmi(self): return self.__weight / (self.height ** 2) li = Person('李岩',1.75,85) print(li.bmi()) 输出; 27.755102040816325
#添加一个@property class Person: def __init__(self, name, height, weight): self.name = name self.height = height self.__weight = weight @property def bmi(self): return self.__weight / (self.height ** 2) li = Person('李岩',1.75,85) print(li.bmi) #bim方法被伪装成一个属性 # print(li.bmi()) 这时候不能这么调用
输出;
27.755102040816325
为什么要用property
#@property 把一个方法 伪装成一个属性
#1.属性的值 是这个方法的返回值
#2.这个方法不能有参数了
将一个类的方法伪装成属性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个方法然后计算出来的,这种特性的使用方式遵循了统一访问的原则
例子; 1.圆的周长和面积;
from math import pi class Circle: def __init__(self, radius): #圆的半径radius self.r = radius @property def area(self): return pi * self.r**2 #计算面积 @property def perimeter(self): return 2*pi*self.r #计算周长 c=Circle(10) print(c.r) print(c.area) #可以向访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值 print(c.perimeter) #同上 ''' 输出结果: 10 314.1592653589793 62.83185307179586 '''
2.超市店庆打折;
全场8折:
私有属性与@property 配合
class Goods: discount = 0.8 #静态属性 def __init__(self, name, price): self.__name = name self.__price = price #原价 @property def price(self): #折后价 return self.__price * Goods.discount apple = Goods('苹果', 10) print(apple.price) banana = Goods("香蕉", 2.5) print(banana.price) 输出: 8.0 2.0
class Goods: discount = 0.8 #静态属性 def __init__(self, name, price): self.__name = name self.__price = price #原价 @property def price(self): #折后价 return self.__price * Goods.discount apple = Goods('苹果', 10) print(apple.price) banana = Goods("香蕉", 2.5) print(banana.price)
让价格商品价格可改: class Goods: discount = 0.8 #静态属性 def __init__(self, name, price): self.__name = name self.__price = price #原价 @property #获取name def name(self): return self.__name @name.setter #修改name def name(self,new_name): self.__name = new_name @property #获取price def price(self): #折后价 return self.__price * Goods.discount @price.setter # 让商品价格可以改 修改price def price(self, new_price): #修改原价 # if type(new_price) is int: #在方法里写一些逻辑判断,维护了代码中的属性的安全性 self.__price = new_price apple = Goods('苹果', 10) apple.price = 6 #使用了setter print(apple.price) #使用了property
#封装
# __私有+property
#让对象的属性变得更安全了
#获取到的对象的值可以进行一些加工
@属性.setter
#修改对象的值的同时可以进行一些验证
一个静态属性property本质就是实现了get,set,delete三种方法 class Foo: @property def AAA(self): print('get的时候运行我啊') @AAA.setter def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() print(f1.AAA) #property 这个地方表面上看是拿到了一个属性,实际上是调用了一个方法,方法有返回值,就是我的属性值,没有返回值,默认返回值None f1.AAA='aaa' #setter del f1.AAA #deleter 输出: get的时候运行我啊 None set的时候运行我啊 delete的时候运行我啊
deleter 怎么用;(非常不常用)
class Goods: __discount = 0.8 #静态属性 def __init__(self,name,price): self.__name = name self.__price = price #原价 @property def name(self): return self.__name @name.setter def name(self,new_name): self.__name = new_name @name.deleter def name(self): del self.__name @property def price(self): #折后价 return self.__price * Goods.__discount @price.setter def price(self,new_price): #修改原价 if type(new_price) is int: self.__price = new_price apple = Goods('苹果', 10) del apple.name
classmethod和staticmthod方法
#定义一个类
#类里面的方法
#并没有用到self
class Goods: __discount = 0.8 def change_discount(self,new_discount): Goods.__discount = new_discount apple = Goods() apple.change_discount(0.75)
上面例子,改动了一个类的属性,却要实例化一个对象来操作,很诡异,类的属性不是该对象特有的
class Goods: __discount = 0.8 @classmethod #类方法 def change_discount(cls, new_discount): #self不传了,换成cls(cls理解为Goods), cls.__discount = new_discount @classmethod def get_discount(cls): return cls.__discount Goods.change_discount(0.75) print(Goods.get_discount()) 输出; 0.75
#类方法
#调用:不需要实例化 直接用类名调用就好
#定义:不用接受self参数,默认传cls,cls就代表当前方法所在的类
#什么时候用类方法?
#需要使用静态变量 且 不需要和对象相关的任何操作的时
#静态方法
#如果这个方法 既不需要操作静态变量
# 也不需要使用对象相关的操作,
# 就使用静态方法
class A: @staticmethod def func(name): #静态方法 print(123) A.func('alex')
#面向对象编程:
#整篇代码都是面向对象的,不可以在你的程序里面写除了类以外的其他东西,不可以定义函数,所有的函数必须定义到类里面,但是有的函数跟这些类都不产生关系,不需要实例化,就可以直接执行,这样的函数,不需要定义成需要实例化的函数,直接定义成一个静态函数,放到类里面。
#专门为面向对象编程提供的一个方法——staticmethod
#它完全可以当做普通函数去用,只不过这个函数要通过 类名.函数名 调用
#其他 传参 返回值 完全没有区别
#类里面,一共可以定义这三种方法:
具体使用哪种方法,是根据你在这个方法里需要用到的变量来定的,
#普通方法 self #使用对象名去调用
#类方法 cls #用到类里面的变量 @classmethod
#静态方法 # 啥都没用上 @staticmethod
class A: @staticmethod def func1(name): #静态方法 print(123) @classmethod def func2(cls): # 类方法 print(123) def func3(self):pass a = A() print(a.func1) #静态方法 print(a.func2) #类方法 : 绑定到A类的func print(a.func3) #普通方法:绑定到A类对象的func
#静态方法和类方法 都是直接可以使用类名调用
#普通方法:对象调用