面向对象-3
面向对象-3
目录
- 派生方法的实战演练
- 面向对象三大特性——封装
- 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数据类型是有限制的 不是所有的类型都可以
可以通过ctry查看内置方法
+-------------------+---------------+
| Python | JSON |
+===================+===============+
| dict | object |
+-------------------+---------------+
| list, tuple | array |
+-------------------+---------------+
| str | string |
+-------------------+---------------+
| int, float | number |
+-------------------+---------------+
| True | true |
+-------------------+---------------+
| False | false |
+-------------------+---------------+
| None | null |
+-------------------+---------------+
ps:即将要被序列化的数据 里里外外都必须是上述类型才可以
报错的原因:
查看JSONEncoder源码
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方法做修改(派生)
解决此类问题,有两种解决方法
-
手动将不符合数据类型要求的数据转成符合要求的
d = { 't1': str(datetime.datetime.today()), 't2': str(datetime.date.today()) } res = json.dumps(d) print(res)
-
利用派生方法
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)
面向对象三大特性——封装
-
什么是封装
封装就是将数据或者功能隐藏起来
-
封装的目的
目的是给这些隐藏的数据开设特定的接口,让用户只有使用接口才可以去调用
在接口中也可增添一些额外的操作
-
如何封装
-
在类定义阶段使用双下划线开头的名字 都是隐藏的属性
后续类和对象都无法直接获取
-
在python中不会真正的限制任何代码
隐藏的属性如果真的需要访问 也可以 只不过需要做变形处理
__ 变量名 _ 类名__变量名
-
-
实例:
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 伪装属性
property 可以理解为将方法伪装成数据
如 : obj.name # 数据只需要点名字
obj.func() # 方法至少还要加括号
伪装之后可以将func方法伪装成数据 obj.func
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 = Cat() # d1 = Dog() # p1 = Pig() # c1.miao() # d1.wang() # p1.heng()
不论对象是什么马,只要调取相应的功能,就能直接调取固定的功能
-
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: # Teacher类有两个与文件类型同名的方法 def run(self):pass def eat(self):pass class Student: # Student 类也有两个与文件类型同名的方法 def run(self):pass def eat(self):pass
-
补充知识
在操作系统
操作系统 体现 含义 Linux系统 一切皆文件 只要你能读数据 能写数据 那么你就是文件 python 一切皆对象 只要你有数据 有功能 那么你就是对象 linxu: 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 class Memory: #Memory类也有两个与文件类型同名的方法:read和write def read(self): pass def write(self): pass 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' 两者虽然只差了引号 但是本质是完全不一样的 """ # 方式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()