面向对象之封装、多态、反射
面向对象之封装
封装:将数据和功能‘封装’起来
隐藏:将数据和功能隐藏起来不让用户直接调用,并开发一些接口间接调用,而且可以在接口内添加一些额外的操作
伪装:将类里面的方法伪装成类里面的数据
隐藏
class A:
a = 1
_b = 2
__c = 3 # 类在定义阶段时,类里面的数据变量名前面有两个下划线,那么该名字会被隐藏起来,无法直接访问
# 在Python中没有真正意义上的隐藏,所谓的隐藏只是披了一层皮而已,_类名__名字
print(A.a) # 1
obj = A()
print(obj.a) # 1
print(obj._b) # 2
print(obj.__c) # 报错
print(A._b) # 2
print(A.__c) # 报错
print(obj._A__c) # 3
如果想让对象也拥有隐藏属性的话同样的也是需要在类定义阶段就弄好
obj.__name = 'zyg'
print(obj.__name) # zyg
# 如果直接添加和正常的数据一样
class Person:
def __init__(self, name, age, hobby):
self.__name = name # 对象也可以拥有隐藏的属性
self.__age = age
self.__hobby = hobby
# 类体代码中可以直接使用隐藏的属性
def get_info(self):
print(f"""
姓名:{self.__name}
年龄:{self.__age}
爱好:{self.__hobby}
""")
# 对隐藏的属性开放修改的接口
def set_name(self, new_name):
if len(new_name) == 0:
raise ValueError('你好歹写点东西')
if new_name.isdigit():
raise ValueError('名字不能是数字')
self.__name = new_name
obj = Person('zyg', 18, 'None')
obj.get_info()
# 姓名:zyg
# 年龄:18
# 爱好:None
obj.set_name('json')
obj.get_info()
# 姓名:json
# 年龄:18
# 爱好:None
伪装
我们在定义一个类以后,如果想要调用类里面的方法是需要加括号的
class Person:
def __init__(self, name, age, hobby):
self.__name = name
self.__age = age
self.__hobby = hobby
def get_info(self):
print(f"""
姓名:{self.__name}
年龄:{self.__age}
爱好:{self.__hobby}
""")
obj = Person('zyg', 18, 'read')
obj.get_info()
# 伪装就是把括号去掉直接变成 obj.get_info 就像调用数据
class Foo:
def __init__(self, val):
self.__NAME = val # 将属性隐藏起来
@property
def name(self):
return self.__NAME
@name.setter # 修改
def name(self, value):
# 在修改数据之前可以做一系列数据验证
self.__NAME = value # 通过类型检查后,将值value存放到真实的位置self.__NAME
@name.deleter # 删除
def name(self):
raise PermissionError('Can not delete')
obj = Foo('zyg')
print(obj.name) # zyg
obj.name = 'lxj'
print(obj.name) # lxj
面向对象之多态
面向对象中多态的意思是一种事物可以有多种形态,但是针对相同的功能应该定义相同的方法,这样无论我们拿到哪种状态的事物都可以通过相同的方法调用功能
eg:
在Linux系统中文件、内存、硬盘都是可以读取数据和保存数据的,那么在对他们定义时可以定义相同的读取和写入方法
class File:
def read(self): pass
def write(self): pass
class Memory:
def read(self): pass
def write(self): pass
class Disk:
def read(self): pass
def write(self): pass
# python永远提倡自由简介大方 不约束程序员行为 但是多态提供了约束的方法
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准,都必须定义一个talk方法,不管父类有没有
def talk(self):
pass
cat = Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
面向对象之反射
利用字符串操作对象的数据与方法,主要用于与用户交互
操作方法
1.hasattr() 判断对象是否含有某个字符串对应的属性名或方法名
2.getattr() 根据字符串获取对象对应的属性或方法
3.setattr() 根据字符串设置对象或者修改数据
4.delattr() 根据字符串删除对象中的名字
正常情况下我们修改数据是使用变量名来修改对应的数据,但是用户输入使用的是 input 输入的是字符串类型的数据,字符串'name'和变量名 name的差别还是很大的,这种情况下我们就可以利用反射来操作
class C1:
school_name = '小姐姐学院'
def choice_course(self):
print('大宝贝们正在选课')
obj = C1()
while True:
target_name = input('请输入您想要操作的名字>>>:') # 字符串类型的数据
if hasattr(obj, target_name): # 判断类中有没有输入的名字
print('恭喜您 系统中有该名字')
# 获取该名字对应的数据(值 函数)
data_or_func = getattr(obj, target_name) # 获取类中名字对应的值或方法
if callable(data_or_func): # 判断能不能加括号调用
print('您本次使用的是系统中的某个方法')
data_or_func()
else:
print('您本次使用的是系统中的某个数据')
print(data_or_func)
else:
print('很抱歉 系统中没有该名字')