派生方法,面向对象三大特征,封装,伪装,多态

  1. 派生方法的实战(非非菲菲菲菲常重要)
    1. 什么是派生
    2. 派生方法的使用
  2. 面向对象三大特征之封装
    1. 封装是什么
    2. 封装的实现
  3. property伪装属性
    1. 实战BMI
  4. 面向对象三大特征之多态
  5. 面向对象之反射

1.派生方法的实战(非非菲菲菲菲常重要)

在学派生实战之前我们再来回顾一下什么是派生

1.1什么是派生: 

   指的是子类继承父类的属性方法,并且派生出自己独有的属性与方法

   通过super来指向父类的属性(super()是一个特殊的类,调用super得到一个对象,该对象指向父类的名称空间)

ok,下面我们来看一串代码:

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

我的目的是想将这个字典用json的方式序列化,但是执行后发现不可行,会报错:

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

所以这个时候我们来查看一下报错的源码(json.JSONEncoder):

在报错的源码中有一个关于default的描述:

class JSONEncoder(object):
    """Extensible JSON <http://json.org> encoder for Python data structures.

    Supports the following objects and types by default:

    +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+    # 就是说即将要被序列化的数据要在这些数据类型中,其他的乱七八糟的就会报错

可以看出我们datetime模块得出的字符不属于左边这一列的数据类型,同样的json也无法接受,那咋整?这个时候我们就要用到我们的派生方法(你可看好楼)

2 派生方法的使用

  我们新建一个MyJsonEncode类,然后将json.JSONEncoder作为他的父类,然后继承他,然后对内部的default进行重新定义(错误就是出现在这里的嘛),用isinstance内置函数进行判断我们输入的数据类型即可!

          

派生方法序列化代码
 d = {
    't1': datetime.datetime.today(),
    't2': datetime.date.today()
}
# res = json.dumps(d)
"""
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方法做修改(派生)
"""


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)

 isinstance()是Python中的一个内建函数。是用来判断一个对象的变量类型

strftime()函数可以把YYYY-MM-DD HH:MM:SS格式的日期字符串转换成其它形式的字符串

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

1.封装是什么

 ---封装是面向对象三大特征之一,是面向对象最基本的概念,我们之前了解到对象是一个容器,装的就是一些数据和功能。封装本来就是一种思想,就是将数据和功能封装到一个对象中
 ---既然对象封装了数据和功能,那么他对这些数据和功能就有着自己的权力,即对象或者类可以把数据和功能给隐藏起来或者开放给使用者,限制对象(类)的使用者权利,或者控制/规范使用者的使用方式
2.如何操作
 

1.在类定义阶段使用双下划线开头命名的属性将会被隐藏 

     形式:__变量名,,,(函数,变量,数据都可以被隐藏,只要是在类体内的所有可以被定义的都可以被隐藏
    后续类和对象都无法直接获取
2.在python中不会真正的限制任何代码
    隐藏的属性如果真的需要访问 也可以 只不过需要做变形处理
                  _类名__变量名
ps:既然隐藏了 就不改使用变形之后的名字去访问 这样就失去了隐藏的意义

封装的代码
 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
"""

 3.property伪装属性   

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

      简单理解就是在类体中,我们调用数据的时候是可以直接点名字的obj.name,而调用方法的时候是要加括号的obj.func(),伪装就是调用方法的时候也可以不用括号obj.func。

    使用方法     @property

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

这样之后我们在调用BMI时候就不用加括号了

  扩展了解
    体质指数(BMI)=体重(kg)÷身高^2(m)

BMI指数
 # 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', 78, 1.83)
# res = p1.BMI()
# print(res)
# p2 = Person('悍匪', 72, 1.73)
# res = p2.BMI()
# print(res)
"""BMI虽然需要计算获得 但是更像是人的数据"""


# p1 = Person('jason', 78, 1.83)
# print(p1.BMI)
# print(p1.name)


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

 面向对象三大特征之多态

 

posted @ 2022-07-28 18:47  没错,干就完了!  阅读(134)  评论(0)    收藏  举报