封装伪装多态
派生方法实战
import json
import datetime
d = {
't1': datetime.date.today(),
't2': datetime.datetime.today(),
't3': 'jason'
}
# res = json.dumps(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.转换方式1:手动转类型(简单粗暴)
# d = {
# 't1': str(datetime.date.today()),
# 't2': str(datetime.datetime.today())
# }
# res = json.dumps(d)
# print(res)
"""
Author:clever-cat
time :2022/11/7-18:33
"""
import json
import datetime
class MyJson(json.JSONEncoder):
"""
:param o: 接收无法被序列化的数据
:return: 返回可以被序列化的数据
"""
def default(self, o):
# 判断是不是datetime数据类型 如果是处理成可以被序列化的类型
if isinstance(o, datetime.datetime):
return o.strftime("%Y-%m-%d %X")
if isinstance(o, datetime.date):
return o.strftime("%Y-%m-%d")
# 最后还是调用原来的方法 防止有一些额外的操作没有做
return super().default(self, o)
data_dict = {
'name': '张',
'age': 18,
'datetime': datetime.datetime.today(),
'date': datetime.date.today()
}
res = json.dumps(data_dict, cls=MyJson, ensure_ascii=False)
print(res)
面向对象三大特性之封装
-
封装:就是将数据和功能 封装 起来
-
隐藏:将数据和功能隐藏起来不让用户直接调用而是开发一些接口间接调用从而可以在接口内添加额外的操作
class School(object): _name = '张' # __开头即为隐藏 __age = 18 def __show(self): print(self.__age) # obj = School() # print(obj._name) # 张 # print(obj.__age) # AttributeError: 'School' object has no attribute '__age # print(School.__dict__) # {'__module__': '__main__', '_name': '张', '_School__age': 18} # obj.__gender = 'male' # 可以访问到 # print(obj.__gender) # male """ 在python中没有真正的隐藏,只是把他的名字该了 1.隐藏属性和方法,只能在类定义时创建,对象调用添加无法成为隐藏属性或方法 2.在哪个类定义的隐藏属性或方法,只能被哪个类调用,即使是对象调用也是一样例如 class T1: __name = '张三' class S1(T1): # __name = '李四' # 如果那这个注销 def func1(self): # 类体代码可以是直接使用类中的隐藏属性和方法 print(self.__name) # 李四 # 如果把__name注销则会报错 AttributeError: 'S1' object has no attribute '_S1__name' # self.__name # 在调用隐藏函数时,会自动把 调用隐藏函数 所在的类 _类名加隐藏函数或方法名加上如 _S1__name obj = S1() obj.func1() """ ''' 3.对象也可有隐藏方法 class A1(object): def __init__(self, name, age, gender): self.__name = name self.__age = age self.__gender = gender def get_name(self): print(f""" 姓名:{self.__name} 年龄:{self.__age} 爱好:{self.__gender} """) # 隐藏的属性开放修改的接口 可以自定义很多功能 def set_name(self,name): if len(name) == 0: return ValueError('不能什么都不输入') self.__name = name obj = A1('张三', 16, 'male') obj.get_name() """ 姓名:张三 年龄:16 爱好:male """ obj.set_name('李四') obj.get_name() # 正常修改 """ 姓名:李四 年龄:16 爱好:male class A1(object): def __init__(self, name, age, gender): self.__name = name self.__age = age self.__gender = gender def get_name(self): print(f""" 姓名:{self.__name} 年龄:{self.__age} 爱好:{self.__gender} """) # 隐藏的属性开放修改的接口 可以自定义很多功能 def set_name(self,name): if len(name) == 0: return ValueError('不能什么都不输入') self.__name = name obj = A1('张三', 16, 'male') obj.get_name() """ 姓名:张三 年龄:16 爱好:male """ obj.set_name('李四') obj.get_name() # 正常修改 """ 姓名:李四 年龄:16 爱好:male ''' """ 4.以后在编写面向对象代码类的定义时 也会看到很多单_开头的名字 表达的意思通常特指不要直接访问 而是查找一下下面可能定义的接口 """
伪装
- 伪装:将类里面的方法伪装成类里面的数据
class C:
sc_name = 'aaaa'
# 伪装关键字 property
@property
def get_name(self):
print(self.sc_name)
obj = C()
obj.get_name
BMI指数:衡量一个人的体重与身高对健康影响的一个指标
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
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('jason', 1.83, 78)
# p1.BMI() # BMI应该作为人的基本数据而不是方法
# print(p1.BMI) # 利用装饰器伪装成数据
-
使伪装可修改
class School(object): def __init__(self, name, age, hobby): self.__name = name self__age = age self.__hobby = hobby @property def name(self): return self.__name @name.setter def name(self, name): if isinstance(name, str): # 在修改值前进行数据类型查验 self.__name = name # 把name放在真实位置self.__name return raise ValueError('修改值必须使字符串') @name.deleter def name(self): if self.__name == '张三': raise PermissionError('不能删除名字位张三的人') del self.__name obj = School('张三1', 18, 'read') print(obj.name) # 张三 obj.name = '张三' # 触发name.setter装饰器对应的函数name # print(obj.name) # 李四 # del obj.name # print(obj.name) # 正常删除 AttributeError: 'School' object has no attribute '_School__name' print(obj.name) # 张三 del obj.name # 触发name.deleter对应的函数name(f),抛出异常PermissionError print(obj.name) # ValueError: 不能删除名字位张三的人
多态
- 多态:一种事物的多种形态
- 水:液态 固态 气态
- 动物:人 猪 猫 狗
- 面向对象中多态的意思是 一种事物可以有多种形态但是针对相同的功能应该定义相同的方法
这样无论我们拿到的是哪个具体的事物 都可以通过相同的方法调用功能
# class Animal:
# def spark(self):
# '''叫的方法'''
# pass
#
#
# class Cat(Animal):
# # def miao(self):
# # print('喵喵喵')
# def spark(self):
# print('喵喵喵')
#
#
# class Dog(Animal):
# # def wang(self):
# # print('汪汪汪')
# def spark(self):
# print('汪汪汪')
#
#
# class Pig(Animal):
# # def heng(self):
# # print('哼哼哼')
# 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__())
-
鸭子类型
- 只要看上去想鸭子
- 走路像鸭子
- 说话像鸭子
- 那么就是鸭子
文件 能够读取数据也能够保存数据 内存 能够读取数据也能够保存数据 硬盘 能够读取数据也能够保存数据 对于linux系统来说,一切皆文件
-
python永远提倡自由简洁大方 不约束程序员行为 但是多态提供了约束的方法
""" Author:clever-cat time :2022/11/7-21:11 """ import abc # 指定metclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化 class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod # 改装饰器限制子类必须定义有个名字位talk方法 def talk(self): # 抽象方法中无需实现具体的功能 pass class Cat(Animal): def talk(self): # 但凡继承Animal的子类都必须遵循Animal规定的标准 pass class Dog(Animal): def aa(self): # 不继承在实例化的时候者报错 pass obj1 = Dog() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化 TypeError: Can't instantiate abstract class Dog with abstract methods talk obj1.aa() obj2 = Cat() obj2.talk()
反射
-
利用字符串操作对象的数据和方法
-
hasattr() 重点
判断对象是否含有某个字符串对应的属性名或方法 -
getattr() 重点
根据字符串获取对象中对应的属性名或方法名 -
setattr()
根据字符串给对象设置或修改数据 -
delatty
根据字符串删除对象里面的名字""" Author:clever-cat time :2022/11/7-21:23 """ class C1: school_name = '小姐姐学院' def choice_course(self): print('大宝贝们正在选课') obj = C1() '''判断某个名字对象是否可以使用(存在)''' try: obj.xxx except Exception: print('没有') # 没有 '''判断用户随意指定的名字对象是否可以使用(存在)''' # name = input('请输入名字').strip() # # try: # obj.name # except Exception: # print('没有') # 没有 但是对象是有的 """ 字符串的名字跟变量名区别很大 'school_name' school_name 非常大 完全不一样 """ # 反射:利用字符串操作对象的数据和方法 while True: name = input('请输入名字') # print(hasattr(obj, name)) # school_name True xxx False if hasattr(obj, name): if callable(getattr(obj,name)): getattr(obj,name)() # 大宝贝们正在选课 print(getattr(obj,name)) # <bound method C1.choice_course of <__main__.C1 object at 0x00000248C0B65A30>> continue print(getattr(obj, name)) # 小姐姐学院 else: print('没有这个名字')
反射实战案例
- 什么时候应该考虑使用反射 只要需求中出现关键字
- 对象。。。字符串。。。
1.模拟cmd终端
class WinCmd:
def tasklist(self):
print("""
1.学习编程
2.学习python
3.学习英语
""")
def ipconfig(self):
print("""
地址:127.0.0.1
地址:上海浦东新区
""")
def get(self, target_file):
print('获取指定文件',target_file)
def put(self, target_file):
print('上传指定文件',target_file)
def server_run(self):
print('欢迎进入简易版本cmd终端')
while True:
target_cmd = input('请输入您的指令>>>:')
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.server_run()
-
一切皆对象
# 利用反射保留某个py文件中所有的大写变量名及对应的数据值 import settings print(dir(settings)) # dir列举对象可以使用的名字 useful_dict = {} for name in dir(settings): if name.isupper(): useful_dict[name] = getattr(settings, name) print(useful_dict) # while True: # target_name = input('请输入某个名字') # if hasattr(settings, target_name): # print(getattr(settings, target_name)) # else: # print('该模块文件中没有该名字')
作业
1.尝试利用反射编写一个简易版本的用户赠删改查功能
class User(object):
def __init__(self, name, age, hobby):
self.name = name
self.age = age
self.hobby = hobby
class Student(User):
pass
obj = Student('张三', 18, '读书')
while True:
print("""
1.查
2.增
3.改
4.删
""")
num = input('请输入需要使用的功能')
name = input('请输入要用的属性')
if num == '2':
vlues = input('请输入值')
setattr(obj, name, vlues)
print(obj.__dict__)
continue
if not hasattr(obj, name):
print('没有这个名字哦')
continue
if num == '1':
print(getattr(obj, name))
continue
if num == '3':
vlues = input('请输入值')
setattr(obj, name, vlues)
print(obj.__dict__)
continue
if num == '4':
delattr(obj, name)
print(obj.__dict__)
continue
print('编号输入不正确')