面向对象三特性
面向对象3
- 继承下的派生实际应用
- 面向对象的三大特性之封装
- 面向对象三大特性之多态
- 反射
- 重要的双下方法
具体内容
继承下的派生实际应用
import datetime # 以时间模块为展示
import json# 需要调用json模块中的相关模块内容
class MyJsonEncoder(json.JSONEncoder):# 将模块作为形参传入类里面
def default(self, o):# 定义序列化的对象 后面要通过json模块进行序列化
# 形参o就是即将要被序列化的数据对象
# print('重写了', o)
'''将o处理成json能够序列化的类型即可'''
if isinstance(o,datetime.datetime):
return o.strftime('%Y-%m-%d %X')
elif isinstance(o, datetime.date):
return o.strftime('%Y-%m-%d')
return super().default(o) # 调用父类的default(让父类的default方法继续执行 防止有其他额外操作)
d1 = {'t1': datetime.datetime.today(), 't2': datetime.date.today()} # 这里赋值要结和对象里面的json序列化格式相对应 否则 TypeError在向你招手
res = json.dumps(d1, cls=MyJsonEncoder)
print(res)
'''
TypeError: Object of type 'datetime' is not JSON serializable
json不能序列化python所有的数据类型 只能是一些基本数据类型
json.JSONEncoder
1.手动将不能序列化的类型先转字符串
{'t1': str(datetime.datetime.today()), 't2': str(datetime.date.today())}
2.研究json源码并重写序列化方法
研究源码发现报错的方法叫default
raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)
我们可以写一个类继承JSONEncoder然后重写default方法
'''
面对对象三大特性:封装
封装含义:
将类中的你想要不被其他人调用的数据或需要隐藏的数据进行特殊处理,防止调用和校验
隐藏的目的是为了提供专门的通道去访问 在通道内可以添加额外的功能
这句话的意思是 隐藏的数据并不是就一定不可以调用 而是需要通过特殊的处理(设置专门的通道去访问)调用 和类的的派生类似也可以添加新的功能(以购物车为例 在用户注册后隐藏注册的用户和密码选项 登录功能是调用 在添加一个用户密码对比功能接口)
# 代码实操
# class Student(object):
# school = '清华大学'
# __label = '逆来顺受' # 由于python崇尚自由 所以并没有真正的隐藏 而是自动转换成了特定的语法
# def __init__(self, name, age):
# self.name = name
# self.age = age
# def choose_course(self):
# print('%s正在选课'%self.name)
# stu1 = Student('jason', 18)
# print(stu1.school) # 清华大学
# print(stu1.name) # jason
# print(stu1.age) # 18
# print(stu1.__label)
# print(Student.__dict__) # '_Student__label': '逆来顺受'
# print(Student._Student__label)
"""
如何封装名字
在变量名的前面加上两个下划线__ : (__label)如果在直接调用的话是无法调用
# 封装的功能只在类定义阶段才能生效!!!
在类中封装其实也不是绝对的 仅仅是做了语法上的变形而已
__变量名 >>> _类名__变量名
我们虽然指定了封装的内部变形语法 但是也不能直接去访问
看到了就表示这个属性需要通过特定的通道(接口)去访问
"""
class Student:
__school = '北京大学'
def __init__(self,name,age,gender):
self.__name = name
self.__age = age
self.__gender = gender
def check(self):
print("""
姓名%s,
年龄%s,
爱好%s
"""%(self.__name,self.__age,self.__gender))
def change(self,name,age):
if len(name) == 0:
print('姓名不能为空字符')
return
if not isinstance(age,int):
print('年龄必须为数字,就是你活到180岁也给我填数字')
return
self.__name = name
self.__age = age
res = Student('yufan',26,'game')
res.check()
res.change('james',28)
res.check()
t1 = Student('jack',18,'game')
print(Student.__dict__)
# t1.__name
print(t1.__dict__)
res.change('',25)
res.change('jack','我就不填数字')
"""
将数据隐藏起来就限制了类外部对数据的直接操作,然后类内应该提供相应的接口来允许类外部间接地操作数据,
接口之上可以附加额外的逻辑来对数据的操作进行严格地控制
目的的是为了隔离复杂度,例如ATM程序的取款功能,该功能有很多其他功能组成
比如插卡、身份认证、输入金额、打印小票、取钱等,而对使用者来说,只需要开发取款这个功能接口即可,其余功能我们都可以隐藏起来
"""
property方法
property就是将方法伪装成数据
用BMI做例子
"""
可扩扩展:
体质指数(BMI)=体重(kg)÷身高^2(m)
"""
有时候很多数据需要经过计算才可以获得
但是这些数据给我们的感觉应该属于数据而不是功能
BMI指数>>>:应该属于人的数据而不是人的功能
class Person(object):
def __init__(self, name, height, weight):
self.__name = name
self.height = height
self.weight = weight
@property # 这种方法我的理解是替代了函数的参数
# def BMI(self,name,height,weight) 这就是将函数的参数直接隐藏
def BMI(self):
# print('%s的BMI指数是:%s' % (self.name, self.weight / (self.height ** 2)))
return '%s的BMI指数是:%s' % (self.__name, self.weight / (self.height ** 2))
res = Person('yufan',1.78,94)
# print(res.BMI)
print(res.BMI)
面向对象三大特性:多态
多态的常规理解:
一种事物的多种形态
比如说:水(液态) 冰(固态) 水蒸气(气态)
哺乳类 海洋(海豚) 猫科(熊猫) 人类
多态性
class Animal(object):
def speak(self):
pass
class Cat(Animal):
def speak(self):
print('喵喵喵')
class Dog(Animal):
def speak(self):
print('汪汪汪')
class Pig(Animal):
def speak(self):
print('哼哼哼')
c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.speak()
d1.speak()
p1.speak()
这种方法是多个类的相互调用 我个人理解为类的派生里面几个不同功能子类但却有着相同父类里面的共同功能 所有动物都要叫的叫的动作可以调用父类Aniaml的speak来实现
property的功能及其类似我们前面的功能
s1 = 'hello world'
l1 = [1, 2, 3, 4]
d1 = {'name': 'jason', 'pwd': 123}
print(len(s1))
print(len(l1))
print(len(d1))
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.speak()
面向对象的多态性也需要python程序员自己去遵守
虽然python推崇的是自由 但是也提供了强制性的措施来实现多态性
import abc
指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Person(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
p1=Person() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
'''
多态性的功能衍生鸭子理论 什么叫duck理论
功能类似 duck 数据类似 duck 运用类似 duck
就是可以相同调用
'''
# 鸭子类型的实战案例
"""
在linux系统中有一句话>>>:一切皆文件!!!
内存可以存取数据
硬盘可以存取数据
...
那么多有人都是文件
"""
class Memory(object):
def read(self):
pass
def write(self):
pass
class Disk(object):
def read(self):
pass
def write(self):
pass
# 得到内存或者硬盘对象之后 只要想读取数据就调用read 想写入数据就调用write 不需要考虑具体的对象是谁
面对对象:反射
反射:
专业解释:指程序可以访问、检测和修改本身状态或者行为的一种能力
通过小范围的代码来对对象的功能和数据修改的操作
反射的四个方法:
hasattr():判断对象是否含有字符串对应的数据或者功能
getattr():根据字符串获取对应的变量名或者函数名
setattr():根据字符串给对象设置键值对(名称空间中的名字)
delattr():根据字符串删除对象对应的键值对(名称空间中的名字)
反射使劲应用
class Student(object):
school = '清华大学'
def get(self):
pass
编写一个小程序 判断Student名称空间中是否含有用户指定的名字 如果有则取出展示
print(Student.__dict__) 这个将取出Student里面的所有公共数据{'__module__': '__main__', 'school': '清华大学', 'get': <function Student.get at 0x0000022A702EDBF8>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
'''字符串的school 跟 变量名school差距大不大? 本质区别'''
本质区别一个是字符串的判断模式str 另一个则是变量 没有赋值的话会报错
guess_name = input('请输入你想要查找的名字>>>:').strip()
在指定取值的情况下不使用反射不太容易实现
如果使用其他方法进行判断比如raise的主动报错和if 判断则无法对字符串进行准确的判断和功能的下一步执行
print(hasattr(Student, 'school')) # True
school 在类里
print(hasattr(Student, 'get')) # True
print(hasattr(Student, 'post')) # False
# print(getattr(Student, 'school')) # 清华大学
# print(getattr(Student, 'get')) # <function Student.get at 0x10527a8c8>
# guess_name = input('请输入你想要查找的名字>>>:').strip()
# if hasattr(Student, guess_name):
# target_name = getattr(Student, guess_name)
# if callable(target_name):
# print('类中有一个功能名字是%s'%guess_name,target_name)
# else:
# print('类中有一个数据名字是%s'%guess_name,target_name)
# else:
# print('类中没有该名字')
setattr(Student,'level','贵族学校')
print(Student.__dict__)
#
def index():
if naem == 'dlta':
print('get out')
obj = Student()
setattr(obj, '血量', 10000)
setattr(obj, '功能', index)
print(obj.__dict__)
delattr(obj, '功能')
print(obj.__dict__)
什么时候使用反射 可以记固定的口诀
以后只要在业务中看到关键字
对象 和 字符串(用户输入、自定义、指定) 那么肯定用反射
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人