【python基础】第35回 派生 封装 多态 反射


目录

1. 派生

1.1 定义

子类中定义类与父类一模一样的方法并且扩展了该功能:派生
即子类继承了父类,并且在子类中定义了与父类一样的方法
子类调用父类的方法super().父类的方法()
操作:拦截,添加,原路返回

1.2 派生方法的实战演练

1.2.1 推导 时间模块与json模块

import datetime
import json

a = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}

res = json.dumps(a)  # 序列化
print(res)

【python基础】第35回 派生 封装 多态 反射
由图可知代码会报错 无法正常序列化

raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type datetime is not JSON serializable

查看JSONEncoder源码发现序列化报错是有default方法触发的。
json序列化python 数据类型是有限制的 不是所有的类型都可以,要被序列化的数据,里里外外都必须是下述类型才可以

PYthon JSON
dict object
list,tuple array
str string
int,float number
True true
False false
None null

1.2.2 解决方式1 转换数据类型

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

a = {
    't1': str(datetime.datetime.today()),
    't2': str(datetime.date.today())
}
res = json.dumps(a)
print(res)  # {"t1": "2022-07-28 15:16:41.417239", "t2": "2022-07-28"}

1.2.3 解决方式2 利用派生方法

如果想要避免报错 那么肯定需要对default方法做修改(派生)

a = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}

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(a, cls=MyJsonEncode)
print(res) # {"t1": "2022-07-28 16:05:23", "t2": "2022-07-28"}

2. 面向对象三大特征之封装

2.1 定义

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

2.2 特征

  1. 在定义阶段使用双下划线开头的名字,都是隐藏的属性,后续类和对象都是无法直接获取的
  2. 在python中不会真正的限制任何代码,隐藏的属性如果真的需要访问,也可以只不过需要做变形处理
  3. 格式 __变量名 = 值 _类名__变量名 = 值
  4. 隐藏就不改变使用变形之后的名字去访问,这样就是去了隐藏的意义

2.3 代码实例

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('','我很大')

3. property伪装属性

3.1 理解

可以简单的理解为,将方法伪装成数据

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

伪装之后可以将func方法伪装成数据 obj.func

3.2 扩展 体质指数(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)
# BMI虽然需要计算获得 但是更像是人的数据
# TypeError: 'float' object is not callable
# p1 = Person('jason', 70, 1.80)
# res = p1.BMI()
# print(res)
p2 = Person('aaaa', 80, 1.70)
print(p2.BMI)  # 68166089965398

3.3 代码实例

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

4. 面向对象三大特征之多态

4.1 定义

多态: 一种事物的多种形态
例:
水:液态,气态,固态
动物:人,狗,猫,猪

4.2 推导

4.2.1 推导1

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

class Cat(Animal):
    def miao(self):
        print('喵喵喵')

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


class Pig(Animal):
    def heng(self):
        print('哼哼哼')


c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.miao()
d1.wang()
p1.heng()

【python基础】第35回 派生 封装 多态 反射

4.2.2 推导2

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

【python基础】第35回 派生 封装 多态 反射

4.2.3 结论

一种事物有多种形态,但是相同的功能应该有相同的名字这样的话 以后我无论拿到哪个具体的动物 都不需要管到底是谁 直接调用相同的功能即可,无论你是鸡 鸭 猫 狗 猪 只要你想叫 你就调用固定的叫的功能

4.2.4 多态例子

不管在列表,字典,元组中统计个数都是用len

l1 = [11, 22, 33, 44]
d1 = {'name': 'jason', 'pwd': 123, 'hobby': 'raed'}
t1 = (11, 22, 33, 44)
print(len(l1))  # 4
print(len(d1))  # 3
print(len(t1))  # 4

4.3 强制操作,自觉遵守

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

4.4 鸭子类型

只要你长得像鸭子 走路像鸭子 说话像鸭子 那么你就是鸭子

# 都是人会跑会吃
class Teacher:
    def run(self):pass
    def eat(self):pass
class Student:
    def run(self):pass
    def eat(self):pass

4.5 操作系统

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

5. 面向对象之反射

5.1 定义

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

5.2 主要方法

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

5.3 代码实例

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

5.3.1 方式1:利用异常处理(过于繁琐)

变量名school 与字符串school 两者虽然只差了引号,但是本质是完全不一样的

stu.school
stu.'school'
class Student:
    school = '清华大学'
    def choice_course(self):
        print('选课')
stu = Student
try:
    if stu.school:
        print(f"True{stu.school}")
except Exception:
    print('没有属性')

【python基础】第35回 派生 封装 多态 反射

5.3.1 获取用户输入的名字,然后判断该名字对象有没有.

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

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

【python基础】第35回 派生 封装 多态 反射

5.4 反射实战案列

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

原创文章,作者:sunnyman218,如若转载,请注明出处:https://blog.ytso.com/tech/python/277594.html

(0)
上一篇 2022年7月28日 20:09
下一篇 2022年7月28日 20:16

相关推荐

发表回复

登录后才能评论