Python——面向对象
派生方法实战
# coding:utf-8
import json
import datetime
# dict_demo = {
# 't1': datetime.date.today(),
# 't2': datetime.datetime.today(),
# 't3': 'jason'
# }
# res = json .dumps(dict_demo)
# 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、手动转换类型
# dict_demo = {
# 't1': str(datetime.date.today()),
# 't2': str(datetime.datetime.today()),
# 't3': 'jason'
# }
# res = json.dumps(dict_demo)
# print(res)
'''
可正常输出:
{"t1": "2022-11-07", "t2": "2022-11-07 14:58:20.630274", "t3": "jason"}
'''
# 2、派生方法
'''
查看dumps源码,注意cls参数,默认传JsonEncoder
if cls is None:
cls = JSONEncoder
查看该类的源码,发现default方法时报错的发起者
def default(self, o):
raise TypeError(f'Object of type {o.__class__.__name__} 'f'is not JSON serializable')
编写类继承JsonEncoder并重写default方法之后调用dumps手动传cls = 我们自己写的类
'''
dict_demo = {
't1': datetime.date.today(),
't2': datetime.datetime.today(),
't3': 'jason'
}
class MyJsonEncoder(json.JSONEncoder):
def default(self, o):
"""
:param o:接收无法被序列化的数据
:return:返回可以被序列化的数据
"""
if isinstance(o, datetime.datetime):
return o.strftime('%Y-%m-%d %X')
elif isinstance(o, datetime.date):
return o.strftime('%Y-%m-%d')
return super().default(o)
res = json.dumps(dict_demo, cls=MyJsonEncoder)
print(res)
'''
{"t1": "2022-11-07", "t2": "2022-11-07 15:17:57", "t3": "jason"}
'''
面向对象之封装
'''
封装: 将数据和功能'封装'起来
隐藏: 将数据和功能隐藏起来不让用户直接调用,而是通过一些接口,通过调用这些接口,间接的使用
'''
# coding:utf-8
# class C:
# def func(self):
# print('come on')
#
#
# obj = C()
# obj.func() # come on
# # 将func方法伪装成类里面的数据
# obj.func # 目前运行没有任何效果
# class MyClass:
# school_name = '清华大学'
# _ = '文学系'
# _name = 'HaiMan'
# '''
# 在类的定义阶段,名字前面有两个下划线,那么该名字会被隐藏,无法被访问
# '''
# __age = '18'
# '''
# 在Python中其实没有真正意义上的隐藏,仅仅是换了名字而已
# 用 类名._类名__名字可以访问
# '''
# def __choice_course(self):
# print('正在选课')
#
#
# print(MyClass.school_name) # 清华大学
# obj = MyClass()
# print(obj.school_name) # 清华大学
# print(MyClass._) # 文学系
# # print(MyClass.__age) # 报找不到对应的对象的属性错
# '''
# AttributeError: type object 'MyClass' has no attribute '__age'. Did you mean: '_name'?
# '''
# MyClass.__hobby = 'read'
# # 在类定义阶段之外,不会被隐藏
# print(obj.__hobby) # read
# print(MyClass._MyClass__age) # 18
# print(MyClass.__dict__) # 查看类里面所有的名字
class Person:
def __init__(self, name, age, hobby):
# 对象也可以拥有隐藏的属性
self.__name = name
self.__age = age
self.__hobby = hobby
def get_info(self):
# 在类体代码中,是可以直接使用,隐藏的名字的
print(f"""
name: {self.__name}
age: {self.__age}
hobby: {self.__hobby}
""")
# 隐藏的属性开放修改的接口,可以自定义很多功能
def set_name(self, new_name):
if len(new_name) == 0:
raise ValueError('Please input name')
if new_name.isdigit():
raise ValueError('Please dont\'t input digit' )
self.__name = new_name
obj = Person('HanMan', 18, 'read')
print(obj.get_info())
'''
name: HanMan
age: 18
hobby: read
'''
print(obj.set_name('jason'))
print(obj.get_info())
'''
name: jason
age: 18
hobby: read
'''
'''
在今后编写面向对象代码类的定义时,还有很多单下划线开头的名字
表达的意思通常是不要直接访问,而是看看下面可能定义的接口
'''
伪装
'''
伪装: 将类里面的方法伪装成类里面的数据
BMI指数:衡量一个人的体重与身高对健康影响的一个指标
体质指数(BMI)=体重(kg)÷身高^2(m)
EX:70kg÷(1.75×1.75)=22.86
'''
# coding:utf-8
# class Person:
# 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('HaiMan', 1.80, 80)
# p1.BMI() # BMI应该作为人的基本数据而不是方法
# 报错TypeError:
# print(p1.BMI) # 24.691358024691358
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(f'{value} must be str')
# 通过类型检查后,将值value存放到真实的位置
self.__NAME = value
@name.deleter
def name(self):
raise PermissionError('Can not delete')
f = Foo('HaiMan')
print(f.name) # HaiMan
f.name = 'HaiMan888'
print(f.name) # HaiMan888
del f.name
'''
f.name = 'HaiMan'
# 触发name.setter装饰器对应的函数name(f,’HaiMan')
f.name = HaiMan123
# 触发name.setter对应的的函数name(f,HaiMan),抛出异常TypeError
del f.name
# 触发name.deleter对应的函数name(f),抛出异常PermissionError
'''
多态
# coding:utf-8
"""
面向对象中多态意思是
一种事物可以有多种形态但是针对相同的功能应该定义相同的方法
这样无论我们拿到的是哪个具体的事物
都可以通过相同的方法调用功能
"""
"""
python永远提倡自由简介大方
不约束程序员行为
但是多态提供了约束的方法
"""
import abc
# 指定metaclass属性将类设置为抽象类,
# 抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
# 该装饰器限制子类必须定义有一个名为talk的方法
@abc.abstractmethod
def talk(self):
print('talk')
class Cat(Animal):
# 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
print('miao')
# 若子类中没有一个名为talk的方法则会抛出异常TypeError,
# 无法实例化
cat = Cat()
print(cat.talk()) # miao
反射
'''
利用字符串操作对象的数据和方法
1.hasattr() 重点
判断对象是否含有某个字符串对应的属性名或方法名
2.getattr() 重点
根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3.setattr()
根据字符串给对象设置或者修改数据
4.delattr()
根据字符串删除对象里面的名字
'''
# coding:utf-8
'''
利用字符串操作对象的数据和方法
1.hasattr() 重点
判断对象是否含有某个字符串对应的属性名或方法名
2.getattr() 重点
根据字符串获取对象对应的属性名(值)或方法名(函数体代码)
3.setattr()
根据字符串给对象设置或者修改数据
4.delattr()
根据字符串删除对象里面的名字
'''
# class C1:
# school = '剑桥大学'
#
# def choice_course(self):
# print('please choice course')
#
#
# obj = C1()
# obj.choice_course() # please choice course
'''
判断某个名字对象是否存在
推导思路
try:
obj.xxx
except AttributeError:
print('your dont have the name')
判断用户随意指定的名字是否存在
target_name = input('Please input your need judge name')
try:
obj.target_name
except AttributeError:
print('your dont have the name')
'''
class C1:
school_name = '清华大学'
def choice_course(self):
print('please choice your course')
obj = C1()
while True:
target_name = input('Please input your need judge name>>>:')
if hasattr(obj, target_name):
print('congratulate you! name is exists')
# 获取改名字对应的数据(func data)
data_or_func = getattr(obj, target_name)
if callable(data_or_func):
print('it is a func')
data_or_func()
else:
print('it is a data', data_or_func)
else:
print('the name is not exists')
反射实战案例
'''
1.什么时候应该考虑使用反射 只要需求中出现了关键字
对象....字符串....
2.实战案例
1.模拟cmd终端
'''
# coding:utf-8
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()
'''
2.一切皆对象
'''
# 利用反射保留某个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('该模块文件中没有该名字')