面向对象3

面向对象3

  • 派生方法的实战案例
  • 面向对象三大特征之封装
  • 面向对象三大特征之多态
  • 面向对象之反射
  • 反射的实战案例

派生方法的实战案例

import datetime
import json
d = {
    't1':datetime.datetime.today(),
    't2':datetime.date.today()
}

res = json.dumps(d)
print(res)  # 报异常  TypeError: Object of type datetime is not JSON serializable
# 因为json的序列化不支持datetime格式,并且在json模块的JSONEncoder中的default方法抛出异常
# 所以我们可以尝试改写JSONEncoder的default方法,在default方法中将datetime格式转换成格式化时间(字符串格式)并返回,这样就不会报错
"""
查看dumps源码 注意cls参数 默认传JsonEncoder
查看该类的源码 发现default方法是报错的发起者
编写类继承JsonEncoder并重写default方法 之后调用dumps手动传cls=我们自己写的类
"""
class MyJSONEncoder(json.JSONEncoder):
    def default(self, o):
      """
       :param o: 接收无法被序列化的数据
        :return: 返回可以被序列化的数据
      """
        if isinstance(o,datetime.datetime):   # 判断是否是datetime类型 如果是则处理成可以被序列化的类型
            return o.strftime('%Y-%m-%d %X')
        if isinstance(o,datetime.date):
            return o.strftime('%Y-%m-%d')
        return super().default(o)  # 最后还是调用原来的方法 防止有一些额外操作没有做

res = json.dumps(d,cls=MyJSONEncoder)  # {"t1": "2022-11-07 13:16:55", "t2": "2022-11-07"}
print(res)import datetime
import json

d = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}
res = json.dumps(d)
print(res)

面向对象三大特征之封装

封装的定义与使用:

1.封装:就是将数据和功能'封装'起来

2.隐藏:将数据的功能隐藏起来不让用户直接调用 而是开发一些接口简接调用从而可以在接口内添加额外的操作

1.在类定义阶段使用双下划线开头的名字 都是隐藏的属性后续类和对象都无法直接获取
2.在python中不会真正的限制使用任何代码。隐藏的属性如果真的需要访问,也可以,只不过需要做处理变形处理
__变量名 ==>  _类名__变量名
但是既然隐藏,使用变形之后的名字去访问,这样失去了隐藏的意义
import datetime
import json
d = {
    't1':datetime.datetime.today(),
    't2':datetime.date.today()
}

res = json.dumps(d)
print(res)  # 报异常  TypeError: Object of type datetime is not JSON serializable
# 因为json的序列化不支持datetime格式,并且在json模块的JSONEncoder中的default方法抛出异常
# 所以我们可以尝试改写JSONEncoder的default方法,在default方法中将datetime格式转换成格式化时间(字符串格式)并返回,这样就不会报错

class MyJSONEncoder(json.JSONEncoder):
    def default(self, o):
        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(o)

res = json.dumps(d,cls=MyJSONEncoder)  # {"t1": "2022-07-28 13:16:55", "t2": "2022-07-28"}
print(res)
在类定义阶段使用双下划线开头的名字,都是隐藏的属性后续类和对象都无法直接获取
在python中不会真正的限制使用任何代码。隐藏的属性如果真的需要访问,也可以,只不过需要做变形处理
__变量名 ==> _类名__变量名
但是既然隐藏了,使用变形之后的名字去访问,这样就失去了隐藏的意义

class Person:
    __name = '清华大学'

    def __init__(self,name,age):
        self.__name =name
        self.__age = age
    # 专门开设一个访问学生数据的通道(接口)
    def check_info(self):
        print(f"""
        学生姓名:{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

p = Person('jason',23)
p.check_info()
p.set_info('',23)  # 用户名不能为空
"""
我们编写python很多时候都是大家墨守成规的东西,不需要真正的限制
class A:
    _school = '清华大学'
    def _choice_course(self):
        pass
"""

3.伪装属性property

property可以简单难度理解为:将类里面的方法伪装成类里面的数据

obj.name  # 数据只需要点名字
obj.func()  # 方法还需要加括号调用

但使用property伪装装饰器之后我们就可以将func方法伪装成数据:obj.func

"""
扩展了解:
	体质指数(BMI) = 体重(kg) ➗ 身高^2(m)
"""
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('jason',70,1.8)
res = p1.BMI  # 通过property语法糖修饰,使得BMI方法像数据一样不需要加括号调用
print(res)  # 21.604938271604937

通过使用property装饰器语法糖, 可是使我们的封装更加以假乱真

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!')
        self.__NAME = value
    @name.deleter
    def name(self):
        raise  PermissionError('Cannot delete')


f = Foo('jason')
print(f.name)  # jason
f.name = 'jjj'
print(f.name)  # jjj
# f.name = 123
# print(f.name)  # TypeError: 123 must be str!
del f.name  # PermissionError: Cannot delete

面向对象之多态

多态指一种事物的多种状态 在编写代码时,不同的类可能有很多相同的功能,相同的功能也应该有相同的名字,我们可以通过在父类引入抽象类的概念来硬性限制子类必须要有某些方法名

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.spark()  # 喵喵喵
d1.spark()  # 汪汪汪
p1.spark()  # 哼哼哼
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:
    def run(self):pass
    def eat(self):pass
class Student:
    def run(self):pass
    def eat(self):pass

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:一切皆对象

只要有数据、功能,那么就是对象。(文件名>>>文件对象 模块名>>>模块对象)

面向对象之反射

反射就是通过字符串来操作对象的数据方法

1.反射的主要方法

hasattr(): 判断对象是否含有某个字符串对应的属性
getattr(): 获取对象字符串对应的属性
setattr():根据字符串给对象设置属性
delattr(): 根据字符串给对象删除属性

实例1 需求:判断用户提供的名字在不在对象可以使用的范围内

class Student:
  shool_name = '清华大学'
  def choice_course(self):
      print('选课中....')
 stu = Student()

方式1:利用异常处理

class Student:
  school_name = '清华大学'
  def choice_course(self ):
      print('选课中.....')
 stu =Student()

try:
  if stu.school_name:
    print(f'Ture{stu.school_name}')
except Exception:
  print('没有该属性')

方式2:获取用户输入的名字,然后判断该名字对象有没有

class Student:
  school_name = '清华大学'
  def choice_course(self):
    print('选课中.....')
stu = Student()

while True:
  cmd_name = input('请输入你想查询的名字').strip()
  if hasattr(stu, cmd_name):
    res = getattr(stu,cmd_name)
    if callable(res):
      print('这是一个函数')
    else:
      print('这个是个数据')
  else:
    print('没有你想要查找的名字')

setattr()、 delattr()用法

class Student:
    school = '清华大学'
    def choice_course(self):
        print('选课')
        
stu = Student()
stu.name = 'nana'
print(stu.name)  # nana
stu.hobby = 'read'
setattr(stu,'name','xiao')
print(stu.name)  # xiao
print(stu.__dict__)  # {'name': 'xiao', 'hobby': 'read'}
delattr(stu,'name')
print(stu.__dict__)  # {'hobby': 'read'}

实例3

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()

反射的实战案例

案例1

加载配置文件纯大写的配置(配置文件加载:获取配置文件中所有大写的配置 小写的直接忽略 组织成字典)

import settings
new_dic = {}
for i in dir(settings):
    if i.isupper():
       new_dic[i] = getattr(settings,i)
print(new_dic)

总结:
1.dir(obj) :获取对象中可以调用的名字,以列表的形式返回
2.熟悉:在python中,一切皆对象(文件,模块...)

案例2

模拟操作系统cmd终端执行用户命令

class Mywindows(object):
    def dir(self):
        print('dir获取当前目录下所有的文件名称')
    def ls(self):
        print('ls获取当前路径下所有的文件名称')
    def ipconfig(self):
        print('ipconfig获取当前计算机的网卡信息')
obj = Mywindows()
while True:
    cmd = input("请输入你的指令>>>:").strip()
    if hasattr(obj,cmd):
        name_cmd = getattr(obj,cmd)
        name_cmd()
    else:
        print(f"{cmd}没有这个指令哦")
posted @ 2022-11-07 20:16  hugmi男孩  阅读(24)  评论(0)    收藏  举报