其他分享
首页 > 其他分享> > 派生方法的实战演练 面向对象三大特性之一:封装 property伪装属性 面向对象三大特性之一:多态 面向对象之反射

派生方法的实战演练 面向对象三大特性之一:封装 property伪装属性 面向对象三大特性之一:多态 面向对象之反射

作者:互联网

目录

派生方法的实战演练

一、发现问题:

import datetime
import json

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

image

上述代码会报错,无法正常序列化

raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)

TypeError: Object of type 'datetime' is not JSON serializable

json序列化python数据是有限制的,不是所有的数据类型都可以

json模块默认:将要序列化的数据必须是以下表格中的类型

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          |
    +-------------------+---------------+

二、解决问题

解决方法 1:手动将数据类型转成符合要求的

import datetime
import json

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

# 输出:{"t1": "2022-07-29 19:25:34.672788", "t2": "2022-07-29"}

解决方法2:利用派生方法

if cls is None:
	cls = JSONEncoder
 return cls(
        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
        check_circular=check_circular, 					    allow_nan=allow_nan, indent=indent,
        separators=separators, default=default, 			    sort_keys=sort_keys,**kw).encode(obj)

查看JSONEncoder源码发现序列化报错是由default方法触发的

raise TypeError("Object of type '%s' is not JSON serializable" % o.__class__.__name__)

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

import datetime
import json

d = {
    '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')
        if 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)

# 输出 {"t1": "2022-07-29 19:42:02", "t2": "2022-07-29"}

面向对象三大特性之一:封装

一、意义

封装其实就是将数据或功能隐藏起来(包起来 装起来)

隐藏的目的不是让用户无法使用,而是给这些隐藏的数据开设特定的接口

让用户使用接口才可以去使用,我们在接口中可以添加一些额外操作

二、特点

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
        self.__name = name
        self.__age = age

stu1 = Student('ZHANG',22)
stu1.set_info('','bbb')
# 输出:用户名不能为空

property伪装属性

一、介绍

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

obj.name  # 数据只需要点名字
obj.func()  # 方法至少还要加括号
'''伪装之后可以将func方法伪装成数据'''
obj.func

二、扩展了解

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

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('JJJ', 200, 180)
res = p1.BMI
print(res)
p2 = Person('SSS',80,200)
res = p2.BMI
print(res)
# 0.006172839506172839
# 0.002


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

面向对象三大特性之一:多态

一、介绍

多态:一种事物的多种形态

eg:水:液态 气态 固态 动物:人 猫 狗 牛

二、代码演示

一种事物有多种形态,但相同的功能应该有相同的名字

这样的话,以后无论拿到那个具体的动物,都不需要管到底是谁,直接调用相同的功能即可

eg:无论是什么动物,猫也好,狗也好,只要想叫,就固定调用speak功能

class Animal(object):
    def speak(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()
'''在这种情况下,每种动物都有各自叫的方法,就是多态'''
'''像下面代码这样,就可以解除多态'''
class Animal(object):
    def speak(self):
        pass


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


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


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


c1 = Cat()
d1 = Dog()
p1 = Pig()
c1.speak()
d1.speak()
p1.speak()

其实上述多态的概念,我们之前就已经接触过

l1 = [11,22,33,44]
d1 = {'name':'zhang','age':19,'hobby':'learn'}
t1 = (1,2,3,4,5)
print(len(l1))
print(len(d1))
print(len(t1))

三、了解知识 —— 抽象类

1.指定metaclass属性将类设置为抽象类

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

image

2.鸭子类型

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

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():根据字符串给对象删除属性

三、代码演示

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

class Student:
    school = '清华大学'

    def choice_course(self):
        print('选课')


stu = Student()

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

try:
    if stu.school:
        print(f'True{stu.school}')
except Exception:
    print('没有属性')

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

'''
变量名school 与字符串school 看起来区别不大
    stu.school
    stu.'school'
两者虽然只差了引号,但是本质完全不同
'''
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('没在对象里找到这个名字')

2.四个方法试用

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

四、反射实战案例

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

    def get(self, file):
        print('Downloading %s...' % file)

    def put(self, file):
        print('Uploading %s...' % file)


obj = FtpServer()
obj.serve_forever()

标签:name,stu,self,特性,面向对象,三大,print,class,def
来源: https://www.cnblogs.com/Zhang614/p/16536899.html