封装、继承、多态、反射
1.封装
- 封装就是将属性隐藏,不让外界发现或使用
- 将可以允许外界使用的内容通过接口开发,让用户通过接口使用
- 隐藏属性的方法是通过 __变量名
1.1封装之隐藏属性
- 隐藏数据属性
class Teacher:
def __init__(self, name, age):
# 将名字和年纪都隐藏起来
self.__name = name
self.__age = age
# 对外提供访问老师信息的接口
def tell_info(self):
print('姓名:%s,年龄:%s' % (self.__name, self.__age))
# 对外提供设置老师信息的接口,并附加类型检查的逻辑
def set_info(self, name, age):
if not isinstance(name, str):
raise TypeError('姓名必须是字符串类型')
if not isinstance(age, int):
raise TypeError('年龄必须是整型')
self.__name = name
self.__age = age
# 实例化类得到对象
teacher = Teacher("serein", 18)
# 查看对象的信息
teacher.tell_info() # 姓名:serein,年龄:18
# 调用对象的方法修改对象的信息 , 符合要求就能修改成功
teacher.set_info('formerly', 22)
# 查看对象的信息
teacher.tell_info() # 姓名:formerly,年龄:22
- 隐藏函数属性
class ATM:
# 插卡
def __card(self):
print('插卡')
# 身份认证
def __auth(self):
print('用户认证')
# 输入金额
def __input(self):
print('输入取款金额')
# 打印小票
def __print_bill(self):
print('打印账单')
# 取钱
def __take_money(self):
print('取款')
# 取款功能
def withdraw(self):
self.__card()
self.__auth()
self.__input()
self.__print_bill()
self.__take_money()
obj = ATM()
obj.withdraw()
# 插卡
# 用户认证
# 输入取款金额
# 打印账单
# 取款
1.2 property装饰器
- property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
- 将函数属性,包装成数据属性
class Foo:
def __init__(self, val):
# 将属性隐藏
self.__NAME = val
@property
def name(self):
print(f'我在获取的时候被触发了')
return self.__NAME
@name.setter # 设置值
def name(self, value):
print(f'我再修改的时候被触发了')
# 在设定值之前进行类型检查
if not isinstance(value, str):
raise TypeError('%s must be str' % value)
# 通过类型检查后,将值value存放到真实的位置self.__NAME
self.__NAME = value
@name.deleter # 删除值
def name(self):
print(f'删除的时候调用了我')
raise PermissionError('Can not delete')
f = Foo('serein')
# print(f.name)
# 触发name.setter装饰器对应的函数name(f,'formerly')
f.name = 'formerly'
print(f.name)
# 触发name.setter对应的的函数name(f,123),抛出异常TypeError
f.name = 123
# 触发name.deleter对应的函数name(f),抛出异常PermissionError
del f.name
2.继承
- 子类可以继承父类中的方法和类变量(不是拷贝一份,父类的还是属于父类,子类可以继承而已)
- 父类也可以叫基类,子类也可以叫派生类
class Base:
def func(self):
print("Base.func")
class Son(Base):
def show(self):
print("Son.show")
s1 = Son()
s1.show() # Son.show
s1.func() # 优先在自己的类中找,自己没有才去父类。
# Base.func
2.1 属性查找顺序
class Base:
def f1(self):
print('before')
self.f2() # slef是obj对象(Foo类创建的对象) obj.f2
print('base.f1')
def f2(self):
print('base.f2')
class Foo(Base):
def f2(self):
print('foo.f2')
obj = Foo()
obj.f1() # 优先去Foo类中找f1,因为调用f1的那个对象是Foo类创建出来的。
>>> before
>>> foo.f2
>>> base.f1
b1 = Base()
b1.f1()
>>> before
>>> base.f2
>>> base.f1
查找总结
- 执行对象.方法时,优先去当前对象所关联的类中找,没有的话才去她的父类中查找。
- Python支持多继承:先继承左边、再继承右边的。
- self到底是谁?去self对应的那个类中去获取成员,没有就按照继承关系向上查找 。
2.2 经典类和新式类
# 经典类和新式类
# Python2区分经典类和新式类
# 在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
# 在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
# Python3都是新式类
# 在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
2.3 隐藏属性
class Foo:
# 变形为_Foo__fa
def __f1(self):
print('Foo.f1')
def f2(self):
print('Foo.f2')
# 变形为self._Foo__fa,因而只会调用自己所在的类中的方法
# _Foo__f1
self.__f1()
#
self._Bar__f1()
class Bar(Foo):
# 变形为_Bar__f1
def __f1(self):
print('Bar.f1')
# _Bar__f1
b = Bar()
# 在父类中找到f2方法,进而调用b._Foo__f1()方法,同样是在父类中找到该方法
b.f2()
# 隐藏属性 : 隐藏给当前类,除了当前类,其他继承的子类均不能找到我隐藏的属性
2.4 抽象和继承
# 继承和抽象
# 继承描述的是子类与父类之间的关系
# 抽象即抽取类似或者说比较像的部分
# 抽象分成两个层次
# 将奥巴马和梅西这俩对象比较像的部分抽取成类;
# 将人,猪,狗这三个类比较像的部分抽取成父类。
# 继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
# 抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类
# 继承与重用性
# 抽象出 猫 和 狗 两个类
# 猫类 : 喵喵叫、吃、喝、拉、撒、睡
# 狗类 : 汪汪叫、吃、喝、拉、撒、睡
# 猫和狗有大量相同的内容
class Cat:
def call(self):
print '喵喵叫'
def eat(self):
pass
def drink(self):
pass
def shit(self):
pass
def pee(self):
pass
class Dog:
def call(self):
print '汪汪叫'
def eat(self):
pass
def drink(self):
pass
def shit(self):
pass
def pee(self):
pass
# 吃、喝、拉、撒是猫和狗都具有的功能,而我们却分别的猫和狗的类中编写了两次
# 如果使用 继承 的思想去实现功能
# 动物:吃、喝、拉、撒
# 猫:喵喵叫(猫继承动物的功能)
# 狗:汪汪叫(狗继承动物的功能)
class Animal(object):
# 吃喝拉撒
def eat(self):
print(f'{self.name} 正在吃饭 ')
def drink(self):
print(f'{self.name} 正在喝水 ')
def shit(self):
print(f'{self.name} 正在尿尿 ')
def pee(self):
print(f'{self.name} 正在大便 ')
# 猫类
class Cat(Animal):
# 初始化猫猫名字
def __init__(self, name):
self.name = name
def cry(self):
print(f'{self.name} 正在喵喵叫 ')
# 狗类
class Dog(Animal):
def __init__(self, name):
self.name = name
def cry(self):
print(f'{self.name} 正在汪汪叫 ')
# 实例话对象
cat_one = Cat(name='小黄')
# 调用父类方法
cat_one.eat() # 小黄 正在吃饭
# 实例话对象
cat_two = Cat(name='小花')
# 调用父类方法
cat_two.drink() # 小花 正在喝水
# 实例话对象
dog_one = Dog(name='旺财')
# 调用父类方法
dog_one.eat() # 旺财 正在吃饭
2.5 继承顺序
2.5.1 非菱形结构继承顺序
- Python中子类可以同时继承多个父类,如A(B,C,D)
- 如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性
2.5.2 菱形结构继承顺序
- 如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先
- 深度优先,当类是经典类时,多继承情况下,在查找属性不存在时,会按照深度优先的方式查找下去
- 广度优先,当类是新式类时,多继承情况下,在查找属性不存在时,会按照广度优先的方式查找下去
class A(object):
def test(self):
print('from A')
class B(A):
def test(self):
print('from B')
class C(A):
def test(self):
print('from C')
class D(B):
def test(self):
print('from D')
class E(C):
def test(self):
print('from E')
class F(D, E):
pass
f1 = F()
f1.test()
# 新式类继承顺序:F->D->B->E->C->A
# 经典类继承顺序:F->D->B->A->E->C
2.6 派生
- 派生是继承的延伸
- 派生是指,子类继承父类,派生出自己的属性和方法,并且重用父类的属性和方法
- Super()超类
class Animal(object):
# 初始化方法
def __init__(self):
self.name = 'xxx'
self.animal_class = ['Cat', 'Dog']
def eat(self):
print(f'{self.name} 正在吃饭!')
class Cat(Animal):
# 我自己的初始化方法
def __init__(self, name):
# 如果我定义了animal_class,会覆盖掉父类里面的 animal_class
self.animal_class = ['Cat', 'Dog','zzp']
# super 超级 , 将父类里面的 init 方法加载到我当前类中
# 【1】方式一:调用super()会得到一个特殊的对象
# 该对象专门用来引用父类的属性,且严格按照MRO规定的顺序向后查找
super().__init__()
# 【2】方式二:指定类的加载 指定类 里面的 __init__() 方法
# Animal.__init__(self)
self.name = name
# super().__init__()
c = Cat(name='小花')
print(c.animal_class) # ['Cat', 'Dog'] # 定义在超类前
print(c.name) # 小花
# 如果子类初始化方法的属性值定义在 super().__init__()前面的话,打印属性值时,出现的是父类的属性值,super()调用时,冲刷了子类定义的属性值
# 反知子类初始化方法的属性值定义在 super().__init__()后面的话,打印属性值时,出现的是子类自己定义的属性值,定义的属性值冲刷了super继承的父类的属性值
2.7 组合
- 在一个类中以另外一个类的对象作为数据属性,称为类的组合
- 组合与继承都是用来解决代码的重用性问题
class Course:
def __init__(self, name, period, price):
self.name = name
self.period = period
self.price = price
def tell_info(self):
print(f'当前课程名字 {self.name} 当前课程周期 {self.period} 当前课程价格 {self.price}')
class Date:
def __init__(self, year, mon, day):
self.year = year
self.mon = mon
self.day = day
def tell_birth(self):
print(f'当前生日 {self.year} 年 {self.mon} 月 {self.day} 日')
class People:
school = '清华大学'
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
# Teacher类基于继承来重用People的代码
# 基于组合来重用Date类和Course类的代码
class Teacher(People):
# 老师是人
def __init__(self, name, sex, age, title, year, mon, day):
# 初始化老师的名字,性别,年龄
super().__init__(name, age, sex)
# 老师有生日
self.birth = Date(year, mon, day)
# 老师有课程,可以在实例化后,往该列表中添加Course类的对象
self.courses = []
def teach(self):
print(f'当前老师正在授课 {self.name}')
# # 实例化Date得到对象
# data = Date(year=2000,mon=9,day=25)
# print(data.tell_birth())
python = Course('python', '3mons', 3000.0)
linux = Course('linux', '5mons', 5000.0)
teacher1 = Teacher('serein', 'male', 18, '金牌讲师', 1999, 12, 17)
# teacher1有两门课程
teacher1.courses.append(python)
teacher1.courses.append(linux)
for i in teacher1.courses:
i.tell_info()
teacher1.birth.tell_birth()
teacher1.teach()
# teacher1.courses.append(python)
# teacher1.courses.append(linux)
#
# # 重用Date类的功能
# teacher1.birth.tell_birth()
#
# # 重用Course类的功能
# for obj in teacher1.courses:
# obj.tell_info()
# 当前生日 1987 年 3 月 23 日
# 当前课程名字 python 当前课程周期 3mons 当前课程价格 3000.0
# 当前课程名字 linux 当前课程周期 5mons 当前课程价格 5000.0
2.7.1组合封装的区别
- 组合与继承都是有效地利用已有类的资源的重要方式。但是二者的概念和使用场景皆不同
- 继承主要是用来提取所有子类共有的功能的,总结成一个父类
- 通过继承建立了派生类与基类之间的关系,它是一种'是'的关系,比如白马是马,人是动物
- 当类之间有很多相同的功能,提取这些共同的功能做成基类,用继承比较好,比如老师是人,学生是人
- 组合就相当于将不同的数据类型,包括八大基本数据类型,包括我们的对象,包括我们的类总结到一起,可以在一个类的对象里面调用其他类的对象和方法
- 用组合的方式建立了类与组合的类之间的关系,它是一种‘有’的关系
- 比如教授有生日,教授教python和linux课程,教授有学生s1、s2、s3...
2.8 抽象类
- 在python中,利用abc模块实现抽象类
- 和 Java的接口一样:我在父类接口中定义了几个方法,只要你这个类继承了我的接口,你就必须在子类给我实现这几个方法,可以不写内容,但必须写方法,否则报错
# 抽象类的书写步骤
# 【1】创建一个父类
# 【2】必须导入 import abc
# 【3】父类必须继承一个属性 class Parent(metaclass=abc.ABCMeta):
# 【4】在想要子类中必须实现的方法上面加一个 装饰器 : @abc.abstractmethod
# 【5】子类继承父类,子类必须重写父类中被添加 了 @abc.abstractmethod 的方法
import abc
# 定义父类,父类必须继承 metaclass=abc.ABCMeta
class Parent(metaclass=abc.ABCMeta):
# 在子类中必须实现的方法: read 和 write
# 在子类中可以不实现的的方法:check
# 声明子类中必须实现的方法,要加装饰器 @abc.abstractmethod
@abc.abstractmethod
def read(self):
...
# 声明子类中必须实现的方法,要加装饰器 @abc.abstractmethod
@abc.abstractmethod
def write(self):
...
def check(self):
...
# 创建一个子类
class SonTwo(Parent):
def read(self):
...
def write(self):
...
class SonOne(Parent):
...
som_two = SonTwo()
son_one = SonOne()
# TypeError: Can't instantiate abstract class SonOne with abstract methods read, write
3.多态
- 多态是面向对象编程中的一个概念,指的是同一种操作在不同的对象上有不同的行为。简而言之,多态允许不同类的对象对相同的消息作出响应,表现出不同的行为
3.1鸭子类型
- 鸭子类型是一种动态类型的概念,它关注对象的行为而非其类型。
- 鸭子类型的核心思想是:如果一个对象走起路来像鸭子、游起泳来像鸭子,那么它就可以被视为鸭子,而不管它是否真正是鸭子
- 在鸭子类型中,关注的是对象是否具有某些方法或行为,而不是关注对象的具体类型
class Animal:
def make_sound(self):
pass
class Dog:
def sound(self):
return "Woof!"
class Cat:
def sound(self):
return "Meow!"
class Duck:
def sound(self):
return "Quack!"
def make_sound(animal):
return animal.sound()
dog = Dog()
cat = Cat()
duck = Duck()
print(make_sound(dog)) # 输出 "Woof!"
print(make_sound(cat)) # 输出 "Meow!"
print(make_sound(duck)) # 输出 "Quack!"
方法补充issubclass 和 isinstance
# 检查对象类型 isinstance
print(isinstance('serein',str)) # True
print(isinstance('serein',int)) # False
class People(object):
...
p = People()
print(isinstance(People,object)) # True
print(isinstance(p,object)) # True
# 判断一个类是否是一个类的子类的
class Person(object):
...
class Student(Person):
...
class Teacther:
...
print(issubclass(Student,Person)) # True
print(issubclass(Teacther,Person)) # False
print(issubclass(Person,object)) # True
print(issubclass(Teacther,object)) # True
4. 反射
- 反射是一种程序可以访问、检测和修改其本身状态或行为的能力
- 在 Python 中,反射主要指通过字符串的形式操作对象的属性
- 通过字符串的形式操作对象相关的属性,python中的一切事物都是对象(都可以使用反射)
# 反射方法的四个方法
# 1.获取对象的属性值,如果属性不存在,可提供默认值。
# getattr(object, name,[default])
# 2.判断对象是否具有指定属性
# hasattr(object, name)
# 3.设置对象的属性值
# setattr(object, name, value)
# 4.删除对象的属性
# delattr(object, name)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def run(self):
print("人能跑 ...")
@staticmethod
def read():
...
person = Person('serein', 18)
# 1.getattr(object, name[, default])
# 获取对象的属性值,如果属性不存在,可提供默认值。
# 获取到对象中的值,如果存在则返回当前值
print(getattr(person,'name'))
# 获取对象中的值,如果不存在则报错
print(getattr(person,'sex'))
# 获取对象中的值,如果不存在则报错,但是可以指定不存在的时候返回的默认值
print(getattr(person,'sex',None))
# **** 如果获取的是对象中的函数,那会直接返回函数的内存地址,可以直接调用该函数
res = getattr(person,'run')
print(res())
# 2.hasattr(object, name)
# 判断对象是否具有指定属性
# 获取到对象中的指定属性,如果存在,就返回 True
print(hasattr(person, 'name')) # True
# 获取到对象中的指定属性,如果不存在,就返回 False
print(hasattr(person, 'sex')) # False
# 3.setattr(object, name, value)
# 设置对象的属性值
# 先用 hasattr 判断当前对象是否具有该属性
# 如果不存在我则新增 setattr
# 如果存在直接获取 getattr
def func(obj=None, name=None, value=None):
if hasattr(obj, name):
return getattr(obj, name)
else:
setattr(obj, name, value)
print(f'设置完成')
return func(obj=obj, name=name, value=value)
# 获取到一个已经存在的值
print(func(obj=person, name='name')) # serein
# 获取一个不存在的值
print(func(obj=person, name='gender', value='male')) # male
# 4.delattr(object, name)
# 删除对象的属性
# 只能删除对象中的数据属性,无法删除函数属性
print(hasattr(person, 'run')) # True
delattr(person, 'run')
print(hasattr(person, 'run')) # 删除后报错
# 类也是对象
# 只有类经过初始化以后。,才会将 init 中属性加载到对象中
# 类没有初始化的时候,是不会存在init里面的属性的
print(Person.name)
print(hasattr(Person,'name'))
Person.run(person)
print(hasattr(Person,'run'))
Person.read()
print(hasattr(Person,'read'))
# 反射当前模块中的成员
import sys
def d1():
...
print(sys.modules[__name__])
print(hasattr(sys.modules[__name__],'sys'))
print(hasattr(sys.modules[__name__],'d1'))
标签:__,封装,name,继承,self,多态,print,class,def
From: https://www.cnblogs.com/Formerly/p/17958782