python33-派生-property伪装,多态,反射,实战。
今日学习
派生方法的实战演练重要
property伪装
面向对象三大特性之多态
面向对象之反射
反射实战案例
派生方法
-
需求.将字典里的时间进行序列化
-----------------------例子-------------------------
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序列化python数据类型是有限制的 不是所有的类型都可以
-
错误原因上述例子报错原因是python序列化不是全部支持。
即将要被序列化的数据 里里外外都必须是如下类型才可以
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
-
需求解决方式
-
解决方式1:手动将不符合的数据类型要求的数据转换成符合的
import datetime
import json
# 解决方式1:手动将不符合数据类型要求的数据转成符合要求的
d = {
't1': str(datetime.datetime.today()),
't2': str(datetime.date.today())
}
res = json.dumps(d)
print(res)
----------执行结果-------
{"t1": "2022-07-28 15:37:47.196146", "t2": "2022-07-28"}
-
为甚解决方式2用派生
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)
json.dumps(d, cls=MyJsonEncode)
---------执行结果------------
{"t1": "2022-07-28 15:44:11", "t2": "2022-07-28"}
面向对象三大特性-封装
-
封装定义
封装其实就是将数据或者功能隐藏起来(包起来 装起来)
隐藏的目的不是让用户无法使用 而是给这些隐藏的数据开设特定的接口 让用户使用接口才可以去使用 我们在接口中添加一些额外的操作
-
如何封装
1.在类定义阶段使用双下划线开头的名字 都是隐藏的属性
后续类和对象都无法直接获取
------------------------------------------------------------
2.在python中不会真正的限制任何代码
隐藏的属性如果真的需要访问 也可以 只不过需要做变形处理
__变量名 _类名__变量名
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很多时候都是大家墨守成规的东西 不需要真正的限制
class A:
_school = '清华大学'
def _choice_course(self):
pass
property 伪装属性
可以简单的理解为 将方法伪装成数据
obj.name #数据只需要点名字
obj.func() #方法至少还要加括号
伪装之后可以将func方法伪装成数据 obj.func
扩展了解
体质指数(BMI)=体重(kg)÷身高^2(m)
-------------------------简单 例子----------------------------------------------
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)
"""BMI虽然需要计算获得 但是更像是人的数据"""
p1 = Person('jason', 78, 1.83)
print(p1.BMI) #不伪装的情况下 #res = p1.BMI()
print(p1.name) # print(res)
--------执行结果---------
23.291229956104985
jason
----------------------------伪装例2- 了解 --------------------------------------------------
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)
# obj.name = 666
# print(obj.name)
del obj.name
面向对象之多态 了解
-
定义
多态:一种事物的多种形态
水:液态 气态 固态
动物:人 狗 猫 猪
-----------------------例子-----------------------------------------------------------
class Animal(object):
def spark(self):
pass
class Cat(Animal):
def spark(self):
print('喵喵喵')
class Dog(Animal):
def spark(self):
print('汪汪汪')
class Pig(Animal):
def spark(self):
print('哼哼哼')
===========================================
c1.spark()
d1.spark()
p1.spark()
"""
其实上述多态的概念 我们很早之前就已经解除过
-----------------------例子2=====================================================
"""
# l1 = [11, 22, 33, 44]
# d1 = {'name': 'jason', 'pwd': 123, 'hobby': 'raed'}
# t1 = (11, 22, 33, 44)
# print(len(l1))
# print(len(d1))
# print(len(t1))
"""
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
面向对象之反射
通过字符串来操作对象的数据或方法
反射主要方法
hasattr():判断对象是否含有某个字符串对应的属性 #一般hassattr 和getattr结合使用
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' #对象不能以点的方式进行调用。会报错。
两者虽然只差了引号 但是本质是完全不一样的,在上述例子中
-
方式二:获取用户输入的名字 然后判断该名字对象有没有
# 方式2:
while True:
target_name = input('请输入您想要核查的名字>>>:').strip()
'''上面的异常更加不好实现 需要用反射'''
# print(hasattr(stu, target_name))
# print(getattr(stu, target_name))
if hasattr(stu, target_name):
# print(getattr(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()
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()