派生方法的实战演练
import datetime
import json
d = {
't1': datetime.datetime.today(),
't2': datetime.date.today()
}
res = json.dumps(d)
print(res)
-
输出结果:
'''
以上代码执行时会报错,原因是因为json模块在python中是有限制的。
对于时间类型不可以序列化。
* 解决方式1: 手动在数据前将类型转成符合要求的数据类型
't1': str(datetime.datetime.today()),
't2': str(datetime.date.today())
* 解决方式2: 利用派生方法
报错发生的流程:
class JSONEncoder:
pass
dumps(obj,cls=None):
if cls == None:
cls = JSONEncoder
return cls(...) # JSONEncoder()
查看源码:shift+鼠标左键
我们先查看有限制的json模块的dumps方法源码
dumps有许多形参。
源码如下图:
'''
我们看到当不给cls形参传值时,会默认给cls赋值一个JSONencoder
后面return cls().encode(obj) 就是实例化JSONencoder的对象,所以查看JSONencoder
JSONencoder源码如下图:
查看源码之后发现我们报错的和default方法主动抛出的报错信息一致。
由此得出。序列化报错是default方法触发的
raise TypeError(f'Object of type {o.__class__.__name__} '
f'is not JSON serializable')
所以我们需要对源码的方法进行修改(派生方法)
class MyJsonEncode(json.JSONEncoder): # 继承了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)
json.dumps(d, cls=MyJsonEncode)
-
json模块可以序列化的类型只有以下类型:
| python | JSON | | --- | --- | | dict | object | | list | array | | tuple | array | | str | string | | int | number | | float | number | | True | true | | False | false | | None | null |
面向对象三大特性之封装
-
封装的目的是为了把数据或者功能隐藏起来(点不出来),给隐藏的数据开设特定接口,用户通过接口才能使用,我们在接口可以添加一些额外的操作
- 在类定义的阶段使用__开头的名字,都是隐藏的属性,后续类和对象均无法直接获取
- 隐藏的属性其实就是做了处理
将 __变量名 处理为了 _类名__变量名 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('', '大了') # 我们编写python很多时候都是大家墨守成规的东西 不需要真正的限制
property伪装属性
- 将方法伪装为数据,可以点出来 property
obj.name
obj.name()
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')
del obj.name
面向对象三大特性之多态
- 一个事物的多种状态称为多态
- 相同的功能应该有相同的名字,这样无论拿到哪个具体的名字,直接调用相同的功能即可
- 多态在以前数据类型内置方法就接触过(len方法)
- python也提供了一种强制性的操作
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
# 该装饰器限制子类必须定义有一个名为talk的方法
@abc.abstractmethod
# 抽象方法中无需实现具体的功能
def talk(self):
pass
# 但凡继承Animal的子类都必须遵循Animal规定的标准
class Person(Animal):
def talk(self):
pass
def run(self):
pass
obj = Person()
- 鸭子类型
- 只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
- 对于linux来说一切皆文件
- Txt,Disk,Memory类有两个与文件类型同名的方法,即read和write
- 对于python来说一切皆对象
- 只要有数据,有功能就是对象
面向对象之反射
- 反射就是通过字符串操作对象的数据或方法(主要有四个)
hasattr(): 判断对象是否含有某个字符对应的属性
getattr(): 获取对象字符串对应的属性
setattr(): 根据字符串给对象设置属性
delattr(): 根据字符串给对象删除属性
class Student:
school = '清华大学'
def choice_course(self):
print('选课')
stu = Student()
# 需求:判断用户提供的名字在不在对象可以使用的范围内
# 方式1:利用异常处理(过于繁琐)
try:
if stu.school:
print(f"True{stu.school}")
except Exception:
print("没有属性")
"""
变量名school与字符串school区别很大,两者虽然只差了引号,但是本质是完全不一样的
stu.school
stu.'school'
"""
# print(hasattr(stu, target_name)) # # 给一个字符串去get里面,找一下打印布尔值
# print(getattr(stu, target_name)) # 给一个字符串去get里面,找一下打印出来
while True:
target_name = input('请输入您想要核查的名字>>>:').strip()
'''上面的异常更加不好实现 需要用反射'''
if hasattr(stu, target_name):
res = getattr(stu, target_name)
if callable(res):
print('拿到的名字是一个函数', res())
else:
print('拿到的名字是一个数据', res)
else:
print('不好意思 您想要查找的名字 对象没有')
print(stu.__dict__)
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)
setattr(stu,'gender','male')
setattr(stu,'hobby','read')
print(stu.__dict__)
del stu.name
print(stu.__dict__)
delattr(stu, 'age')
print(stu.__dict__)
"""
遇到关键字....对象....字符串就要使用反射
"""
反射实战演练
class FtpServer:
def serve_forever(self):
while True:
inp = input('input your cmd>>: ').strip()
cmd, file = inp.split()
# 根据用户输入的cmd,判断对象self有无对应的方法属性
if hasattr(self, cmd):
# 根据字符串cmd,获取对象self对应的方法属性
func = getattr(self, cmd)
func(file)
def get(self, file):
print('Downloading %s...' % file)
def put(self, file):
print('Uploading %s...' % file)
obj = FtpServer()
obj.serve_forever()