面向对象-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方法做修改(派生)

解决此类问题,有两种解决方法

  1. 手动将不符合数据类型要求的数据转成符合要求的

    d = {
        't1': str(datetime.datetime.today()),
        't2': str(datetime.date.today())
    }
    res = json.dumps(d)
    print(res)
    
  2. 利用派生方法

    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)
    
    

面向对象三大特性——封装

  • 什么是封装

    ​ 封装就是将数据或者功能隐藏起来

  • 封装的目的

    ​ 目的是给这些隐藏的数据开设特定的接口,让用户只有使用接口才可以去调用

    ​ 在接口中也可增添一些额外的操作

  • 如何封装

    1. 在类定义阶段使用双下划线开头的名字 都是隐藏的属性

      ​ 后续类和对象都无法直接获取

    2. 在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()  
    
posted @ 2022-07-28 16:52  Nirvana*  阅读(31)  评论(0)    收藏  举报