面向对象三大特性-封装
封装:
【封装】
隐藏对象的属性和实现细节,仅对外提供公共访问方式。
【好处】
1. 将变化隔离;
2. 便于使用;
3. 提高复用性;
4. 提高安全性;
【封装原则】
1. 将不需要对外提供的内容都隐藏起来;
2. 把属性都隐藏,提供公共方法对其访问。
广义封装:
把一些属性和方法放到类里,这本身就是一种封装
class Foo: role = ‘class’ def func(self): pass
狭义上的:
把属性和方法藏在类里,只能在类内部调用,不能在外部使用
class Dog: __role = 'dog' #私有的静态属性 (相当于内部自动变成 _Dog__role) def __discount(self): #私有的方法 print('in __func') def price(self): self.__discount() #私有的对象属性 def func(self): print(Dog.__role) #内部可以调用私有属性 print(Dog.__dict__) print(Dog._Dog__role) #知道有这种查看方法就行,一般不要用 这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形 {'__module__': '__main__', '_Dog__role': 'dog',.......} _Dog__role相当于内部外运加上了(_类名) #dog 类似加了一层密码 ------------------------ d = Dog() d.func() #内部调用 #dog
==============================
在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
#把fa定义成私有的,即__fa class A: def __fa(self): #在定义时就变形为_A__fa print('from A') def test(self): self.__fa() #这个self.__fa() 实际上是self._A__fa() 所以调用的是A内部的def__fa() class B(A): def __fa(self): print('from B') b=B() b.test() from A
================
私有的不能被继承:
class A: def __fa(self): #在定义时就变形为_A__fa print('from A') class B(A): def __init__(self): self.__fa() # 实际上是self._B__fa() b=B()
==============
狭义封装小结:
私有属性: 静态属性,方法,对象属性 3种
内部能调用,外部不能
不能被继承
当有一个属性,不想被外部使用,也不想被子类继承,只想内部使用,就定义成私有的
========================================================
property方法
property: 伪装 (__变量 配合起来就很强大了)
@property: 把一个方法伪装成一个属性
1.属性的值是方法的返回值
2.这个方法就不能有参数了
--------------
例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)
成人的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,name,height,weight): self.name = name self.height = height self.__weight = weight @property # 只是伪装作用 def bmi(self): # 方法不能有参数 return self.__weight / (self.height ** 2) kit = Person('kitty',1.75,85) print(kit.bmi) #kit.bmi 相当于 kit.bmi() #27.755
如果 print(kit.bmi()) 就相当于 print(27.755()) 报错:TypeError: 'float' object is not callable
-------------------------------------------------------------------------------------------------------------------------
class Goods: __discount = 0.8 #静态属性 def __init__(self,name,price): self.__name = name self.__price = price #原价 @property def name(self): return self.__name @property def price(self): #折后价 return self.__price * Goods.__discount apple = Goods('苹果',10) print(apple.price) apple.price = 100 报错:AttributeError: can't set attribute (安全:属性不能随便修改) print(apple.price) # __私有+property #让对象的属性变得更安全了
-------------------------------------------------------------------------------
property setter
例题三:
修改价格,加了一个if判断
class Goods: __discount = 0.8 #静态属性 def __init__(self,name,price): self.__name = name #将所有的数据属性都隐藏起来 self.__price = price #原价 @property def name(self): return self.__name #obj.name访问的是self.__name(这也是真实值的存放位置) @name.setter setter的位置只能传一个参数 obj.name = 'apple' def name(self,new_name): self.__name = new_name @property def price(self): #折后价 #在设定值之前进行类型检查 return self.__price * Goods.__discount #通过类型检查后,将值value存放到真实的位置self.__name @price.setter #绕了一大圈,就为了修改属性这个简单的操作,目的是 加个 if 判断 def price(self,new_price): #修改原价 if type(new_price) is int: #如果是float类型,必须输入浮点数 self.__price = new_price @price.deleter def price(self): raise TypeError('不能修改') #主动触发异常 apple = Goods('苹果',10) print(apple.price) #8.0 apple.price = 100 #settrt 改变的是对象的属性 print(apple.price) #property #80.0 ----------- banana = Goods('香蕉',10) print(banana.price) #16.0 print(apple.price) #创建新对象,苹果的价格没有变化 #80.0 ------------- del apple.price TypeError: 不能修改
封装:
@property @price.setter @price.deleter
1.方法伪装成数据属性 (例题一:print(kit.bmi) )
2.__私有+property, 让对象的属性变得更安全了 (例题二:修改价格,报错)
3.获取到的对象的值可以进行一些加工 (例题三:修改价格,成功, 但是只能传一个参数)
4.修改对象的值的同时可以进行一些验证 (例题三:判断输入的是否是数字)
========================================================
1.如果只用@property, 属性就是固定的了,不能更改了
2.为什么setter 呢?
就是每次操作,都可以 if判断, raise主动触发异常 等等操作
让对象 的属性变得更安全了
-------------------------------------------------------------------------------
=================================
class Foo: # @property def AAA(self): print('get的时候运行我啊') @AAA.setter 如果没有@property 报错:AttributeError: 'function' object has no attribute 'setter' def AAA(self,value): print('set的时候运行我啊') @AAA.deleter def AAA(self): print('delete的时候运行我啊') #只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter f1=Foo() f1.AAA #property f1.AAA='aaa' #setter del f1.AAA #deleter #get的时候运行我啊 #set的时候运行我啊 #delete的时候运行我啊
类方法:@classmethod
class Goods: __discount = 0.8 # 想把类的静态属性改了 def change_discount(self,new_discount): self.__discount = new_discount def get_discount(self): return self.__discount # Goods._Goods__discount = 1 #不能用这个 apple = Goods() print(Goods._Goods__discount) # 0.8 apple.change_discount(0.75) print(apple.get_discount()) # 1 类的实例对象修改的是对象自己的属性 print(Goods._Goods__discount) # 0.8 但是类的静态变量没变
# 错误示范
----------------------
class Goods: __discount = 0.8 @classmethod #类方法 def change_discount(cls,new_discount): #cls 不需要self参数,直接传 类 cls.__discount = new_discount @classmethod def get_discount(cls): return cls.__discount # 不需要创建对象 print(Goods.get_discount()) #0.8 Goods.change_discount(0.75) print(Goods.get_discount()) #类的属性改变了
============================
类方法
调用:不需要实例化 直接用类名调用就好
定义:不用接受self参数,默认传cls,cls就代表当前方法所在的类
什么时候用类方法?
需要使用静态变量 且 不需要和对象相关的任何操作的时候
类方法:必须通过类的调用,而且此方法的意义:就是对类里面的变量或者方法进行修改添加。
=========================
静态方法:@staticmethod
先来看一段代码
class A: def func(): #没有self, 有红色下划线 (这已经是一个静态方法了) print(123) A.func() #123 #也不报错 但是不符合规范
定义一个静态方法,要使用@staticmethon
class A: @staticmethod #如果要定义一个不传参数的方法,要加@staticmethon 这样就不报错了 def func(name,age): #静态方法 print(name,age) A.func('kitty',18) #'kitty',18
和普通的调用函数没有什么区别了
========================
静态方法: 就是一个不依赖类以及对象的一个普通函数,为什么在类里面?
为了保证代码的一致性,可调控性,整洁性。
静态方法:
如果这个方法 ,既不需要操作静态变量,也不需要使用对象相关的操作,就用静态方法
---------------------------------------------------------------------------------
面向对象编程:
什么是面向对象编程?
面向对象是一种思想,但是它也有一种规范,假如整篇用面向对象编程,那么整个程序里面不可以写除了类以外的其他东西,不可以定义函数,所有的函数必须定义到类里面.
staticmethod--专门为面向对象编程提供的一个方法
它完全可以当做普通函数去用,只不过这个函数要通过类名.函数名调用
其他 传参 返回值 完全没有区别
======================================
类里面,一共可以定义这三种方法:
1.普通方法 self (方法里面用到对象相关的变量)
2.类方法 cls (方法里面用到类的静态变量或静态方法)
3.静态方法 (方法里面啥都没有用到)
class A: @staticmethod def func1(name): #静态方法 print(123) @classmethod def func2(cls): # 静态方法 print(123) def func3(self):pass a = A() print(a.func1) #静态方法 <function A.func1 at 0x00000000023188C8> print(a.func2) #类方法 : 绑定到A类的func <bound method A.func2 of <class '__main__.A'>> print(a.func3) #普通方法:绑定到A类对象的func <bound method A.func3 of <__main__.A object at 0x000000000231B160>>
类里面,一共可以定义三种方法:
- 普通方法: self 对象调用 (方法里面用到对象相关的变量)
- 类方法: @classmethod 类调用 (方法里面用到类的静态变量或静态方法)
- 静态方法: @staticmethod 类调用 (方法里面啥都没有用到)
( 类方法和静态方法 对象也可以调用,但没必要为了调用 去创建一个对象)
=================================================