python学习Day33
Day 33
今日内容概要
- 派生方法的实战演练(重要)
- 面向对象三大特性—封装
- property伪装属性
- 面向对象三大特性—多态
- 面向对象三大特征—反射(重要)
- 反射实战演练(重要)
今日内容详细
1.派生方法的实战演练(重要)
import datetime
import json
d = {'t1': datetime.datetime.today(),'t2': datetime.date.today()}
res = json.dumps(d)
print(res)
上述代码会报错 无法正常序列化
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable
#不具备json序列化的能力
json序列化python数据类型是有限制的 不是所有的类型都可以
输入:json.JSONEncoder, ctrl点JSONEncoder可查看源码看到可序列化的数据类型
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
ps:即将要被序列化的数据 里里外外都必须是上述类型才可以否则就会出错
解决方式1:手动将不符合的数据类型转成符合要求的
缺点: 凸显不出水平
import datetime
import json
d={'t1':str(datetime.datetime.today()),
't2':str(datetime.date.today())}
res=json.dumps(d)
print(res)#{"t1":"2022-07-28 18:09:39.205855","t2":"2022-07-28"}
解决方式2:利用派生方法
首先查看json.dumps的源码:
class JSONEncoder:
pass
dumps(obj,cls=None...):
if cls == None:
cls = JSONEncoder
return cls(...) # JSONEncoder()
查看JSONEncoder源码发现序列化报错是有default方法触发的
raise TypeError(f'Object of type {o.__class__.__name__} '
f'is not JSON serializable')
我们如果想要避免报错 那么需要对default方法做修改(派生操作)
——————————————————————————————————————————————————————
解决方式2:
import datetime
import json
d={'t1':datetime.datetime.today(),
't2':datetime.date.today()}
class MyJsonEncode(json.JSONEncoder):
def default(self, o):
'''o就是json即将要序列化的数据'''
if isinstance(o, datetime.datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(o, datetime.date):
return o.strftime('%Y-%m-%d')
# 如果是可以序列化的类型 那么不做任何处理 直接让它序列化即可
return super().default(o)
res = json.dumps(d, cls=MyJsonEncode)
print(res)#{"t1":"2022-07-28 18:41:19","t2":"2022-07-28"}
2.面向对象三大特性—封装
封装其实就是将数据或者功能隐藏起来(包起来 装起来)
隐藏的目的不是让用户无法使用 而是给这些隐藏的数据开设特定的接口,让用户使用接口才可以去使用,我们在接口中添加额外操作
1.在类'定义阶段'使用双下划线开头的名字都是隐藏的属性(__school、__name等)
后续类和对象都无法直接获取
2.在python中不会真正的限制任何代码
隐藏的属性如果真的需要访问,则需要做变形处理
用print(类名.__dict__)可查看类的名称空间则会发现:
__变量名 实际变成了:_类名__变量名
ps:既然隐藏了就不该使用变形之后的名字去访问,因为这样就失去了隐藏的意义
只允许写一个方法,调用该方法去访问:
class Student(object):
__school = '清华大学'
def __init__(self, name, age):
self.__name = name
self.__age = age
# 专门开设一个访问学生数据的通道(接口)
def check_info(self):
print("""
学生姓名:%s
学生年龄:%s
""" % (self.__name, self.__age))
# 专门开设一个修改学生数据的通道(接口)
def set_info(self,name,age):
if len(name) == 0:
print('用户名不能为空')
return
if not isinstance(age,int):
print('年龄必须是数字')
return
self.__name = name
self.__age = age
stu1 = Student('jason',18)
#修改学生数据
stu1.set_info('jjj',19)
#查看学生信息
stu1.check_info()
"""
我们编写python很多时候都是大家墨守成规的东西 不需要真正的限制 君子协定
class A:
_school = '清华大学'
def _choice_course(self):
pass
"""
3.property伪装属性
可以理解为:将方法伪装成数据
obj.name #数据需要.名字
obj.func() #方法需要加括号
伪装之后可以将func方法伪装成数据 obj.func
扩展了解:
eg:体质指数(BMI)=体重(kg)÷身高(m)^2
# class Person:
# def __init__(self,name,weight,height):
# self.name=name
# self.weight=weight
# self.height=height
# def BMI(self):
# return self.weight/(self.height**2)
# p1=Person('jason',60,1.65)
# #调用参数
# res=p1.BMI()
# print(res)#22.03
'''
由于BMI应该属于人的数据,不该是功能
但是又需要计算得出所以就需要伪装(@property)
'''
class Person:
def __init__(self,name,weight,height):
self.name=name
self.weight=weight
self.height=height
@property
def BMI(self):
return self.weight/(self.height**2)
p1=Person('jason',60,1.65)
#调用参数
res=p1.BMI#调用方式不再是加括号,而是.名字
print(res)#22.03
更彻底的伪装:(了解即可)
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 must be str' % value)
self.__NAME = value # 通过类型检查后,将值value存放到真实的位置self.__NAME
#删除名字
@name.deleter
def name(self):
raise PermissionError('Can not delete')
obj = Foo('jason')
print(obj.name)#jason
obj.name = 666
print(obj.name)#只能修改为字符串类型
del obj.name#删除名字会被拒绝
4.面向对象三大特性—多态
多态:一种事物的多种形态
水 :液态 气体 固态
动物:人 狗 猫 猪
# class Animal(object):
# def spark(self):
# pass
# class Cat(Animal):
# def miao(self):
# print('喵喵喵')
# class Dog(Animal):
# def wang(self):
# print('汪汪汪')
# c1 = Cat()
# d1 = Dog()
# c1.miao()#喵喵喵
# d1.spark()#汪汪汪
"""
一种事物有多种状态 但是相同的功能应该有相同的名字
以后无论拿到哪个具体的动物都不需要管它是谁,直接调用相同功能即可
"""
class Animal(object):
def spark(self):
pass
class Cat(Animal):
def spark(self):
print('喵喵喵')
class Dog(Animal):
def spark(self):
print('汪汪汪')
c1 = Cat()
d1 = Dog()
c1.spark()#喵喵喵
d1.spark()#汪汪汪
'''
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
# def run(self):
# pass
# obj = Person()
鸭子类型:只要你长得像鸭子,走路像鸭子,说话像鸭子,那么你就是鸭子
#定义两个类,只要一样类型的,哪怕不继承,也应该封装一个一样的方法
class Teacher:
def run(self):pass
def eat(self):pass
class Student:
def run(self):pass
def eat(self):pass
扩展知识:
linux系统:一切皆文件
只要你能读取数据 写数据 那么你就是文件(内存、硬盘..)
class Txt: #Txt类有两个与文件类型同名的方法,即read和write
def read(self):
pass
def write(self):
pass
class Disk: #Disk类也有两个与文件类型同名的方法:read和write
def read(self):
pass
def write(self):
pass
python中:一切皆对象
只要你有数据 有功能 那么你就是对象(文件名、模块名..其实就是叫文件对象、模块对象..)
5.面向对象三大特征—反射(重要)
反射:通过字符来操作对象的数据或方法
反射主要就四个方法:
hasattr():判断对象是否含有某个字符串对应的属性
getattr():获取对象字符串对应的属性
setattr():根据字符串给对象设置属性
delattr():根据字符串给对象删除属性
'''
什么时候使用反射?
只要在需求中看到关键字:..对象..字符串 都用反射
'''
1.hasattr():判断对象是否含有某个字符串对应的属性
2.getattr():获取对象字符串对应的属性
——————————————————————————————————————————
需求:获取用户输入的名字,然后判断该名字对象有没有,并获取对应的属性
class Student:
school='清华大学'
def choice_course(self):
print('选课')
stu=Student()
while True:
target_name=input('输入您要查看的名字:').strip()
#判断是否有该字符串对应的属性
if hasattr(stu,target_name):
#如果存在则把获取到对应属性的值赋值给res
res=getattr(stu,target_name)
#判断括号里的东西能不能加括号调用
if callable(res):
print('拿到的名字是一个函数',res())
else:
print('拿到的名字是一个数据',res)
else:
print('没该名字对象')
3.setattr():根据字符串给对象设置属性
4.delattr():根据字符串给对象删除属性
——————————————————————————————————————
class Student:
school='清华大学'
def choice_course(self):
print('选课')
stu=Student()
print(stu.__dict__)#{}
#给对象设置属性
stu.name = 'jason'
print(stu.__dict__)#{'name':'jason'}
#用字符串给对象设置属性:(获取用户输入的都是字符串!!)
setattr(stu,'gender','male')
print(stu.__dict__)#{'name':'jason','gender':'male'}
#给对象删除属性
del stu.name
print(stu.__dict__)#{'gender':'male'}
#用字符串给对象删除属性:(获取用户输入的都是字符串!!)
delattr(stu, 'age')
print(stu.__dict__)#{}
6.反射实战演练(重要)
class FtpServer:
def serve_forever(self):
while True:
inp = input('input your cmd>>: ').strip()
cmd, file = inp.split()
if hasattr(self, cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
func = getattr(self, cmd) # 根据字符串cmd,获取对象self对应的方法属性
func(file)
def get(self, file):
print('Downloading %s...' % file)
def put(self, file):
print('Uploading %s...' % file)
obj = FtpServer()
obj.serve_forever()
#类似智能语音,用户说一句话,智能语音内部的模块将这句话转换成字符串然后匹配对应的功能方法去执行
作业
1.整理今日内容及博客
2.备战ATM购物车
3.准备迎接选课系统
选课系统(暂时可不看 学有余力者可提前思考)
角色:学校、学员、课程、讲师
要求:1. 创建北京、上海 2 所学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
3. 课程包含,周期,价格,通过学校创建课程
4. 通过学校创建班级, 班级关联课程、讲师5. 创建学员时,选择学校,关联班级
5. 创建讲师角色=时要关联学校,
6. 提供三个角色接口
6.1 学员视图, 可以注册, 交学费, 选择班级,
6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里