【7.28】面向对象的多种用法

学习内容概要

  • 派生方法的实战演练
  • 面向对象三大特性之封装
  • 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中的数据对象是有规定的 不是所有的类型都能序列化 
  • 能被 json 序列化的数据表

    Python JSON
    dict object
    list, tuple array
    str string
    int, float number
    True true
    False false
    None null
  • 处理方式一
    • 手动将不符合数据类型要求的数据转成符合要求的(str类型转换)
import datetime
import json

d = {
    't1': str(datetime.datetime.today()),
    't2': str(datetime.date.today())
}
res = json.dumps(d)
print(res)  # {"t1": "2022-07-28 16:41:34.215700", "t2": "2022-07-28"}
  • 处理方式二
    • 利用派生方法
import datetime
import json


d = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}
res = json.dumps(d)
print(res)
# 上述打印会打印报错 
# 通过查看json.dumps 和 json.JSONEncoder 的底层源码 我们发现
# dumps(obj,cls=None):  # 里面的第一个形参 obj 是来接收传入的数据  如果不给cls传值
#    if cls == None:  # 默认cls == None
#        cls = JSONEncoder
#    return cls(...)  # 返回cls就是返回 JSONEncoder  因为  JSONEncoder 是个类 我们直接研究它就可以了
# class JSONEncoder:
#    pass        
# 查看JSONEncoder源码发现序列化报错是有default方法触发的
# raise TypeError(f'Object of type {o.__class__.__name__} '
#                         f'is not JSON serializable')
# 我们如果想要避免报错 那么肯定需要对default方法做修改(派生)

class MyJsonEncode(json.JSONEncoder):
    def default(self, o):
        '''o就是json即将要序列化的数据'''
        if isinstance(o, datetime.datetime):  # 做判断o如果是datetime.datetime
            return o.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(o, datetime.date):  # 做判断o如果是datetime.date
            return o.strftime('%Y-%m-%d')
        # 如果是可以序列化的类型 那么不做任何处理 直接让它序列化即可
        return super().default(o)


res = json.dumps(d, cls=MyJsonEncode)
print(res)  # "t1": "2022-07-28 17:29:47", "t2": "2022-07-28"}  实现json格式的字符串

面向对象三大特征之封装

  • 1.本质:
    • 封装就像是用袋子把功能装起来 隐藏起来
    • 隐藏起来不让后 给这些隐藏的数据封定成接口 让用户调用接口来查看隐藏起来的数据
  • 2.在类定义阶段使用双下划线开头的名字 都是隐藏的属性
    • 后续类和对象都无法去直接调用获取
  • 3.在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

obj = Student('jason',18)
obj.check_info()  # 学生姓名:jason  学生年龄:18
obj.set_info('make',19)
obj.check_info()  # 学生姓名:mak   学生年龄:19

property伪装属性

  • 伪装属性可以理解为将方法伪装成数据
    • 对象直接就可以点出数据 obj.school
    • 对象点方法还需要加上括号才可以使用 obj.func()
    • 趣味了解:体质指数(BMI)=体重(kg)÷身高^2(m)
class Person:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    
    def BMI(self):
        return self.weight / (self.height ** 2)

p1 = Person('小五',75 , 1.78)
res = p1.BMI()  # 调用方法需要这样操作
print(res)  # 23.671253629592222

"""
加上伪装属性:@property
"""
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)

p1 = Person('小五',75 , 1.78)
print(p1.BMI)  # 23.671253629592222  不需要加括号 和调用数据一样  直接就打印出来

面向对象三大特征之多态

  • 多态本质:
    • 属于同一种事物中 不同的表现形态
    • 水:固态 气态 液态
    • 生物: 狗 猫 牛 羊
class Cat():
    def miao(self):
        print('喵喵喵')


class Dog():
    def wang(self):
        print('汪汪汪')


class Pig():
    def heng(self):
        print('哼哼哼') 
        
        
c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.miao()  # 喵喵喵
d1.wang()  # 汪汪汪
p1.heng()  # 哼哼哼 

"""
属于同一种类  这样调取比较繁琐 麻烦
需要记忆的方法名  比较多的
"""





class biology(object):
    def spark(self):
        pass

class Cat(biology):
    def spark(self):
        print('喵喵喵')


class Dog(biology):
    def spark(self):
        print('汪汪汪')


class Pig(biology):
    def spark(self):
        print('哼哼哼')
    
c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.spark()  # 喵喵喵
d1.spark()  # 汪汪汪
p1.spark()  # 哼哼哼


"""
一种事物有多种形态 但是相同的功能应该有相同的名字

这样调取方法时  就不需要特意的去记方法名了
"""
    
"""
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()


  • 鸭子类型
    • 只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子
    • linux系统:一切皆文件
    • python:一切皆对象
"""
操作系统
    linux系统:一切皆文件
        只要你能读数据 能写数据 那么你就是文件
            内存
            硬盘
        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()
posted @   W日常  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示