面向对象的三大特性之封装、多态以及面向对象的反射

派生方法

实战演练(重要)

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)

面向对象三大特性之封装

封装的定义与使用

封装 其实就是将数据或者功能隐藏起来(包起来,装起来),隐藏的目的不是让用户无法使用,而是给这些隐藏的数据开设特定的接口,让用户使用接口才可以去使用,我们也可以在接口添加一些额外的操作。

  1. 在类定义阶段使用双下划线开头的名字,都是隐藏的属性后续类和对象都无法直接获取
  2. 在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
"""

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.miao()
# d1.wang()
# p1.heng()
"""
一种事物有多种形态,但是相同的功能应该有相同的名字
这样的话,以后我无论拿到哪个具体的动物,都不需要管到底是谁,直接调用相同的功能即可

无论你是鸡、鸭、猫、狗、猪,只要你想叫,你就调用固定的叫的功能
"""
# c1.spark()
# d1.spark()
# p1.spark()

"""
其实上述的多态概念,我们很早之前就了解过
"""
l1 = [11,22,33,44]
d1 = {'name':'xunfei','pwd':'123456','hobby':'read'}
print(len(l1))
print(len(d1))

此外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:
    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:一切皆对象 --> 只要你有数据 有功能 那么你就是对象
            文件名         文件对象
            模块名         模块对象
"""       

面向对象之反射( 重要)

定义与使用

反射:通过字符串来操作对象的数据或者方法

方法 含义
hasattr() 判断对象是否含有某个字符串对应的属性
getattr() 获取对象字符串对应的属性
setattr() 根据字符串给对象设置属性
delattr() 根据字符串给对象删除属性
class Student:
    school = '清华大学'
    def choice_course(self):
        print('选课')

stu = Student()

"""
需求:判断用户提供的名字在不在对象的可以使用的范围内
"""
# 方式1
# try:
#     if stu.school:
#         print(f'True{stu.school}')  # True清华大学
# except Exception:
#     print('没有属性')

"""
变量名school 于字符串school 区别大不大
    stu.school
    stu.'school'

两者虽然只差了引号,但是本质是完全不一样的
"""
# 方式2:获取用户输入的名字,然后判断该对象是否存在
# while True:
#     name = input('请输入您要核查的数据或功能>>>:').strip()
#     if hasattr(stu,name):
#         res = getattr(stu,name)
#         if callable(res):
#             print('拿到的是一个函数',res())
#         else:
#             print('拿到的是一个数据',res)
#     else:
#         print('不好意思您的输入不存在')

print(stu.__dict__)  # {}
stu.name = 'jason'
stu.age = 18
print(stu.__dict__)  # {'name': 'jason', 'age': 18}
setattr(stu,'gender','male')
setattr(stu,'hobby','read')
print(stu.__dict__)  # {'name': 'jason', 'age': 18, 'gender': 'male', 'hobby': 'read'}
del stu.name
delattr(stu,'age')
print(stu.__dict__)  # {'gender': 'male', 'hobby': 'read'}

"""
以后只要在需求中看到了关键字
    ...对象...字符串
    那么肯定需要使用反射
"""

反射实战演练

class FtpServer:

    def server_forever(self):
        while True:
            inp = input('input your cmd>>>:').strip()
            cmd,file = inp.split()
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(file)
    def get(self, file):
        print(f'Downloading {file}')

    def put(self, file):
        print(f'Uploading {file}')

obj = FtpServer()
obj.server_forever()
posted @ 2022-07-28 15:54  荀飞  阅读(29)  评论(0编辑  收藏  举报