今日内容回顾

派生方法的实战演练

# 1.需求将下列字典序列化。
# time_dict = {'time1': datetime.datetime.today(),
#              'time2': datetime.date.today()}
# print(time_dict)

# {'time1': datetime.datetime(2022, 7, 28, 14, 33, 20, 481094),
# 'time2': datetime.date(2022, 7, 28)}
# res = json.dumps(time_dict)  # 报错

"""

如果直接将其序列化会报错,报错如下:
    raise TypeError(f'Object of type {o.__class__.__name__} '
    TypeError: Object of type datetime is not JSON serializable



首先下列是可以被序列化的类型,但我们的字典中的value值不在能够被序列化的类型中
是不能够直接被序列化的,能够被序列化的必须里里外外都符合下列条件才能够被序列化。
  +-------------------+---------------+
    | Python            | JSON          |
    +===================+===============+
    | dict              | object        |
    +-------------------+---------------+
    | list, tuple       | array         |
    +-------------------+---------------+
    | str               | string        |
    +-------------------+---------------+
    | int, float        | number        |
    +-------------------+---------------+
    | True              | true          |
    +-------------------+---------------+
    | False             | false         |
    +-------------------+---------------+
    | None              | null          |
    +-------------------+---------------+

"""
# 解决方式1:手动将不符合数据类型要求的数据转成符合要求的
time_dict = {
    't1': str(datetime.datetime.today()),
    't2': str(datetime.date.today())
}
res = json.dumps(time_dict)
print(res)
# 解决方式2:利用派生方法
time_dict = {
    't1': (datetime.datetime.today()),
    't2': (datetime.date.today())
}
"""
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方法做修改(派生)
"""
# 定义一个类,继承父类json.JSONEncoder
class Myjsondecoder(json.JSONEncoder):
    # 创建一个和父类同样的功能
    def default(self, o):
        # 做上面做出扩展,判断被序列化的对象是否为datetime类型
        if isinstance(o, datetime.datetime):
            # 如果是将对象转为字符串格式的时间
            return o.strftime('%Y-%m-%d %H %M %S')
        # 判断被序列化的对象是否为datetime类型
        if isinstance(o, datetime.date):
            # 如果是将对象转为字符串格式的时间
            return o.strftime('%Y-%m-%d')
        # 对象如果是可以序列化的类型,则不做任何操作,让其直接序列化即可。
        return super().default(o)

#
res = json.dumps(time_dict, cls=Myjsondecoder)

print(res)  # {"time1": "2022-07-28 14 56 34", "time2": "2022-07-28"}

面向对象的三大特性之封装

  • 什么是封装

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

  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
        if not isinstance(age,int):
            print('年龄必须是数字')
            return
        self.__name = name
        self.__age = age

stu1 = Student('jason', 18)
# 正常访问是访问不到的,会直接报错
# print(stu1.__school)

# 可以通过 _类名__变量名的方式访问
# print(stu1._Student__school)  # 清华大学
"""
但是上述方式是不对的,直接失去了隐藏的意义干脆不隐藏得了
"""

# 正常方式 通过特定的接口来访问
print(stu1.check_info())
"""
       学生姓名:jason
        学生年龄:18
"""
# 我们编写python很多时候都是大家墨守成规的东西 不需要真正的限制
class A:
	_school = '清华大学'
	def _choice_course(self):
		pass
"""
如果看到别人在类体代码中变量名是以 _ 开头的,我们就要知道,这是个隐藏属性的数据
那么我们在调用的时候就需要,特定开设的接口通过接口来访问它
"""

property伪装属性

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

伪装之前的方法需要加括号才能调用,而伪装之后不需要加括号就可以调用

将其伪装成,调用方法就像是在调用一个数据一样的感觉。

伪装方法,只针对无参的方法。基本是用 方法上面加上装饰器@property

示例1:

计算体质的方法
体质指数(BMI)=体重(kg)÷身高^2(m)

class Person:
    def __init__(self, name, weight, height):
        self.name = name
        self.weight = weight
        self.height = height

    # 伪装之前
    def BMI(self):
        return self.weight / (self.height ** 2)

    # 伪装之后
    @property
    def BMI(self):
        return self.weight / (self.height ** 2)

# 调用方法BMI需要加括号才能调用
p1 = Person('jason', 78, 1.83)
res = p1.BMI()
print(res)  # 23.291229956104985

# 直接以访问数据的方式可以直接调用方法
p2 = Person('tomy', 65, 1.75)
print(p2.BMI)  # 21.224489795918366

示例2:

使用property保证了属性访问的一致性,并且property还提供了设置和删除属性的方法

使用@name.setter设置属性和用@name.deleter删除属性

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

面向对象三大特性之多态

  • 什么是多态

    多态其实指的就是一种事物的多种形态。

    如:水有液态、气态、固态 。动物有猫科动物、犬科动物等...

# 示例1:

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('哼哼哼')
"""
一种事物,有多种形态,但是有相同的功能。
如上述代码中描述的动物虽然是不同形态的,但是它们有着相同的功能,那么也应该有相同的功能名字
这样的话 以后我无论拿到哪个具体的动物 都不需要管到底是谁 直接调用相同的功能即可

"""

# 示例2:
# 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(object):
    school = '清华大学'
    
    def choice_course(self)
    	print("选课")
stu = Student()
# 1.需求:判断用户提供的名字在不在对象可以使用的范围内

target_name = input('请输入您想要核查的名字>>>:').strip()
"""
接收用户输入得到的是字符串,而对象中存在的是名字
两者虽然只差了引号 但是本质是完全不一样的
可以,使用反射方法进行判断
"""

# 判断对象stu和产生对象的类中是否含有用户提供的target_name对应的属性
if hasattr(stu,target_name):  
   # 如果对象中含有对应的名字,则获取名字的数据或方法。
	res = getattr(stu,target_name)
   # 判断获取的名字是否可以加括号调用。 
   	if callable(res)
    	# 条件成立,则表明拿到的是方法,加括号调用即可
    	res()
   # 条件不成立,则表明拿到的是数据,直接打印即可
   	elseprint(res)

elseprint"你所提供的名字,对象中没有"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__)    
    
"""
以后只要在需求中看到了关键字
	....对象....字符串 
那么肯定需要使用反射
"""

反射实战案例

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()
posted @   瓮小辉  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示