python-面向对象封装、多态、反射
1.派生方法实战演练
"""
背景:如何将字典d序列化成json格式?
"""
import json
import datetime
d = {
't1': datetime.date.today(),
't2': datetime.datetime.today(),
't3': 'jason'
}
res = json.dump(d)
print(res) # 会报错
"""
序列化报错
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type date is not JSON serializable
"""
"""
能够被序列化的数据是有限的>>>:里里外外都必须是下列左边的类型
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
"""
转换方式1:手动转换,把字典的V转化成字符串
import json
import datetime
d = {
't1': str(datetime.date.today()),
't2': str(datetime.datetime.today()),
't3': 'jason'
}
res = json.dumps(d)
print(res) # {"t1": "2022-11-07", "t2": "2022-11-07 14:56:09.858723", "t3": "jason"}
转换方式2:派生方法
class MyJsonEncoder(json.JSONEncoder):
def default(self, o):
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)
res = json.dumps(d, cls=MyJsonEncoder)
print(res) # {"t1": "2022-11-07", "t2": "2022-11-07 15:04:11.130998", "t3": "jason"}
2.面向对象三大特征之封装
封装:就是将数据和功能'封装'起来
隐藏:在类的定义阶段名字前面使用两个下划线表示隐藏。就是将数据和功能隐藏起来不让用户直接调用,而是开发一些接口间接调用,从而可以在接口内添加额外的操作
伪装:将类里面的方法伪装成数据,目的是调用方法可以像调用数据一样不用加括号
"""
隐藏类名称空间中名字的语法结构是在类的定义阶段名字前面加上__,那么通过类或者对象点名字的方式都无法直接找到这个名字,如果想要拿到语法结构为:类或对象._类名__名字
"""
class Myclass:
school_name = '清华大学'
_ = '嘿嘿嘿'
_name = 'max'
"""
类在定义阶段,名字前面有两个下划线,那么名字会被隐藏起来,无法直接访问
"""
__age = 25
"""
在python中,没有真正的隐藏,仅仅是换了个名字
"""
def __choice_course(self):
print('选课系统')
print(Myclass.school_name) # 清华大学
obj = Myclass()
print(obj.school_name) # 清华大学
print(Myclass._) # 嘿嘿嘿
print(obj._) # 嘿嘿嘿
print(Myclass._name) # max
print(obj._name) # max
print(Myclass.__age) # 会报错,因为__被隐藏
print(Myclass._Myclass__age) # 25
print(obj._Myclass__age) # 25
Myclass.__gender = 'male'
print(Myclass.__gender) # male 手动添加的类的名字无法隐藏
obj = Myclass()
obj.__addr = 'shanghai'
print(obj.__addr) # shanghai 手动添加对象的名字无法隐藏
"""
除了类,对象也可以设置隐藏属性,但是只能在类体中设置,对象在类体中设置独有方式的方法就只有__init__方法。类体代码中可以直接用隐藏的名字
"""
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('max', 25, 'fitness')
obj.get_info()

obj.set_name('jack')
obj.get_info()

"""
如果用__dict__查看对象名称空间,发现字典中所有的键都变成了:_类名__名字,所以说我们也可以通过对象名.__类名_名字的方式来拿对象的名字,但是不建议
"""

"""
以后我们在编写面向对象代码类的定义是,也会看到很多单下划线开头的名字,表达的意思通常是不要直接访问,而是查找一下下面可能定义的接口
"""
3.伪装
"""
类中有的功能返回值是一个数字,而我们调用的时候仍需要像调用函数一样调用,此时可以通过@property来装饰功能,使被装饰的功能从调用方式上来说更像一个数据
"""
BMI指数:衡量一个人的体重与身高对健康影响的一个指标
BMI=体重(kg)÷身高^2(m)
class Person(object):
def __init__(self, name, height, weight):
self.name = name
self.height = height
self.weight = weight
def BMI(self):
return self.weight / (self.height ** 2)
p1 = Person('max', 1.82, 70)
print(p1.BMI()) # 21.132713440405748
有了@property之后功能BMI更像是一个数据,但是用该方法伪装不能加参数
class Person(object):
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)
p1 = Person('max', 1.82, 70)
print(p1.BMI) # 21.132713440405748
"""
当用property修饰之后的函数无法用对象名点名字的方式修改,此时用@函数名.setter修饰一个修改的函数,用户可以直接用对象名点的方式来修改
"""
class Foo:
def __init__(self, val):
self.__NAME = val
@property
def name(self):
return self.__NAME
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('%s必须是字符串' % value)
self.__NAME = value
@name.deleter
def name(self):
raise PermissionError('Can not delete')
f1 = Foo('jason')
print(f1.name) # jason
f1.name = 'max' # 触发name.setter装饰器对应的函数
print(f1.name) # max
del f1.name # PermissionError: 不能被删除
# 触发name.deleter对应的函数name(f),抛出异常PermissionError
"""
用property装饰后的功能类似一个数据,查的时候只需要对象名点功能名即可查到。同样改和删也是以对象点函数名的方式来执行,改是用对象名点函数名等于新的值来调用修改功能,和之前的调用方式不同
"""
4.三大特性之多态
1.多态:多态是指一种食物可以有多种形态但是针对相同的功能应该设定相同的方法,这样无论我们拿到的是哪个具体事务,都可以通过相同的方法调用功能(多态类型下父类的功能不再是为了省代码,而是提示每一个子类如果想要达成相同的目的必须要有相同的方法)
class Animal:
def spark(self):
pass
def Cat(Animal):
def spark(self)
print('喵喵喵')
def Dogs(Animal):
def spark(self)
print('汪汪汪')
def Pig(Animal):
def spark(self)
print('哼哼哼')
"""
面向对象中多态意思是,一种事物可以有多种形态但是针对相同的功能应该定义相同的方法,这样无论我们拿到的是哪个具体的事物,都可以通过相同的方法调用功能
"""
s1 = 'hello world'
l1 = [11, 22, 33, 44]
d = {'name': 'jason', 'pwd': 123}
print(s1.__len__())
print(l1.__len__())
print(d.__len__())
2.鸭子类型:只要你看上去像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子。以linux系统为例,文件能够读取数据也可以保存数据,内存能够读取数据也能保存数据吧,硬盘能够读取数据也能保存数据,所以在linux中有句话叫一切皆文件
eg:
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
5.面向对象之反射
反射:利用字符串操作对象和数据的方法,可以动态的向对象中添加属性和方法
1.hasattr():
判断对象是否含有某个字符串对应的属性名或方法名
用法:hasaattr(obj,str),判断输入的str字符串在对象obj中是否存在(属性或方法),存在返回True,否则返回False
2.getattr():
根据字符串获取对象对应的属性名(数据)或方法(函数体代码)
用法:getattr(obj,str),将按照输入的str字符串在对象obj中查找。如找到同名属性,则返回该属性;如找到同名方法,则返回方法的引用,想要调用此方法得使用 getattr(obj,str)()进行调用.如果未能找到同名的属性或者方法,则抛出异常:AttributeError。
3.setattr():
根据字符串给对象设置或者修改数据
用法:setattr(obj,name,value),name为属性名或者方法名,value为属性值或者方法的引用
(1) 动态添加属性。如上
(2)动态添加方法。首先定义一个方法。再使用setattr(对象名,想要定义的方法名,所定义方法的方法名)
4.delattr():
根据字符串删除对象里面的名字
用法:delattr(obj,str),将你输入的字符串str在对象obj中查找,如找到同名属性或者方法就进行删除
"""
判断某个名字对象是否存在
"""
class School:
school_name = '小姐姐学院'
def choice_course(self):
print('选课系统')
obj = School
try:
obj.school_name
except Exception:
print('没有这个名字')
else: # try的子代码为True时走else子代码
print('名字存在哦')
# 执行结果:名字存在哦
"""
请结合用户输入,查看用户输入的名字是否存在
"""
class School:
school_name = '小姐姐学院'
def choice_course(self):
print('选课系统')
obj = School
target_name = input('请输入您想查看的名字>>>:').strip()
try:
obj.target_name
except Exception:
print('没有这个名字')
else:
print('名字存在哦')
# 执行结果:没有这个名字
"""
因为获取用户输入得到的是字符串,而对象点的是变量名。反射的作用就是利用字符串操作对象的数据和方法
"""
print(hasattr(obj, 'school_name')) # True
print(getattr(obj, 'school_name')) # 小姐姐学院
print(getattr(obj, 'choice_course')) # <function School.choice_course at 0x0000020D15538598>
"""
hasattr和getattr找对象的数据和方法首先从对象名称空间中找,其次从产生对象的类名称空间中找,找不到再去父类名称空间中找
"""
class A:
name = 'jason'
class B(A):
pass
b = B()
print(getattr(b, 'name')) # jason
"""
判断用户输入的名字是否存在,如果存在则执行
"""
class C1:
school_name = '小姐姐学院'
def choice_course(self):
print('选课系统')
obj = C1()
while True:
target_name = input('请输入您想要操作的名字>>>:').strip()
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('系统当中未找到该名字')
"""
当用get(obj, str)调用类中的功能时,如果功能有参数(self除外),则需要在get(obj, str)后面再加一个括号,填写参数才能调用。用getattr调用功能时self餐谁也不用上传
"""
class School:
school_name = '美少女学院'
def func1(self, name, age):
print(f'{name}今年刚满{age}')
obj = School()
getattr(obj, 'func1') # 什么都不打印
getattr(obj, 'func1')('jason', 18) # jason今年刚满18
6.反射实战案例
1.什么时候考虑使用:只要需求中出现关键字:对象...字符串...,用反射可以将获取用户输入和面向对象结合起来
2.实战案例:
class WinCmd:
def tasklist(self):
print("""
1.学习编程
2.学习理论
""")
def ipconfig(self):
print("""
addr:127.0.0.1
""")
def get(self, target_file):
print('正在获取指定文件', target_file)
def put(self, target_file):
print('正在上传指定文件', target_file)
def service_run(self):
print('欢迎进入简易版cmd终端')
while True:
target_cmd = input('请输入您的指令>>>:').strip()
res = target_cmd.split(' ')
if len(res) == 1:
if hasattr(self, res[0]):
getattr(self, res[0])()
else:
print(f'{res[0]}不是内部或者内部或者外部命令')
elif len(res) == 2:
if hasattr(self, res[0]):
getattr(self, res[0])(res[1])
else:
print(f'{[res[0]]}不是内部或外部命令')
obj = WinCmd()
obj.service_run()
2.一切皆对象:文件也是对象,getattr/hasattr(obj, name)中的obj也可以换成文件名
需求一:利用反射保留某个py文件中所有的大写的变量名及对应的数据值
import settings
upper_dict = {}
for name in dir(settings):
if name.isupper():
upper_dict[name] = getattr(settings, name)
print(upper_dict) # {'AGE': 25, 'NAME': 'max'}
需求二:找到settings中为大写的名字对应的值:
import settings
while True:
target_name = input('请输入名字>>>:').strip()
if hasattr(settings, target_name):
print(getattr(settings, target_name))
else:
print('模块文件中暂无该名字')
setattr只会修改对象的属性,不会修改数据库中的数据:
def test2(request):
book_obj2 = Book.objects.filter(id=1).first()
'''拿到数据库中的数据'''
print(book_obj2.price) # 888.88
'''只会修改属性的值,不会修改数据库中的数据'''
setattr(book_obj2, 'price', 999)
'''拿到book_obj2的price属性,此时的price属性已经不是表中的price字段对应的值而是对象的price属性'''
print(book_obj2.price) # 999
return HttpResponse('更新成功')
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律