一、封装
1.1 封装的引入
- 封装的概念: 封装是面向对象的三大特性之一。就是为了保护数据安全,形成的一个默认的规则,告知协同开发者,这个属性很重要,不要去随意修改。
- 使用封装的背景:隐藏对象中一些不希望被类的外部所访问到的属性或方法。
比如:
class Car():
def __init__(self, name, color):
self.name = name
self.color = color
def run(self):
print("这是一辆{0}的{1}".format(self.color, self.name))
def ddd(self):
print('汽车鸣笛了')
car = Car('法拉利', '红色')
car.run()
car.name = '中华田园犬' # 这样会把前面的类的属性修改了,相当于重新修改了实例属性。
car.run()
car.ddd()
--------------执行---------------
这是一辆红色的法拉利
这是一辆红色的中华田园犬
汽车鸣笛了
- 影响: 上面程序中,假如项目开发完成后,后面其他同时随意修改代码,代码本身没有影响,但是代码逻辑发生改变,造成影响。一般实际工作中,都是多开发人员协同开发,我们不能去修改当前的代码,因此引入了封装的概念,
1.2 封装的引入
下面介绍2种相对封装的方法: 私有属性 和 隐藏属性。
(1)私有属性
- 私有属性:方法外可以访问,告诉别人不要去轻易修改这个变量
- 定义格式: 一个下划线 +属性名称 (如:_name)
class Car():
def __init__(self, name, color):
self._name = name # _name 私有属性,外面可以访问,但是不要轻易去修改它
self.color = color
def run(self):
print("这是一辆{0}的{1}".format(self.color, self._name))
car = Car('法拉利', '红色')
car.run()
car._name = '帕萨特'
print(car._name) # 依然是可以修改的
--------------执行---------------
这是一辆红色的法拉利
帕萨特
(2)隐藏属性
- 隐藏属性: 在类的外面无法访问,是不可读的。相当于一个常量,外面不可以去修改。
- 定义格式: 2个下划线+ 属性名称 (如: __name)
class Car():
def __init__(self, name, color):
self.__name = name ## __name 私有属性,相当于一个常量,不可以更改
self.color = color
def run(self):
print("这是一辆{0}的{1}".format(self.color, self.__name))
car = Car('法拉利', '白色')
car.run()
# print(car.__name) # 隐藏属性,外面无法访问,是不可读的。
print(car._Car__name) # 可以通过这种方式获取私有属性。
--------------执行---------------
这是一辆白色的法拉利
法拉利
1.3 getter() 与 setter() 方法
上面我们介绍到了,为了保护代码,提供2种封装方式,私有属性和隐藏属性,都是为了防止代码外部修改属性或方法,但是如果真的要修改呢? 也有专门的方式可以访问到属性或方式: getter() 和 setter() 方法。
getter() 方法 : 提供给代码外部访问这个属性的方法(访问)。
setter() 方法:提供给代码外部修改这个属性的方法 (修改,重新赋值)。
class Car():
def __init__(self,name, color):
self._name = name # 隐藏属性,不可读的属性
self.color = color
def get_name(self): # getter方法,return返回到外层,提供给你访问这个属性的方法
return self._name
def set_name(self, name): #注意这里要传参
self._name = name # setter方法,重新赋值,提供给你修改这个属性的方法
def run(self):
print("这是一辆{0}的{1}".format(self.color,self._name))
car = Car('法拉利','红色')
print(car.get_name())
car.run()
car.set_name('保时捷')
print(car.get_name())
car.run()
--------------执行---------------
法拉利
这是一辆红色的法拉利
保时捷
这是一辆红色的保时捷
注意:
- 假如上面,魔法方法下面,既没有getter方法,也没有setter方法,就说明这个隐藏属性,不要去访问也不要去修改。
- 假如上面,魔法方法下面,如果只有getter 方法,说明上面隐藏属性是可读的,可以访问。
- 假如上面,魔法方法下面,如果只有setter 方法,说明上面隐藏属性是可以修改。
二、property 装饰器
2.1 引入property装饰器的背景
装饰器为了让我们更好的去定义属性。
比如上面,我们如果要去访问属性和调用方法时,需要:
print(cat.name) # 访问属性
car.name() # 调用方法
那么如果要去访问 get_name() 的属性呢 ?
print(car.get_name())
这样一来,是不是容易记混淆。所以,我们引入了@property装饰器,来帮助我们创建私有属性。
2.2 getter 装饰器的使用
class Car:
def __init__(self, name, color):
self._name = name
self.color = color
# property装饰器
@property
def get_name(self):
return self._name
def set_name(self, name):
self._name = name
def run(self):
print("这是一辆{0}的{1}".format(self.color, self._name))
car = Car('法拉利','红色')
car.run() # 调用方法
print(car._name) # 访问属性
# print(car.get_name())
print(car.get_name) # 去掉括号,直接使用属性
--------------执行---------------
这是一辆红色的法拉利
法拉利
法拉利
2.3 setter 装饰器的使用
class Car:
def __init__(self, name, color):
self._name = name
self.color = color
# getter方法装饰器
@property
def name(self):
return self._name
# setter 方法装饰器
@name.setter
def name(self, name): # 改成同名的方法
self._name = name
def run(self):
print("这是一辆{0}的{1}".format(self.color, self._name))
car = Car('保时捷','白色')
car.run()
# 使用set装饰器时,怎么去使用
car.name = '玛莎拉蒂'
print(car.name)
--------------执行---------------
这是一辆白色的保时捷
玛莎拉蒂
三、 继承
- 继承是面向对象三大特性之一。<封装,继承,多态>
- 通过继承我们可以使一个类获取到其他类中的属性和方法。
- 在定义类时,可以在类后面的括号种指定当前类的父类(超类、基类)。
- 继承提高了类的复用性,让类与类之间产生了关系,有了这个关系,才有了多态的特性。
3.1 继承的简介
class Doctor():
name = ''
age = ''
def study(self):
print('治病救人')
class Soldier():
name = ''
aget = ''
def study(self):
print('保家卫国')
- 上面这种情况下,代码逻辑都一样,相当于代码重复,那么这2个类怎么去优化呢 ?
class Person():
name = ''
age = ''
def study(self):
pass
3.2 继承的引入
class Animal(object):
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal): # 这样就叫继承,创建类的时候,后面括号里面写上你要继承的内容。
pass
dog = Dog() # 创建实例
dog.run() # 调用方法。
补充
isinstance() : 检测实例是否为一个类的实例的方法。
issubcalss(): 检测一个类是否为一个类的父类的方法。
print(isinstance(dog, Dog))
print(isinstance(dog, Animal))
print(issubclass(Animal, object))
--------------执行---------------
True
True
True
- object 是所有类的最大父类,处于最顶端,所有的类都是继承至object。在定义类时,如果没有些,默认所有类都继承至 object。
3.2 方法的重写(必须在继承的前提之下)
继承的特性:通过继承我们可以去查找其他类中的属性和方法,一层一层网上查找,直到找到为止。
- 会优先去当前对象中寻址是否具有该方法,如果有直接调用
- 如果没有,则取对象的父类中寻找,如果父类中有则直接调用;
- 如果父类中没有,则去父类的父类中寻找,依次类推,直到找到 object为止,如果最终没有找到就会报错。
class A(object): # object 是所有类共有的最大的父类
def test(self):
print('A.......')
class B(A):
pass
class C(B):
pass
c = C()
c.test()
# A.......
3.3 super方法的使用
super方法的作用:将被覆盖了的父类方法,重新调用。比如类Dog 中的sleep方法,在原有基础上又学习到继承的类中其他新的方法。
使用条件:
1.必须要有父类的继承。
2.必须要有方法的重写。使用super()
class Animal(object): # 括号中没有,默认继承object
def sleep(self):
print('动物会睡觉')
def run(self):
print('动物会跑')
class Dog(Animal):
def sleep(self): # 重写,可以查看左边栏监听,箭头向下表示继承,箭头向上表示重写
print('狗会睡觉')
super().sleep() # 获取到父类的方法,重新调用,2个都会有了。
dog = Dog()
dog.sleep()
dog.run()
------------------执行-----------------
狗会睡觉
动物会睡觉
动物会跑
注意:如果有多级继承,super只会学习上一级的方法。
class A(object):
def sleep(self):
print('1111')
class Animal(A):
def run(self):
print('动物会跑')
def sleep(self):
print('动物会睡觉')
class Dog(Animal):
def sleep(self):
print('狗会睡觉')
super().sleep() # 如果有多级继承,super只会学习上一级的方法。
dog = Dog()
dog.run()
dog.sleep()
--------------执行---------------
狗会睡觉
动物会睡觉
动物会跑
3.4 多重继承
- 什么时多重继承 ?
- 在Python 中可以为一个类指定多个父类。
- 定义格式: 在类名的() 里面添加多个类,来实现多重继承。
- 多重继承:会使子类同时拥有多个父类,并且获取到所有父类中的方法;
- 在开发中没有特殊情况下,我们应该尽量避免使用多重继承,因为多重继承会让我们的代码更加复杂。
- 如果在多个父类中有同名的方法,则会在第一个父类中寻找,然后找第二个,找第三个……一直向上查找,前面会覆盖后面的。
class Person1(object):
def chuiniu(self):
print('很会吹牛')
class Person2(object):
def paimapi(self):
print('很会拍马屁')
class Son(Person1, Person2): # 对应顺序
pass # pass 占位
son = Son()
son.chuiniu()
son.paimapi()
总结:
1. 在开发中没有特殊情况下,我们应该尽量避免使用多重继承,因为多重继承会让我们的代码更加复杂。
2. 尽量做到代码解耦合。